Role selector WIP
flags page WIP
This commit is contained in:
parent
953366d394
commit
6476a205a4
@ -53,3 +53,15 @@ export type Role = {
|
|||||||
position: number,
|
position: number,
|
||||||
rateLimits: RateLimits
|
rateLimits: RateLimits
|
||||||
} & APIEntity
|
} & APIEntity
|
||||||
|
|
||||||
|
type FlagType = string | number | boolean | number[] | null
|
||||||
|
type FlagEnv = 'test' | 'prod'
|
||||||
|
type FlagConsumer = 'client' | 'server' | 'api'
|
||||||
|
|
||||||
|
export type Flag = {
|
||||||
|
id: string,
|
||||||
|
name: string,
|
||||||
|
env: FlagEnv,
|
||||||
|
consumer: FlagConsumer,
|
||||||
|
value: FlagType
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
export type DropdownBaseProps = {
|
export type DropdownBaseProps = {
|
||||||
children: React.ReactNode
|
children: React.ReactNode,
|
||||||
|
className?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DropdownItemProps = {
|
export type DropdownItemProps = {
|
||||||
|
@ -49,7 +49,8 @@ function App() {
|
|||||||
{
|
{
|
||||||
to: '/admin', label: 'Admin', items: [
|
to: '/admin', label: 'Admin', items: [
|
||||||
{ to: '/users', label: 'Users', relative: true },
|
{ to: '/users', label: 'Users', relative: true },
|
||||||
{ to: '/roles', label: 'Roles', relative: true }
|
{ to: '/roles', label: 'Roles', relative: true },
|
||||||
|
{ to: '/flags', label: 'Flags', relative: true }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React, { Children, useRef, useState } from "react";
|
import React, { Children, useRef, useState } from "react";
|
||||||
import ClickDetector from "../util/ClickDetector";
|
import ClickDetector from "../util/ClickDetector";
|
||||||
import { DropdownBaseProps, DropdownItemProps } from "../@types/Components";
|
import { DropdownBaseProps, DropdownItemProps } from "../@types/Components";
|
||||||
import '../css/components/Selectors.css';
|
import '../css/components/InputElements.css';
|
||||||
|
|
||||||
export const FileSelector = ({ cb }: { cb: (file: File) => void }) => {
|
export const FileSelector = ({ cb }: { cb: (file: File) => void }) => {
|
||||||
|
|
||||||
@ -62,8 +62,12 @@ export const VerticalToggleSwitch = ({ value, onChange, ref, children }:
|
|||||||
</p>;
|
</p>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const DropdownHeader = ({ children }: DropdownBaseProps) => {
|
export const NumberInput = () => {
|
||||||
return <summary className='card is-vertical-align dropdown-header'>
|
//
|
||||||
|
};
|
||||||
|
|
||||||
|
const DropdownHeader = ({ children, className = '' }: DropdownBaseProps) => {
|
||||||
|
return <summary className={`card is-vertical-align dropdown-header p-2 ${className}`}>
|
||||||
{children}
|
{children}
|
||||||
</summary>;
|
</summary>;
|
||||||
};
|
};
|
||||||
@ -83,7 +87,7 @@ const DropdownItem = ({ children, type, selected, onClick }: DropdownItemProps)
|
|||||||
};
|
};
|
||||||
|
|
||||||
const DropdownItemList = ({ children }: DropdownBaseProps) => {
|
const DropdownItemList = ({ children }: DropdownBaseProps) => {
|
||||||
return <div className='card'>
|
return <div className='card w-100 '>
|
||||||
{children}
|
{children}
|
||||||
</div>;
|
</div>;
|
||||||
};
|
};
|
||||||
@ -92,10 +96,11 @@ type DropdownProps = {
|
|||||||
name?: string,
|
name?: string,
|
||||||
multi?: boolean,
|
multi?: boolean,
|
||||||
selection?: [],
|
selection?: [],
|
||||||
children: React.ReactNode[]
|
children: React.ReactNode[],
|
||||||
|
className?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const DropdownComp = ({ children }: DropdownProps) => {
|
const DropdownComp = ({ children, className = '' }: DropdownProps) => {
|
||||||
|
|
||||||
if (!children)
|
if (!children)
|
||||||
throw new Error('Missing children');
|
throw new Error('Missing children');
|
||||||
@ -108,7 +113,7 @@ const DropdownComp = ({ children }: DropdownProps) => {
|
|||||||
return <ClickDetector callback={() => {
|
return <ClickDetector callback={() => {
|
||||||
if (detailsRef.current) detailsRef.current.open = false;
|
if (detailsRef.current) detailsRef.current.open = false;
|
||||||
}}>
|
}}>
|
||||||
<details ref={detailsRef} className='dropdown'>
|
<details ref={detailsRef} className={`dropdown w-100 ${className}`}>
|
||||||
{children}
|
{children}
|
||||||
</details>
|
</details>
|
||||||
</ClickDetector>;
|
</ClickDetector>;
|
1200
src/css/chota.min.css
vendored
1200
src/css/chota.min.css
vendored
File diff suppressed because one or more lines are too long
3
src/css/components/InputElements.css
Normal file
3
src/css/components/InputElements.css
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
.role-selector {
|
||||||
|
background-color: white;
|
||||||
|
}
|
@ -1,10 +1,11 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { Route, Routes } from "react-router";
|
import { Route, Routes } from "react-router";
|
||||||
import { FileSelector } from "../components/Selectors";
|
import { FileSelector } from "../components/InputElements";
|
||||||
import '../css/pages/Admin.css';
|
import '../css/pages/Admin.css';
|
||||||
import ErrorBoundary from "../util/ErrorBoundary";
|
import ErrorBoundary from "../util/ErrorBoundary";
|
||||||
import Roles from "./admin/Roles";
|
import Roles from "./admin/Roles";
|
||||||
import Users from "./admin/Users";
|
import Users from "./admin/Users";
|
||||||
|
import Flags from "./admin/Flags";
|
||||||
|
|
||||||
|
|
||||||
const Main = () => {
|
const Main = () => {
|
||||||
@ -42,6 +43,7 @@ const Admin = () => {
|
|||||||
<Route path="/" element={<Main />} />
|
<Route path="/" element={<Main />} />
|
||||||
<Route path="/users/*" element={<Users />} />
|
<Route path="/users/*" element={<Users />} />
|
||||||
<Route path='/roles/*' element={<Roles />} />
|
<Route path='/roles/*' element={<Roles />} />
|
||||||
|
<Route path='/flags/*' element={<Flags />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</ErrorBoundary>;
|
</ErrorBoundary>;
|
||||||
};
|
};
|
||||||
|
69
src/pages/admin/Flags.tsx
Normal file
69
src/pages/admin/Flags.tsx
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { Flag as APIFlag } from "../../@types/ApiStructures";
|
||||||
|
import { get } from "../../util/Util";
|
||||||
|
import { ToggleSwitch } from "../../components/InputElements";
|
||||||
|
|
||||||
|
const inputTypes = {
|
||||||
|
boolean: ToggleSwitch
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
const Flag = ({ flag }: { flag: APIFlag }) => {
|
||||||
|
|
||||||
|
return <div className='flag card'>
|
||||||
|
<p><b>Name:</b> {flag.name}</p>
|
||||||
|
<p><b>ID:</b> {flag.id}</p>
|
||||||
|
<p><b>Environment:</b> {flag.env}</p>
|
||||||
|
<p><b>Consumer:</b> {flag.consumer}</p>
|
||||||
|
<label><b>Value:</b> <input /></label>
|
||||||
|
</div>;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
const FlagList = ({ flags, error }: { flags: APIFlag[], error: string | null }) => {
|
||||||
|
|
||||||
|
console.log(flags);
|
||||||
|
return <div className='col-6-lg col-12'>
|
||||||
|
<h4>Flags</h4>
|
||||||
|
{error && <p>{error}</p>}
|
||||||
|
|
||||||
|
{flags.map(flag => <Flag key={flag.id} flag={flag} />)}
|
||||||
|
|
||||||
|
</div>;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
const CreateFlag = () => {
|
||||||
|
return <div className='col-6-lg col-12'>
|
||||||
|
<h4>Create Flag</h4>
|
||||||
|
|
||||||
|
<div className='card'>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Flags = () => {
|
||||||
|
|
||||||
|
const [flags, setFlags] = useState<APIFlag[]>([]);
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
const response = await get('/api/flags');
|
||||||
|
if (!response.success) return setError(response.message as string);
|
||||||
|
setFlags(response.data as APIFlag[]);
|
||||||
|
})();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return <div className='row'>
|
||||||
|
|
||||||
|
<FlagList flags={flags} error={error} />
|
||||||
|
|
||||||
|
<CreateFlag />
|
||||||
|
|
||||||
|
</div>;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Flags;
|
@ -7,7 +7,7 @@ import { Table, TableListEntry } from "../../components/Table";
|
|||||||
import ErrorBoundary from "../../util/ErrorBoundary";
|
import ErrorBoundary from "../../util/ErrorBoundary";
|
||||||
import { get, patch, post } from "../../util/Util";
|
import { get, patch, post } from "../../util/Util";
|
||||||
import { Permissions as Perms, Role as R } from "../../@types/ApiStructures";
|
import { Permissions as Perms, Role as R } from "../../@types/ApiStructures";
|
||||||
import { ToggleSwitch } from "../../components/Selectors";
|
import { ToggleSwitch } from "../../components/InputElements";
|
||||||
|
|
||||||
const Role = ({ role }: {role: R}) => {
|
const Role = ({ role }: {role: R}) => {
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ import { Table, TableListEntry } from "../../components/Table";
|
|||||||
import { BackButton, PageButtons } from "../../components/PageControls";
|
import { BackButton, PageButtons } from "../../components/PageControls";
|
||||||
import { Permissions } from "../../views/PermissionsView";
|
import { Permissions } from "../../views/PermissionsView";
|
||||||
import { Application as App, Permissions as Perms, User as APIUser, Role } from "../../@types/ApiStructures";
|
import { Application as App, Permissions as Perms, User as APIUser, Role } from "../../@types/ApiStructures";
|
||||||
import { Dropdown, ToggleSwitch } from "../../components/Selectors";
|
import { Dropdown, ToggleSwitch } from "../../components/InputElements";
|
||||||
|
|
||||||
type PartialUser = {
|
type PartialUser = {
|
||||||
apps: App[]
|
apps: App[]
|
||||||
@ -78,7 +78,7 @@ const Application = ({ app }: { app: App }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const RoleComp = ({ role, onClick }: { role: Role, onClick: React.ReactEventHandler }) => {
|
const RoleComp = ({ role, onClick }: { role: Role, onClick: React.ReactEventHandler }) => {
|
||||||
return <div className='role card' >
|
return <div className='role card p-3' >
|
||||||
{role.name} <span className="clickable" onClick={onClick}>X</span>
|
{role.name} <span className="clickable" onClick={onClick}>X</span>
|
||||||
</div>;
|
</div>;
|
||||||
};
|
};
|
||||||
@ -98,7 +98,7 @@ const Roles = ({ userRoles, roles }: { userRoles: Role[], roles: Role[] }) => {
|
|||||||
};
|
};
|
||||||
return <Dropdown>
|
return <Dropdown>
|
||||||
|
|
||||||
<Dropdown.Header>
|
<Dropdown.Header className='role-selector'>
|
||||||
{equippedRoles.map(role => <RoleComp key={role.id} role={role} onClick={() => {
|
{equippedRoles.map(role => <RoleComp key={role.id} role={role} onClick={() => {
|
||||||
roleDeselected(role);
|
roleDeselected(role);
|
||||||
}} />)}
|
}} />)}
|
||||||
@ -167,9 +167,9 @@ const User = ({ user, roles }: { user: APIUser, roles: Role[] }) => {
|
|||||||
<label>Roles</label>
|
<label>Roles</label>
|
||||||
<Roles userRoles={user.roles} roles={roles} />
|
<Roles userRoles={user.roles} roles={roles} />
|
||||||
|
|
||||||
<button className="button primary">
|
{/* <button className="button primary">
|
||||||
Save User
|
Save User
|
||||||
</button>
|
</button> */}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React, { useRef, useState } from "react";
|
import React, { useRef, useState } from "react";
|
||||||
import { capitalise, get, post } from "../../util/Util";
|
import { capitalise, get, post } from "../../util/Util";
|
||||||
import { useLoginContext } from "../../structures/UserContext";
|
import { useLoginContext } from "../../structures/UserContext";
|
||||||
import { FileSelector } from "../../components/Selectors";
|
import { FileSelector } from "../../components/InputElements";
|
||||||
import { ExternalProfile as EP, User } from "../../@types/ApiStructures";
|
import { ExternalProfile as EP, User } from "../../@types/ApiStructures";
|
||||||
|
|
||||||
const TwoFactorControls = ({ user }: {user: User}) => {
|
const TwoFactorControls = ({ user }: {user: User}) => {
|
||||||
|
Loading…
Reference in New Issue
Block a user