Role selector WIP

flags page WIP
This commit is contained in:
Erik 2023-05-09 21:59:57 +03:00
parent 953366d394
commit 6476a205a4
Signed by: Navy.gif
GPG Key ID: 2532FBBB61C65A68
12 changed files with 1310 additions and 19 deletions

View File

@ -52,4 +52,16 @@ export type Application = {
export type Role = {
position: number,
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
}

View File

@ -1,5 +1,6 @@
export type DropdownBaseProps = {
children: React.ReactNode
children: React.ReactNode,
className?: string
}
export type DropdownItemProps = {

View File

@ -49,7 +49,8 @@ function App() {
{
to: '/admin', label: 'Admin', items: [
{ to: '/users', label: 'Users', relative: true },
{ to: '/roles', label: 'Roles', relative: true }
{ to: '/roles', label: 'Roles', relative: true },
{ to: '/flags', label: 'Flags', relative: true }
]
}
];

View File

@ -1,7 +1,7 @@
import React, { Children, useRef, useState } from "react";
import ClickDetector from "../util/ClickDetector";
import { DropdownBaseProps, DropdownItemProps } from "../@types/Components";
import '../css/components/Selectors.css';
import '../css/components/InputElements.css';
export const FileSelector = ({ cb }: { cb: (file: File) => void }) => {
@ -62,8 +62,12 @@ export const VerticalToggleSwitch = ({ value, onChange, ref, children }:
</p>;
};
const DropdownHeader = ({ children }: DropdownBaseProps) => {
return <summary className='card is-vertical-align dropdown-header'>
export const NumberInput = () => {
//
};
const DropdownHeader = ({ children, className = '' }: DropdownBaseProps) => {
return <summary className={`card is-vertical-align dropdown-header p-2 ${className}`}>
{children}
</summary>;
};
@ -83,7 +87,7 @@ const DropdownItem = ({ children, type, selected, onClick }: DropdownItemProps)
};
const DropdownItemList = ({ children }: DropdownBaseProps) => {
return <div className='card'>
return <div className='card w-100 '>
{children}
</div>;
};
@ -92,10 +96,11 @@ type DropdownProps = {
name?: string,
multi?: boolean,
selection?: [],
children: React.ReactNode[]
children: React.ReactNode[],
className?: string
}
const DropdownComp = ({ children }: DropdownProps) => {
const DropdownComp = ({ children, className = '' }: DropdownProps) => {
if (!children)
throw new Error('Missing children');
@ -108,7 +113,7 @@ const DropdownComp = ({ children }: DropdownProps) => {
return <ClickDetector callback={() => {
if (detailsRef.current) detailsRef.current.open = false;
}}>
<details ref={detailsRef} className='dropdown'>
<details ref={detailsRef} className={`dropdown w-100 ${className}`}>
{children}
</details>
</ClickDetector>;

1200
src/css/chota.min.css vendored

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,3 @@
.role-selector {
background-color: white;
}

View File

@ -1,10 +1,11 @@
import React, { useState } from "react";
import { Route, Routes } from "react-router";
import { FileSelector } from "../components/Selectors";
import { FileSelector } from "../components/InputElements";
import '../css/pages/Admin.css';
import ErrorBoundary from "../util/ErrorBoundary";
import Roles from "./admin/Roles";
import Users from "./admin/Users";
import Flags from "./admin/Flags";
const Main = () => {
@ -42,6 +43,7 @@ const Admin = () => {
<Route path="/" element={<Main />} />
<Route path="/users/*" element={<Users />} />
<Route path='/roles/*' element={<Roles />} />
<Route path='/flags/*' element={<Flags />} />
</Routes>
</ErrorBoundary>;
};

69
src/pages/admin/Flags.tsx Normal file
View 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;

View File

@ -7,7 +7,7 @@ import { Table, TableListEntry } from "../../components/Table";
import ErrorBoundary from "../../util/ErrorBoundary";
import { get, patch, post } from "../../util/Util";
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}) => {

View File

@ -7,7 +7,7 @@ import { Table, TableListEntry } from "../../components/Table";
import { BackButton, PageButtons } from "../../components/PageControls";
import { Permissions } from "../../views/PermissionsView";
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 = {
apps: App[]
@ -78,7 +78,7 @@ const Application = ({ app }: { app: App }) => {
};
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>
</div>;
};
@ -98,7 +98,7 @@ const Roles = ({ userRoles, roles }: { userRoles: Role[], roles: Role[] }) => {
};
return <Dropdown>
<Dropdown.Header>
<Dropdown.Header className='role-selector'>
{equippedRoles.map(role => <RoleComp key={role.id} role={role} onClick={() => {
roleDeselected(role);
}} />)}
@ -167,9 +167,9 @@ const User = ({ user, roles }: { user: APIUser, roles: Role[] }) => {
<label>Roles</label>
<Roles userRoles={user.roles} roles={roles} />
<button className="button primary">
{/* <button className="button primary">
Save User
</button>
</button> */}
</div>

View File

@ -1,7 +1,7 @@
import React, { useRef, useState } from "react";
import { capitalise, get, post } from "../../util/Util";
import { useLoginContext } from "../../structures/UserContext";
import { FileSelector } from "../../components/Selectors";
import { FileSelector } from "../../components/InputElements";
import { ExternalProfile as EP, User } from "../../@types/ApiStructures";
const TwoFactorControls = ({ user }: {user: User}) => {