Create role fields and button and some other WIP
This commit is contained in:
parent
d342c71fd1
commit
d4858d809d
@ -24,12 +24,13 @@ type APIEntity = {
|
|||||||
name: string,
|
name: string,
|
||||||
disabled: boolean,
|
disabled: boolean,
|
||||||
permissions: Permissions,
|
permissions: Permissions,
|
||||||
createdTimestamp: number
|
createdTimestamp: number,
|
||||||
|
note: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserLike = {
|
export type UserLike = {
|
||||||
icon: string | null,
|
icon: string | null,
|
||||||
roles: string[],
|
roles: Role[],
|
||||||
temporary: boolean
|
temporary: boolean
|
||||||
} & APIEntity
|
} & APIEntity
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
|
|
||||||
const FileSelector = ({cb}: {cb: (file: File) => void}) => {
|
export const FileSelector = ({ cb }: { cb: (file: File) => void }) => {
|
||||||
|
|
||||||
if (!cb) throw new Error('Missing callback');
|
if (!cb) throw new Error('Missing callback');
|
||||||
const [file, setFile] = useState<File | null>(null);
|
const [file, setFile] = useState<File | null>(null);
|
||||||
|
|
||||||
@ -35,4 +35,32 @@ const FileSelector = ({cb}: {cb: (file: File) => void}) => {
|
|||||||
</label>;
|
</label>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default FileSelector;
|
type DropdownProps = {
|
||||||
|
name?: string,
|
||||||
|
multi?: boolean,
|
||||||
|
selection?: [],
|
||||||
|
children: React.ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DropdownMenu = (props: DropdownProps) => {
|
||||||
|
|
||||||
|
return <div>
|
||||||
|
<div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{props.children}
|
||||||
|
</div>
|
||||||
|
</div>;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ToggleSwitch = ({ value, onChange, ref, children }:
|
||||||
|
{ value: boolean, ref?: React.RefObject<HTMLInputElement>, onChange?: React.ChangeEventHandler, children: string }) => {
|
||||||
|
return <p>
|
||||||
|
<span className="check-box">
|
||||||
|
<b>{children}</b>
|
||||||
|
<input ref={ref} className="check-box" defaultChecked={value} onChange={onChange} type='checkbox' />
|
||||||
|
</span>
|
||||||
|
</p>;
|
||||||
|
};
|
@ -1,6 +1,6 @@
|
|||||||
import React, { useRef, useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { Route, Routes } from "react-router";
|
import { Route, Routes } from "react-router";
|
||||||
import FileSelector from "../components/FileSelector";
|
import { FileSelector } from "../components/Selectors";
|
||||||
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";
|
||||||
|
@ -57,7 +57,7 @@ const CredentialFields = () => {
|
|||||||
|
|
||||||
<form className="loginForm">
|
<form className="loginForm">
|
||||||
<input ref={usernameRef} autoComplete='off' placeholder='Username' required id='username' type='text' autoFocus />
|
<input ref={usernameRef} autoComplete='off' placeholder='Username' required id='username' type='text' autoFocus />
|
||||||
<input autoComplete='off' placeholder='Password' required id='password' type='password' />
|
<input ref={passwordRef} autoComplete='off' placeholder='Password' required id='password' type='password' />
|
||||||
<button className="button primary" onClick={loginClick}>Enter</button>
|
<button className="button primary" onClick={loginClick}>Enter</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
@ -1,27 +1,18 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useRef, useState } from "react";
|
||||||
import { Route, Routes, useNavigate, useParams } from "react-router";
|
import { Route, Routes, useNavigate, useParams } from "react-router";
|
||||||
import { BackButton, PageButtons } from "../../components/PageControls";
|
import { BackButton, PageButtons } from "../../components/PageControls";
|
||||||
import { Permissions } from "../../components/PermissionsView";
|
import { Permissions } from "../../views/PermissionsView";
|
||||||
import { RateLimits } from "../../components/RateLimitsView";
|
import { RateLimits } from "../../views/RateLimitsView";
|
||||||
import { Table, TableListEntry } from "../../components/Table";
|
import { Table, TableListEntry } from "../../components/Table";
|
||||||
import ErrorBoundary from "../../util/ErrorBoundary";
|
import ErrorBoundary from "../../util/ErrorBoundary";
|
||||||
import { get, patch } 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";
|
||||||
|
|
||||||
const Role = ({ role }: {role: R}) => {
|
const Role = ({ role }: {role: R}) => {
|
||||||
|
|
||||||
const perms = { ...role.permissions };
|
// const perms = { ...role.permissions };
|
||||||
const updatePerms = (chain: string, raw: string) => {
|
const [perms, updatePerms] = useState<Perms>(role.permissions);
|
||||||
const value = parseInt(raw);
|
|
||||||
if (isNaN(value)) return;
|
|
||||||
|
|
||||||
let selected = perms;
|
|
||||||
const keys = chain.split(':');
|
|
||||||
for (const key of keys) {
|
|
||||||
if (key === keys[keys.length - 1]) selected[key] = value;
|
|
||||||
else selected = selected[key] as Perms;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const commitPerms = async () => {
|
const commitPerms = async () => {
|
||||||
await patch(`/api/roles/${role.id}`, perms);
|
await patch(`/api/roles/${role.id}`, perms);
|
||||||
@ -42,7 +33,11 @@ const Role = ({ role }: {role: R}) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p><b>Created: </b>{new Date(role.createdTimestamp).toDateString()}</p>
|
<p><b>Created: </b>{new Date(role.createdTimestamp).toDateString()}</p>
|
||||||
<p><b>Disabled: </b>{role.disabled.toString()}</p>
|
{/* <p><b>Disabled: </b>{role.disabled.toString()}</p> */}
|
||||||
|
|
||||||
|
<ToggleSwitch value={role.disabled}>
|
||||||
|
Disabled
|
||||||
|
</ToggleSwitch>
|
||||||
|
|
||||||
<label htmlFor='username'>Name</label>
|
<label htmlFor='username'>Name</label>
|
||||||
<input autoComplete='off' id='username' defaultValue={role.name} />
|
<input autoComplete='off' id='username' defaultValue={role.name} />
|
||||||
@ -50,8 +45,8 @@ const Role = ({ role }: {role: R}) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="col-6-lg col-12">
|
<div className="col-6-lg col-12">
|
||||||
<Permissions updatePerms={updatePerms} perms={role.permissions} />
|
<Permissions onUpdate={updatePerms} perms={role.permissions} />
|
||||||
<button onClick={commitPerms} className="button primary">Update</button>
|
<button onClick={commitPerms} className="button primary">Save Permissions</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -62,6 +57,43 @@ const Role = ({ role }: {role: R}) => {
|
|||||||
</div>;
|
</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const CreateRoleField = ({ numRoles, className, addRole }: { numRoles: number, className?: string, addRole: (role: R) => void }) => {
|
||||||
|
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
const nameRef = useRef<HTMLInputElement>(null);
|
||||||
|
const positionRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
|
const createRole: React.MouseEventHandler<HTMLButtonElement> = async (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
if (!nameRef.current || !positionRef.current) return setError('Must supply name and position');
|
||||||
|
const name = nameRef.current.value;
|
||||||
|
const position = parseInt(positionRef.current.value);
|
||||||
|
|
||||||
|
const response = await post('/api/roles', { name, position });
|
||||||
|
if (!response.success) return setError(response.message || 'Unknown error');
|
||||||
|
addRole(response.data as R);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
return <div className={className}>
|
||||||
|
|
||||||
|
<h4>Create Role</h4>
|
||||||
|
|
||||||
|
{error && <p>{error}</p>}
|
||||||
|
<form>
|
||||||
|
<label>Name</label>
|
||||||
|
<input ref={nameRef} required={true} placeholder='Special access' autoComplete='off' type='text' />
|
||||||
|
|
||||||
|
<label>Position</label>
|
||||||
|
<input ref={positionRef} defaultValue={numRoles} type='number' min={0} max={numRoles} />
|
||||||
|
|
||||||
|
<button className='button primary' onClick={createRole}>Create</button>
|
||||||
|
</form>
|
||||||
|
</div>;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
const Roles = () => {
|
const Roles = () => {
|
||||||
|
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
@ -108,6 +140,9 @@ const Roles = () => {
|
|||||||
<PageButtons {...{page, setPage, length: roles.length}} />
|
<PageButtons {...{page, setPage, length: roles.length}} />
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<CreateRoleField addRole={role => setRoles([...roles, role])} className='col-6-lg col-12' numRoles={roles.length} />
|
||||||
|
|
||||||
</div>;
|
</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -5,10 +5,11 @@ import { get, post } from '../../util/Util';
|
|||||||
import '../../css/pages/Users.css';
|
import '../../css/pages/Users.css';
|
||||||
import { Table, TableListEntry } from "../../components/Table";
|
import { Table, TableListEntry } from "../../components/Table";
|
||||||
import { BackButton, PageButtons } from "../../components/PageControls";
|
import { BackButton, PageButtons } from "../../components/PageControls";
|
||||||
import { Permissions } from "../../components/PermissionsView";
|
import { Permissions } from "../../views/PermissionsView";
|
||||||
import { Application as App, Permissions as Perms, User as APIUser } from "../../@types/ApiStructures";
|
import { Application as App, Permissions as Perms, User as APIUser, Role } from "../../@types/ApiStructures";
|
||||||
|
import { DropdownMenu, ToggleSwitch } from "../../components/Selectors";
|
||||||
|
|
||||||
const ApplicationList = ({ apps }: {apps: App[]}) => {
|
const ApplicationList = ({ apps }: { apps: App[] }) => {
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
return <Table headerItems={['Name', 'ID']}>
|
return <Table headerItems={['Name', 'ID']}>
|
||||||
@ -24,12 +25,12 @@ const ApplicationList = ({ apps }: {apps: App[]}) => {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const Application = ({ app }: {app: App}) => {
|
const Application = ({ app }: { app: App }) => {
|
||||||
|
|
||||||
const descProps = {defaultValue: app.description || '', placeholder: 'Describe your application'};
|
const descProps = { defaultValue: app.description || '', placeholder: 'Describe your application' };
|
||||||
return <div>
|
return <div>
|
||||||
<h4>{app.name} ({app.id})</h4>
|
<h4>{app.name} ({app.id})</h4>
|
||||||
|
|
||||||
<b>Description</b>
|
<b>Description</b>
|
||||||
<textarea {...descProps} >
|
<textarea {...descProps} >
|
||||||
|
|
||||||
@ -37,31 +38,30 @@ const Application = ({ app }: {app: App}) => {
|
|||||||
</div>;
|
</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: Groups, description, notes
|
const Roles = ({ userRoles, roles }: { userRoles: Role[], roles: Role[] }) => {
|
||||||
const User = ({ user }: {user: APIUser}) => {
|
console.log(roles);
|
||||||
|
return <DropdownMenu>
|
||||||
|
{roles.map(role => {
|
||||||
|
return <div key={role.id}>
|
||||||
|
<label>{role.name}</label>
|
||||||
|
<input defaultChecked={Boolean(userRoles.find((r: Role) => r.id === role.id))} type='checkbox' />
|
||||||
|
</div>;
|
||||||
|
})}
|
||||||
|
</DropdownMenu>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const User = ({ user, roles }: { user: APIUser, roles: Role[] }) => {
|
||||||
|
|
||||||
const [apps, updateApps] = useState<App[]>([]);
|
const [apps, updateApps] = useState<App[]>([]);
|
||||||
const perms = {...user.permissions};
|
const [perms, updatePerms] = useState<Perms>(user.permissions);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
const response = await get(`/api/users/${user.id}/applications`);
|
const appsResponse = await get(`/api/users/${user.id}/applications`);
|
||||||
if(response.status === 200) updateApps(response.data as App[]);
|
if (appsResponse.success) updateApps(appsResponse.data as App[]);
|
||||||
})();
|
})();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const updatePerms = (chain: string, raw: string) => {
|
|
||||||
const value = parseInt(raw);
|
|
||||||
if (isNaN(value)) return;
|
|
||||||
|
|
||||||
let selected = perms;
|
|
||||||
const keys = chain.split(':');
|
|
||||||
for (const key of keys) {
|
|
||||||
if (key === keys[keys.length - 1]) selected[key] = value;
|
|
||||||
else selected = selected[key] as Perms;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const commitPerms = async () => {
|
const commitPerms = async () => {
|
||||||
await post(`/api/users/${user.id}/permissions`, perms);
|
await post(`/api/users/${user.id}/permissions`, perms);
|
||||||
};
|
};
|
||||||
@ -80,14 +80,12 @@ const User = ({ user }: {user: APIUser}) => {
|
|||||||
<BackButton />
|
<BackButton />
|
||||||
<h2 className="userId">User {user.id}</h2>
|
<h2 className="userId">User {user.id}</h2>
|
||||||
</div>
|
</div>
|
||||||
<div className="actionButtons mt-4">
|
|
||||||
{user.disabled ? <button className="button success">Enable</button> : <button className="button error">Disable</button>}
|
|
||||||
<button className="button error">Delete</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p><b>Created: </b>{new Date(user.createdTimestamp).toDateString()}</p>
|
<p><b>Created: </b>{new Date(user.createdTimestamp).toDateString()}</p>
|
||||||
<p><b>2FA: </b>{user.twoFactor.toString()}</p>
|
<p><b>2FA:</b> {(user.twoFactor && <button className='button danger'>Disable</button>) || 'Disabled'}</p>
|
||||||
<p><b>Disabled: </b>{user.disabled.toString()}</p>
|
<ToggleSwitch value={user.disabled}>
|
||||||
|
Login Disabled:
|
||||||
|
</ToggleSwitch>
|
||||||
|
|
||||||
<label htmlFor='username'>Username</label>
|
<label htmlFor='username'>Username</label>
|
||||||
<input autoComplete='off' id='username' defaultValue={user.name} />
|
<input autoComplete='off' id='username' defaultValue={user.name} />
|
||||||
@ -95,28 +93,40 @@ const User = ({ user }: {user: APIUser}) => {
|
|||||||
<label htmlFor='displayName'>Display Name</label>
|
<label htmlFor='displayName'>Display Name</label>
|
||||||
<input autoComplete='off' id='displayName' placeholder='Name to display instead of username' defaultValue={user.displayName || ''} />
|
<input autoComplete='off' id='displayName' placeholder='Name to display instead of username' defaultValue={user.displayName || ''} />
|
||||||
|
|
||||||
<h3 className="mt-5">Applications</h3>
|
<label htmlFor='userNote'>Note</label>
|
||||||
<Routes>
|
<textarea id='userNote' defaultValue={user.note} />
|
||||||
<Route path='/' element={<ApplicationList apps={apps} />} />
|
|
||||||
<Route path='/applications/:appid' element={<ApplicationWrapper />} />
|
<label>Roles</label>
|
||||||
</Routes>
|
<Roles userRoles={user.roles} roles={roles} />
|
||||||
|
|
||||||
|
|
||||||
|
<button className="button primary">
|
||||||
|
Save User
|
||||||
|
</button>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="col-6-lg col-12">
|
<div className="col-6-lg col-12">
|
||||||
<Permissions updatePerms={updatePerms} perms={user.permissions} />
|
<Permissions onUpdate={updatePerms} perms={user.permissions} />
|
||||||
<button onClick={commitPerms} className="button primary">Update</button>
|
<button onClick={commitPerms} className="button primary">Save Permissions</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='row'>
|
||||||
|
<div className='col-6-lg col-12'>
|
||||||
|
<h3>Applications</h3>
|
||||||
|
<BackButton />
|
||||||
|
<Routes>
|
||||||
|
<Route path='/' element={<ApplicationList apps={apps} />} />
|
||||||
|
<Route path='/applications/:appid' element={<ApplicationWrapper />} />
|
||||||
|
</Routes>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button className="button primary">
|
|
||||||
Save
|
|
||||||
</button>
|
|
||||||
|
|
||||||
</div>;
|
</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const CreateUserField = () => {
|
const CreateUserField = ({ className }: { className: string }) => {
|
||||||
|
|
||||||
const [link, setLink] = useState<string | null>(null);
|
const [link, setLink] = useState<string | null>(null);
|
||||||
const getSignupCode = async () => {
|
const getSignupCode = async () => {
|
||||||
@ -131,8 +141,8 @@ const CreateUserField = () => {
|
|||||||
const element = event.target as HTMLElement;
|
const element = event.target as HTMLElement;
|
||||||
const { parentElement } = element;
|
const { parentElement } = element;
|
||||||
navigator.clipboard.writeText(element.innerText);
|
navigator.clipboard.writeText(element.innerText);
|
||||||
if(parentElement)
|
if (parentElement)
|
||||||
(parentElement.childNodes[parentElement.childNodes.length-1] as HTMLElement).style.visibility = "visible";
|
(parentElement.childNodes[parentElement.childNodes.length - 1] as HTMLElement).style.visibility = "visible";
|
||||||
};
|
};
|
||||||
|
|
||||||
const closeToolTip: React.MouseEventHandler = (event) => {
|
const closeToolTip: React.MouseEventHandler = (event) => {
|
||||||
@ -140,12 +150,12 @@ const CreateUserField = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// TODO: Finish this
|
// TODO: Finish this
|
||||||
return <div>
|
return <div className={className}>
|
||||||
<h4>One-Click Sign-Up</h4>
|
<h4>One-Click Sign-Up</h4>
|
||||||
{link ?
|
{link ?
|
||||||
<div className="registerCodeWrapper tooltip"><p onClick={copyToClipboard}>{link}</p><span onClick={closeToolTip} className="tooltiptext">Code copied!</span></div> :
|
<div className="registerCodeWrapper tooltip"><p onClick={copyToClipboard}>{link}</p><span onClick={closeToolTip} className="tooltiptext">Code copied!</span></div> :
|
||||||
<button onClick={getSignupCode} className="button primary is-center">Create sign-up link</button>}
|
<button onClick={getSignupCode} className="button primary is-center">Create sign-up link</button>}
|
||||||
<hr/>
|
<hr />
|
||||||
<h4>Create User</h4>
|
<h4>Create User</h4>
|
||||||
<form>
|
<form>
|
||||||
<p>Users will be forced to change the password you set here.</p>
|
<p>Users will be forced to change the password you set here.</p>
|
||||||
@ -159,6 +169,7 @@ const CreateUserField = () => {
|
|||||||
const Users = () => {
|
const Users = () => {
|
||||||
|
|
||||||
const [users, setUsers] = useState<APIUser[]>([]);
|
const [users, setUsers] = useState<APIUser[]>([]);
|
||||||
|
const [roles, updateRoles] = useState<Role[]>([]);
|
||||||
const [page, setPage] = useState<number>(1);
|
const [page, setPage] = useState<number>(1);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
@ -171,14 +182,16 @@ const Users = () => {
|
|||||||
setUsers(result.data as APIUser[]);
|
setUsers(result.data as APIUser[]);
|
||||||
} else setError(result.message || 'Unknown error');
|
} else setError(result.message || 'Unknown error');
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
const rolesResponse = await get('/api/roles');
|
||||||
|
if (rolesResponse.success) updateRoles(rolesResponse.data as Role[]);
|
||||||
})();
|
})();
|
||||||
}, [page]);
|
}, [page]);
|
||||||
|
|
||||||
const UserWrapper = () => {
|
const UserWrapper = () => {
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
const user = users.find(u => u.id === id);
|
const user = users.find(u => u.id === id);
|
||||||
if (!user) return <p>Unknown user</p>;
|
if (!user) return <p>Unknown user</p>;
|
||||||
return <User user={user} />;
|
return <User roles={roles} user={user} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@ -196,12 +209,10 @@ const Users = () => {
|
|||||||
itemKeys={['name', 'id']} />)}
|
itemKeys={['name', 'id']} />)}
|
||||||
</Table>
|
</Table>
|
||||||
|
|
||||||
<PageButtons {...{page, setPage, length: users.length}} />
|
<PageButtons {...{ page, setPage, length: users.length }} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="col-6-lg col-12">
|
<CreateUserField className="col-6-lg col-12" />
|
||||||
<CreateUserField />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</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/FileSelector";
|
import { FileSelector } from "../../components/Selectors";
|
||||||
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}) => {
|
||||||
|
@ -13,10 +13,10 @@ class ErrorBoundary extends React.Component<BoundaryProps, StateProps> {
|
|||||||
|
|
||||||
fallback?: React.ReactNode;
|
fallback?: React.ReactNode;
|
||||||
|
|
||||||
constructor({fallback, ...props}: BoundaryProps) {
|
constructor(props: BoundaryProps) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = { error: false };
|
this.state = { error: false };
|
||||||
this.fallback = fallback;
|
this.fallback = props.fallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
override componentDidCatch(error: Error, errorInfo: unknown) {
|
override componentDidCatch(error: Error, errorInfo: unknown) {
|
||||||
|
@ -82,6 +82,7 @@ export const post = async (url: string, body?: object | string, opts: RequestOpt
|
|||||||
if (options.headers && options.headers['Content-Type'] === 'application/json' && body) options.body = JSON.stringify(body);
|
if (options.headers && options.headers['Content-Type'] === 'application/json' && body) options.body = JSON.stringify(body);
|
||||||
else options.body = body as string;
|
else options.body = body as string;
|
||||||
|
|
||||||
|
console.log(url, options);
|
||||||
const response = await fetch(url, options);
|
const response = await fetch(url, options);
|
||||||
return parseResponse(response);
|
return parseResponse(response);
|
||||||
};
|
};
|
||||||
|
@ -33,7 +33,20 @@ export const PermissionGroup = ({ name, value, chain, updatePerms }: {name: stri
|
|||||||
</li>;
|
</li>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Permissions = ({ perms, updatePerms }: {perms: Perms, updatePerms: (chain: string, perm: string) => void}) => {
|
export const Permissions = ({ perms, onUpdate }: { perms: Perms, onUpdate: (perms: Perms) => void }) => {
|
||||||
|
|
||||||
|
const updatePerms = (chain: string, raw: string) => {
|
||||||
|
const value = parseInt(raw);
|
||||||
|
if (isNaN(value)) return;
|
||||||
|
|
||||||
|
let selected = perms;
|
||||||
|
const keys = chain.split(':');
|
||||||
|
for (const key of keys) {
|
||||||
|
if (key === keys[keys.length - 1]) selected[key] = value;
|
||||||
|
else selected = selected[key] as Perms;
|
||||||
|
}
|
||||||
|
onUpdate(perms);
|
||||||
|
};
|
||||||
|
|
||||||
const elements = [];
|
const elements = [];
|
||||||
const keys = Object.keys(perms);
|
const keys = Object.keys(perms);
|
Loading…
Reference in New Issue
Block a user