diff --git a/package.json b/package.json
index bcf6a48..859e21b 100644
--- a/package.json
+++ b/package.json
@@ -1,51 +1,52 @@
-{
- "name": "frontend",
- "version": "0.1.0",
- "private": true,
- "dependencies": {
- "@types/node": "^18.15.11",
- "@types/react": "^18.0.37",
- "@types/react-dom": "^18.0.11",
- "react": "^18.2.0",
- "react-dom": "^18.2.0",
- "react-router": "^6.4.3",
- "react-router-dom": "^6.4.3",
- "react-scripts": "5.0.1",
- "react-top-loading-bar": "^2.3.1",
- "typescript": "^5.0.4",
- "web-vitals": "^2.1.4"
- },
- "devDependencies": {
- "@testing-library/jest-dom": "^5.16.5",
- "@testing-library/react": "^13.4.0",
- "@testing-library/user-event": "^13.5.0",
- "@types/jest": "^29.5.1",
- "eslint": "^8.27.0",
- "eslint-plugin-react": "^7.31.10",
- "http-proxy-middleware": "^2.0.6"
- },
- "scripts": {
- "start": "react-scripts start",
- "build": "react-scripts build",
- "test": "react-scripts test",
- "eject": "react-scripts eject"
- },
- "eslintConfig": {
- "extends": [
- "react-app",
- "react-app/jest"
- ]
- },
- "browserslist": {
- "production": [
- ">0.2%",
- "not dead",
- "not op_mini all"
- ],
- "development": [
- "last 1 chrome version",
- "last 1 firefox version",
- "last 1 safari version"
- ]
- }
-}
+{
+ "name": "frontend",
+ "version": "0.1.0",
+ "private": true,
+ "dependencies": {
+ "@types/node": "^18.15.11",
+ "@types/react": "^18.0.37",
+ "@types/react-dom": "^18.0.11",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0",
+ "react-router": "^6.4.3",
+ "react-router-dom": "^6.4.3",
+ "react-scripts": "5.0.1",
+ "react-top-loading-bar": "^2.3.1",
+ "typescript": "^5.0.4",
+ "web-vitals": "^2.1.4"
+ },
+ "devDependencies": {
+ "@testing-library/jest-dom": "^5.16.5",
+ "@testing-library/react": "^13.4.0",
+ "@testing-library/user-event": "^13.5.0",
+ "@types/jest": "^29.5.1",
+ "eslint": "^8.27.0",
+ "eslint-plugin-react": "^7.31.10",
+ "http-proxy-middleware": "^2.0.6"
+ },
+ "scripts": {
+ "start": "react-scripts start",
+ "build": "react-scripts build",
+ "test": "react-scripts test",
+ "eject": "react-scripts eject",
+ "lint": "eslint src/ --fix"
+ },
+ "eslintConfig": {
+ "extends": [
+ "react-app",
+ "react-app/jest"
+ ]
+ },
+ "browserslist": {
+ "production": [
+ ">0.2%",
+ "not dead",
+ "not op_mini all"
+ ],
+ "development": [
+ "last 1 chrome version",
+ "last 1 firefox version",
+ "last 1 safari version"
+ ]
+ }
+}
diff --git a/src/App.tsx b/src/App.tsx
index ca3341e..9389a60 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,119 +1,125 @@
-import React, { useEffect, useState } from 'react';
-import { Navigate, Route, Routes, useNavigate} from 'react-router-dom';
-
-import './css/App.css';
-import Home from './pages/Home';
-import ErrorBoundary from './util/ErrorBoundary';
-import Sidebar, { SidebarMenu } from './components/Sidebar';
-import UserControls from './components/UserControls';
-import { useLoginContext } from './structures/UserContext';
-import Login from './pages/Login';
-import { get, setSession, setSettings } from './util/Util';
-import { PrivateRoute } from './structures/PrivateRoute';
-import { UnauthedRoute } from './structures/UnauthedRoute';
-import Admin from './pages/Admin';
-import TitledPage from './components/TitledPage';
-import Register from './pages/Register';
-import { ClientSettings } from './@types/Other';
-import { User } from './@types/ApiStructures';
-
-function App() {
-
- const [user, updateUser] = useLoginContext();
- const [loading, setLoading] = useState(true);
- const navigate = useNavigate();
-
- useEffect(() => {
- (async () => {
-
- const settings = await get('/api/settings');
- setSettings(settings.data as ClientSettings);
-
- const result = await get('/api/user');
- if (result.status === 200) {
- setSession(result.data as User);
- updateUser();
- }
- setLoading(false);
- if (result.data?.twoFactor) return navigate('/login/verify');
- })();
- }, []);
-
- const menuItems = [
- {
- to: '/home', label: 'Home', items: [
- { to: '/profile', label: 'Profile', relative: true },
- { to: '/applications', label: 'Applications', relative: true }
- ]
- },
- {
- to: '/admin', label: 'Admin', items: [
- { to: '/users', label: 'Users', relative: true },
- { to: '/roles', label: 'Roles', relative: true },
- { to: '/flags', label: 'Flags', relative: true }
- ]
- }
- ];
-
- if (loading) return null;
-
- return (
-
-
-
-
- {user ?
-
-
-
-
-
-
- : null}
-
-
-
-
-
-
-
-
-
-
-
- } />
-
-
-
-
-
- } />
-
-
-
- } />
-
-
-
- } />
-
- } />
-
-
-
-
-
-
-
-
-
-
-
- );
-}
-
-export default App;
+import React, { useEffect, useState } from 'react';
+import { Navigate, Route, Routes, useNavigate} from 'react-router-dom';
+
+import './css/App.css';
+import Home from './pages/Home';
+import ErrorBoundary from './util/ErrorBoundary';
+import Sidebar, { SidebarMenu } from './components/Sidebar';
+import UserControls from './components/UserControls';
+import { useLoginContext } from './structures/UserContext';
+import Login from './pages/Login';
+import { get, setSession, setSettings } from './util/Util';
+import { PrivateRoute } from './structures/PrivateRoute';
+import { UnauthedRoute } from './structures/UnauthedRoute';
+import Admin from './pages/Admin';
+import TitledPage from './components/TitledPage';
+import Register from './pages/Register';
+import { ClientSettings } from './@types/Other';
+import { User } from './@types/ApiStructures';
+
+function App()
+{
+
+ const [user, updateUser] = useLoginContext();
+ const [loading, setLoading] = useState(true);
+ const navigate = useNavigate();
+
+ useEffect(() =>
+ {
+ (async () =>
+ {
+
+ const settings = await get('/api/settings');
+ setSettings(settings.data as ClientSettings);
+
+ const result = await get('/api/user');
+ if (result.status === 200)
+ {
+ setSession(result.data as User);
+ updateUser();
+ }
+ setLoading(false);
+ if (result.data?.twoFactor)
+ return navigate('/login/verify');
+ })();
+ }, []);
+
+ const menuItems = [
+ {
+ to: '/home', label: 'Home', items: [
+ { to: '/profile', label: 'Profile', relative: true },
+ { to: '/applications', label: 'Applications', relative: true }
+ ]
+ },
+ {
+ to: '/admin', label: 'Admin', items: [
+ { to: '/users', label: 'Users', relative: true },
+ { to: '/roles', label: 'Roles', relative: true },
+ { to: '/flags', label: 'Flags', relative: true }
+ ]
+ }
+ ];
+
+ if (loading)
+ return null;
+
+ return (
+
+
+
+
+ {user ?
+
+
+
+
+
+
+ : null}
+
+
+
+
+
+
+
+
+
+
+
+ } />
+
+
+
+
+
+ } />
+
+
+
+ } />
+
+
+
+ } />
+
+ } />
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+export default App;
diff --git a/src/components/InputElements.tsx b/src/components/InputElements.tsx
index a679a87..0e58ff9 100644
--- a/src/components/InputElements.tsx
+++ b/src/components/InputElements.tsx
@@ -1,217 +1,245 @@
-import React, { Children, useRef, useState } from "react";
-import ClickDetector from "../util/ClickDetector";
-import { DropdownBaseProps, DropdownItemProps } from "../@types/Components";
-import '../css/components/InputElements.css';
-
-export const FileSelector = ({ cb }: { cb: (file: File) => void }) => {
-
- if (!cb) throw new Error('Missing callback');
- const [file, setFile] = useState(null);
-
- const onDragOver: React.MouseEventHandler = (event) => {
- event.stopPropagation();
- event.preventDefault();
- };
-
- const onDrop: React.DragEventHandler = (event) => {
- event.preventDefault();
- const { dataTransfer } = event;
- if (!dataTransfer.files.length) return;
-
- const [file] = dataTransfer.files;
- setFile(file);
- cb(file);
- };
-
- return ;
-};
-
-export type InputElementProperties = {
- value?: T,
- inputRef?: React.RefObject,
- onChange?: React.ChangeEventHandler,
- children?: React.ReactNode, //JSX.Element | JSX.Element[] | string,
- placeholder?: string
-}
-
-export const ToggleSwitch = ({ value, onChange, inputRef, children }: InputElementProperties) => {
- return ;
-};
-
-export const VerticalToggleSwitch = ({ value, onChange, inputRef, children }: InputElementProperties) => {
- return ;
-};
-
-type ManualInputProperties = {
- onBlur?: React.FocusEventHandler,
- onKeyUp?: React.KeyboardEventHandler
-} & InputElementProperties;
-
-type StringInputProperties = {
- maxLength?: number,
- minLength?: number
-} & ManualInputProperties
-export const StringInput = ({ value, onChange, inputRef, children, placeholder, onBlur, onKeyUp, minLength, maxLength }: StringInputProperties) => {
-
- const input = ;
- if (children)
- return ;
- return input;
-};
-
-export type NumberInputProperties = {
- min?: number,
- max?: number,
- type?: 'float' | 'int',
- step?: number
-} & ManualInputProperties
-
-export const NumberInput = ({ children, placeholder, inputRef, onChange, value, min, max, type, step }: NumberInputProperties) => {
- if (typeof step === 'undefined') {
- if (type === 'float') step = 0.1;
- else if (type === 'int') step = 1;
- else step = 1;
- }
-
- const input = ;
-
- if (children) return ;
-
- return input;
-};
-
-export const ClickToEdit = ({ value, onUpdate, inputElement }:
- { value: string, onUpdate?: (value: string) => void, inputElement?: React.ReactElement }) => {
- const [editing, setEditing] = useState(false);
- const ref = useRef(null);
- const onClick = () => {
- setEditing(false);
- if (ref.current && onUpdate)
- onUpdate(ref.current.value);
- };
-
- const input = inputElement ? inputElement : ;
- if (editing) return
- {input}
-
- ;
- return setEditing(true)} className='mt-0 mb-1 clickable'>{value};
-};
-
-const DropdownHeader = ({ children, className = '' }: DropdownBaseProps) => {
- return
- {children}
- ;
-};
-
-const DropdownItem = ({ children, type, selected, onClick }: DropdownItemProps) => {
- let InnerElement = null;
- if (type === 'multi-select')
- InnerElement =
- {children as string}
- ;
- else InnerElement = {
- event.preventDefault();
- if (onClick) onClick(event);
- }}>
- {children}
-
;
- return
- {InnerElement}
-
;
-};
-
-const DropdownItemList = ({ children, className = '' }: DropdownBaseProps) => {
- return
- {children}
-
;
-};
-
-type DropdownProps = {
- name?: string,
- multi?: boolean,
- selection?: [],
- children: React.ReactNode[],
- className?: string
-}
-
-const DropdownComp = ({ children, className = '' }: DropdownProps) => {
-
- if (!children)
- throw new Error('Missing children');
-
- if (!children.some(element => element && (element as React.ReactElement).type === DropdownHeader))
- throw new Error('Missing a header');
-
- const detailsRef = useRef(null);
-
- return {
- if (detailsRef.current) detailsRef.current.open = false;
- }}>
-
- {children}
-
- ;
-
-};
-
-const Dropdown = Object.assign(DropdownComp, {
- Header: DropdownHeader,
- ItemList: DropdownItemList,
- Item: DropdownItem
-});
-
-export type InputElementType =
- | ((props: InputElementProperties) => React.ReactElement)
- | ((props: InputElementProperties) => React.ReactElement)
- | ((props: NumberInputProperties) => React.ReactElement);
-
+import React, { Children, useRef, useState } from "react";
+import ClickDetector from "../util/ClickDetector";
+import { DropdownBaseProps, DropdownItemProps } from "../@types/Components";
+import '../css/components/InputElements.css';
+
+export const FileSelector = ({ cb }: { cb: (file: File) => void }) =>
+{
+
+ if (!cb)
+ throw new Error('Missing callback');
+ const [file, setFile] = useState(null);
+
+ const onDragOver: React.MouseEventHandler = (event) =>
+ {
+ event.stopPropagation();
+ event.preventDefault();
+ };
+
+ const onDrop: React.DragEventHandler = (event) =>
+ {
+ event.preventDefault();
+ const { dataTransfer } = event;
+ if (!dataTransfer.files.length)
+ return;
+
+ const [file] = dataTransfer.files;
+ setFile(file);
+ cb(file);
+ };
+
+ return ;
+};
+
+export type InputElementProperties = {
+ value?: T,
+ inputRef?: React.RefObject,
+ onChange?: React.ChangeEventHandler,
+ children?: React.ReactNode, //JSX.Element | JSX.Element[] | string,
+ placeholder?: string
+}
+
+export const ToggleSwitch = ({ value, onChange, inputRef, children }: InputElementProperties) =>
+{
+ return ;
+};
+
+export const VerticalToggleSwitch = ({ value, onChange, inputRef, children }: InputElementProperties) =>
+{
+ return ;
+};
+
+type ManualInputProperties = {
+ onBlur?: React.FocusEventHandler,
+ onKeyUp?: React.KeyboardEventHandler
+} & InputElementProperties;
+
+type StringInputProperties = {
+ maxLength?: number,
+ minLength?: number
+} & ManualInputProperties
+export const StringInput = ({ value, onChange, inputRef, children, placeholder, onBlur, onKeyUp, minLength, maxLength }: StringInputProperties) =>
+{
+
+ const input = ;
+ if (children)
+ return ;
+ return input;
+};
+
+export type NumberInputProperties = {
+ min?: number,
+ max?: number,
+ type?: 'float' | 'int',
+ step?: number
+} & ManualInputProperties
+
+export const NumberInput = ({ children, placeholder, inputRef, onChange, value, min, max, type, step }: NumberInputProperties) =>
+{
+ if (typeof step === 'undefined')
+ {
+ if (type === 'float')
+ step = 0.1;
+ else if (type === 'int')
+ step = 1;
+ else
+ step = 1;
+ }
+
+ const input = ;
+
+ if (children)
+ return ;
+
+ return input;
+};
+
+export const ClickToEdit = ({ value, onUpdate, inputElement }:
+ { value: string, onUpdate?: (value: string) => void, inputElement?: React.ReactElement }) =>
+{
+ const [editing, setEditing] = useState(false);
+ const ref = useRef(null);
+ const onClick = () =>
+ {
+ setEditing(false);
+ if (ref.current && onUpdate)
+ onUpdate(ref.current.value);
+ };
+
+ const input = inputElement ? inputElement : ;
+ if (editing)
+ return
+ {input}
+
+ ;
+ return setEditing(true)} className='mt-0 mb-1 clickable'>{value};
+};
+
+const DropdownHeader = ({ children, className = '' }: DropdownBaseProps) =>
+{
+ return
+ {children}
+ ;
+};
+
+const DropdownItem = ({ children, type, selected, onClick }: DropdownItemProps) =>
+{
+ let InnerElement = null;
+ if (type === 'multi-select')
+ InnerElement =
+ {children as string}
+ ;
+ else
+ InnerElement =
+ {
+ event.preventDefault();
+ if (onClick)
+ onClick(event);
+ }}>
+ {children}
+
;
+ return
+ {InnerElement}
+
;
+};
+
+const DropdownItemList = ({ children, className = '' }: DropdownBaseProps) =>
+{
+ return
+ {children}
+
;
+};
+
+type DropdownProps = {
+ name?: string,
+ multi?: boolean,
+ selection?: [],
+ children: React.ReactNode[],
+ className?: string
+}
+
+const DropdownComp = ({ children, className = '' }: DropdownProps) =>
+{
+
+ if (!children)
+ throw new Error('Missing children');
+
+ if (!children.some(element => element && (element as React.ReactElement).type === DropdownHeader))
+ throw new Error('Missing a header');
+
+ const detailsRef = useRef(null);
+
+ return
+ {
+ if (detailsRef.current)
+ detailsRef.current.open = false;
+ }}>
+
+ {children}
+
+ ;
+
+};
+
+const Dropdown = Object.assign(DropdownComp, {
+ Header: DropdownHeader,
+ ItemList: DropdownItemList,
+ Item: DropdownItem
+});
+
+export type InputElementType =
+ | ((props: InputElementProperties) => React.ReactElement)
+ | ((props: InputElementProperties) => React.ReactElement)
+ | ((props: NumberInputProperties) => React.ReactElement);
+
export { Dropdown };
\ No newline at end of file
diff --git a/src/components/PageControls.tsx b/src/components/PageControls.tsx
index 64cbbe9..052363f 100644
--- a/src/components/PageControls.tsx
+++ b/src/components/PageControls.tsx
@@ -7,22 +7,27 @@ type PageButtonProps = {
pages: number
}
-export const PageButtons = ({ setPage, page, pages }: PageButtonProps) => {
+export const PageButtons = ({ setPage, page, pages }: PageButtonProps) =>
+{
return
-
;
};
-export const BackButton = () => {
+export const BackButton = () =>
+{
const navigate = useNavigate();
- return {
+ return
+ {
navigate(-1);
}}>Back;
};
\ No newline at end of file
diff --git a/src/components/PageElements.tsx b/src/components/PageElements.tsx
index 1123782..248e2c1 100644
--- a/src/components/PageElements.tsx
+++ b/src/components/PageElements.tsx
@@ -1,8 +1,10 @@
import React from "react";
import '../css/components/PageElements.css';
-export const Popup = ({ display = false, children }: { display: boolean, children: React.ReactElement }) => {
- if (!display) return null;
+export const Popup = ({ display = false, children }: { display: boolean, children: React.ReactElement }) =>
+{
+ if (!display)
+ return null;
return
{children}
;
diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx
index d0a43b4..a39a06b 100644
--- a/src/components/Sidebar.tsx
+++ b/src/components/Sidebar.tsx
@@ -1,82 +1,95 @@
-import React, {} from 'react';
-import '../css/components/Sidebar.css';
-import NavLink from '../structures/NavLink';
-import logoImg from '../img/logo.png';
-import { useNavigate } from "react-router";
-import { useLoginContext } from "../structures/UserContext";
-import { clearSession, post } from "../util/Util";
-import { SidebarMenuItem } from '../@types/Other';
-
-const Sidebar = ({children}: {children?: React.ReactNode}) => {
- return
- {children}
-
;
-};
-
-const toggleMenu: React.MouseEventHandler = (event) => {
- event.preventDefault();
- (event.target as HTMLElement).parentElement?.classList.toggle("open");
-};
-
-const expandMobileMenu: React.MouseEventHandler = (event) => {
- event.preventDefault();
- (event.target as HTMLElement).parentElement?.parentElement?.classList.toggle("show-menu");
-};
-
-const closeMobileMenu: React.MouseEventHandler = (event) => {
- const element = event.target as HTMLElement;
- if(element.classList.contains("sidebar-menu-item-carrot")) return;
- (element.getRootNode() as ParentNode).querySelector(".sidebar")?.classList.remove("show-menu");
-};
-
-// Nav menu
-const SidebarMenu = ({menuItems = [], children}: {menuItems: SidebarMenuItem[], children?: React.ReactNode}) => {
- const [user, updateUser] = useLoginContext();
- const navigate = useNavigate();
-
- if (!user) return null;
-
- const logOut = async () => {
- const response = await post('/api/logout');
- if (response.status === 200) {
- clearSession();
- updateUser();
- navigate('/login');
- }
- };
-
- const elements = [];
- for (const menuItem of menuItems) {
- const { label, items: subItems = [], ...rest } = menuItem;
-
- let subElements = null;
- if (subItems.length) subElements = subItems.map(({ label, to, relative = true }) => {
- if(relative) to = `${menuItem.to}${to}`;
- return {label};
- });
-
- elements.push(
-
- {label}{subElements && }
-
- {subElements &&
- {subElements}
-
}
-
);
- }
-
- return
-
-
- {elements}
-
- {children}
-
-
;
-
-};
-
-export {SidebarMenu, Sidebar};
+import React, {} from 'react';
+import '../css/components/Sidebar.css';
+import NavLink from '../structures/NavLink';
+import logoImg from '../img/logo.png';
+import { useNavigate } from "react-router";
+import { useLoginContext } from "../structures/UserContext";
+import { clearSession, post } from "../util/Util";
+import { SidebarMenuItem } from '../@types/Other';
+
+const Sidebar = ({children}: {children?: React.ReactNode}) =>
+{
+ return
+ {children}
+
;
+};
+
+const toggleMenu: React.MouseEventHandler = (event) =>
+{
+ event.preventDefault();
+ (event.target as HTMLElement).parentElement?.classList.toggle("open");
+};
+
+const expandMobileMenu: React.MouseEventHandler = (event) =>
+{
+ event.preventDefault();
+ (event.target as HTMLElement).parentElement?.parentElement?.classList.toggle("show-menu");
+};
+
+const closeMobileMenu: React.MouseEventHandler = (event) =>
+{
+ const element = event.target as HTMLElement;
+ if(element.classList.contains("sidebar-menu-item-carrot"))
+ return;
+ (element.getRootNode() as ParentNode).querySelector(".sidebar")?.classList.remove("show-menu");
+};
+
+// Nav menu
+const SidebarMenu = ({menuItems = [], children}: {menuItems: SidebarMenuItem[], children?: React.ReactNode}) =>
+{
+ const [user, updateUser] = useLoginContext();
+ const navigate = useNavigate();
+
+ if (!user)
+ return null;
+
+ const logOut = async () =>
+ {
+ const response = await post('/api/logout');
+ if (response.status === 200)
+ {
+ clearSession();
+ updateUser();
+ navigate('/login');
+ }
+ };
+
+ const elements = [];
+ for (const menuItem of menuItems)
+ {
+ const { label, items: subItems = [], ...rest } = menuItem;
+
+ let subElements = null;
+ if (subItems.length)
+ subElements = subItems.map(({ label, to, relative = true }) =>
+ {
+ if(relative)
+ to = `${menuItem.to}${to}`;
+ return {label};
+ });
+
+ elements.push(
+
+ {label}{subElements && }
+
+ {subElements &&
+ {subElements}
+
}
+
);
+ }
+
+ return
+
+
+ {elements}
+
+ {children}
+
+
;
+
+};
+
+export {SidebarMenu, Sidebar};
export default Sidebar;
\ No newline at end of file
diff --git a/src/components/Table.tsx b/src/components/Table.tsx
index 93d9abf..7589194 100644
--- a/src/components/Table.tsx
+++ b/src/components/Table.tsx
@@ -1,45 +1,47 @@
-import React from "react";
-
-type Item = {
- [key: string]: unknown
-}
-
-type TableEntry = {
- onClick?: () => void,
- item: Item,
- itemKeys: string[]
-}
-
-type TableProps = {
- headerItems: string[],
- children?: React.ReactNode,
- items?: Item[],
- itemKeys?: string[]
-}
-
-export const TableListEntry = ({onClick, item, itemKeys}: TableEntry) => {
-
- return
- {itemKeys.map(key => {item[key] as string} | )}
-
;
-};
-
-export const Table = ({headerItems, children, items, itemKeys}: TableProps) => {
-
- if (!headerItems)
- throw new Error(`Missing table headers`);
- if(!children && !items)
- throw new Error('Missing items or children');
- let i = 0;
- return
-
-
- {headerItems.map(item => {item} | )}
-
-
-
- {children ? children : items?.map(item => )}
-
-
;
-
+import React from "react";
+
+type Item = {
+ [key: string]: unknown
+}
+
+type TableEntry = {
+ onClick?: () => void,
+ item: Item,
+ itemKeys: string[]
+}
+
+type TableProps = {
+ headerItems: string[],
+ children?: React.ReactNode,
+ items?: Item[],
+ itemKeys?: string[]
+}
+
+export const TableListEntry = ({onClick, item, itemKeys}: TableEntry) =>
+{
+
+ return
+ {itemKeys.map(key => {item[key] as string} | )}
+
;
+};
+
+export const Table = ({headerItems, children, items, itemKeys}: TableProps) =>
+{
+
+ if (!headerItems)
+ throw new Error(`Missing table headers`);
+ if(!children && !items)
+ throw new Error('Missing items or children');
+ let i = 0;
+ return
+
+
+ {headerItems.map(item => {item} | )}
+
+
+
+ {children ? children : items?.map(item => )}
+
+
;
+
};
\ No newline at end of file
diff --git a/src/components/TitledPage.tsx b/src/components/TitledPage.tsx
index 581281b..d6e8b5a 100644
--- a/src/components/TitledPage.tsx
+++ b/src/components/TitledPage.tsx
@@ -1,14 +1,15 @@
-import React from "react";
-import '../css/pages/Empty.css';
-
-const TitledPage = ({title, children}: {title: string, children: React.ReactNode}) => {
-
- return
-
{title}
-
- {children}
-
-
;
-};
-
+import React from "react";
+import '../css/pages/Empty.css';
+
+const TitledPage = ({title, children}: {title: string, children: React.ReactNode}) =>
+{
+
+ return
+
{title}
+
+ {children}
+
+
;
+};
+
export default TitledPage;
\ No newline at end of file
diff --git a/src/components/UserControls.tsx b/src/components/UserControls.tsx
index 188a653..cc00e4e 100644
--- a/src/components/UserControls.tsx
+++ b/src/components/UserControls.tsx
@@ -1,45 +1,51 @@
-import React, { useRef } from "react";
-import { useNavigate } from "react-router";
-import '../css/components/UserControls.css';
-import { useLoginContext } from "../structures/UserContext";
-import ClickDetector from "../util/ClickDetector";
-import { clearSession, post } from "../util/Util";
-
-const UserControls = () => {
-
- const [user, updateUser] = useLoginContext();
- const detailsRef = useRef(null);
- const navigate = useNavigate();
-
- if (!user) return null;
-
- const logOut = async () => {
- const response = await post('/api/logout');
- if (response.status === 200) {
- clearSession();
- updateUser();
- navigate('/login');
- }
- };
-
- return {
- if (detailsRef.current) detailsRef.current.removeAttribute('open');
- }}>
-
-
- Hello {user.displayName || user.name}
-
-
-
-
-
-
- ;
-
-};
-
+import React, { useRef } from "react";
+import { useNavigate } from "react-router";
+import '../css/components/UserControls.css';
+import { useLoginContext } from "../structures/UserContext";
+import ClickDetector from "../util/ClickDetector";
+import { clearSession, post } from "../util/Util";
+
+const UserControls = () =>
+{
+
+ const [user, updateUser] = useLoginContext();
+ const detailsRef = useRef(null);
+ const navigate = useNavigate();
+
+ if (!user)
+ return null;
+
+ const logOut = async () =>
+ {
+ const response = await post('/api/logout');
+ if (response.status === 200)
+ {
+ clearSession();
+ updateUser();
+ navigate('/login');
+ }
+ };
+
+ return
+ {
+ if (detailsRef.current)
+ detailsRef.current.removeAttribute('open');
+ }}>
+
+
+ Hello {user.displayName || user.name}
+
+
+
+
+
+
+ ;
+
+};
+
export default UserControls;
\ No newline at end of file
diff --git a/src/index.tsx b/src/index.tsx
index 2dcb30b..7722ce3 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -8,11 +8,11 @@ import { BrowserRouter } from 'react-router-dom';
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
root.render(
-
-
-
-
-
+
+
+
+
+
);
// root.render(
//
diff --git a/src/pages/Admin.tsx b/src/pages/Admin.tsx
index 6c5584f..d8d6f32 100644
--- a/src/pages/Admin.tsx
+++ b/src/pages/Admin.tsx
@@ -1,51 +1,54 @@
-import React, { useState } from "react";
-import { Route, Routes } from "react-router";
-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 = () => {
- const [file, setFile] = useState(null);
-
- const submit: React.MouseEventHandler = (event) => {
- event.preventDefault();
- console.log(file);
- };
-
- return
-
-
-
Thematic customisation
-
-
-
-
-
-
Dingus
-
-
-
-
;
-};
-
-const Admin = () => {
-
- return
-
- } />
- } />
- } />
- } />
-
- ;
-};
-
+import React, { useState } from "react";
+import { Route, Routes } from "react-router";
+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 = () =>
+{
+ const [file, setFile] = useState(null);
+
+ const submit: React.MouseEventHandler = (event) =>
+ {
+ event.preventDefault();
+ console.log(file);
+ };
+
+ return
+
+
+
Thematic customisation
+
+
+
+
+
+
Dingus
+
+
+
+
;
+};
+
+const Admin = () =>
+{
+
+ return
+
+ } />
+ } />
+ } />
+ } />
+
+ ;
+};
+
export default Admin;
\ No newline at end of file
diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx
index 7cda0bf..80a4d7b 100644
--- a/src/pages/Home.tsx
+++ b/src/pages/Home.tsx
@@ -1,25 +1,27 @@
-import React from "react";
-import { Route, Routes } from "react-router";
-import '../css/pages/Home.css';
-import ErrorBoundary from "../util/ErrorBoundary";
-import Applications from "./home/Applications";
-import Profile from "./home/Profile";
-
-const Main = () => {
-
- return
- What to put here? hmmm
-
;
-};
-
-const Home = () => {
-
- return
-
- } />
- } />
- } />
-
- ;
-};
+import React from "react";
+import { Route, Routes } from "react-router";
+import '../css/pages/Home.css';
+import ErrorBoundary from "../util/ErrorBoundary";
+import Applications from "./home/Applications";
+import Profile from "./home/Profile";
+
+const Main = () =>
+{
+
+ return
+ What to put here? hmmm
+
;
+};
+
+const Home = () =>
+{
+
+ return
+
+ } />
+ } />
+ } />
+
+ ;
+};
export default Home;
\ No newline at end of file
diff --git a/src/pages/Login.tsx b/src/pages/Login.tsx
index f9aff1e..9c2b3d1 100644
--- a/src/pages/Login.tsx
+++ b/src/pages/Login.tsx
@@ -1,146 +1,159 @@
-import React, { useState, useRef } from "react";
-import { Route, Routes, useNavigate } from "react-router";
-import '../css/pages/Login.css';
-import logoImg from '../img/logo.png';
-import { useLoginContext } from "../structures/UserContext";
-import LoadingBar, {LoadingBarRef} from 'react-top-loading-bar';
-import { fetchUser, getSettings, post } from "../util/Util";
-import { OAuthProvider } from "../@types/Other";
-
-const CredentialFields = () => {
-
- const [, setUser] = useLoginContext();
- const [fail, setFail] = useState(false);
- const navigate = useNavigate();
- const ref = useRef(null);
- const usernameRef = useRef(null);
- const passwordRef = useRef(null);
-
- const loginMethods = getSettings()?.OAuthProviders || [];
-
- const loginClick: React.MouseEventHandler = async (event: React.MouseEvent) => {
- ref.current?.continuousStart();
- event.preventDefault();
- // const username = (document.getElementById('username') as HTMLInputElement)?.value;
- // const password = (document.getElementById('password') as HTMLInputElement)?.value;
- const username = usernameRef.current?.value;
- const password = passwordRef.current?.value;
- if (!username?.length || !password?.length) return;
-
- const result = await post('/api/login', { username, password });
-
- if (![200, 302].includes(result.status)) {
- ref.current?.complete();
- setFail(true);
- return;
- }
-
- if (!result.data?.twoFactor) {
- setUser(await fetchUser());
- ref.current?.complete();
- return navigate('/home');
- }
- ref.current?.complete();
- return navigate('verify');
-
- };
-
- return
-
-
-
-
-
-
-
Log in
- {fail ?
Invalid credentials
: null}
-
-
-
-
-
-
-
-
-
Alternate login methods
-
- {loginMethods.map((method: OAuthProvider) =>
{ window.location.pathname = `/api/login/${method.name}`; }}>
-
-
{method.name}
-
)}
-
-
-
;
-};
-
-const TwoFactor = () => {
-
- const [fail, setFail] = useState(false);
- const [, setUser] = useLoginContext();
- const navigate = useNavigate();
- const mfaCode = useRef(null);
- const ref = useRef(null);
-
- const twoFactorClick: React.MouseEventHandler = async (event) => {
- event.preventDefault();
- // const code = document.getElementById('2faCode').value;
- const code = mfaCode.current?.value;
- if (!code) return;
- ref.current?.continuousStart();
- const result = await post('/api/login/verify', { code });
- if (result.status === 200) {
- setUser(await fetchUser());
- ref.current?.complete();
- return navigate('/home');
- }
- ref.current?.complete();
- setFail(true);
- };
-
- return
-
-
-
-
-
-
-
Verification Code
- {fail ?
Invalid code
: null}
-
-
-
;
-};
-
-const Login = () => {
-
- return
-
-
-
- } />
- } />
-
-
-
-
;
-
-};
-
+import React, { useState, useRef } from "react";
+import { Route, Routes, useNavigate } from "react-router";
+import '../css/pages/Login.css';
+import logoImg from '../img/logo.png';
+import { useLoginContext } from "../structures/UserContext";
+import LoadingBar, {LoadingBarRef} from 'react-top-loading-bar';
+import { fetchUser, getSettings, post } from "../util/Util";
+import { OAuthProvider } from "../@types/Other";
+
+const CredentialFields = () =>
+{
+
+ const [, setUser] = useLoginContext();
+ const [fail, setFail] = useState(false);
+ const navigate = useNavigate();
+ const ref = useRef(null);
+ const usernameRef = useRef(null);
+ const passwordRef = useRef(null);
+
+ const loginMethods = getSettings()?.OAuthProviders || [];
+
+ const loginClick: React.MouseEventHandler = async (event: React.MouseEvent) =>
+ {
+ ref.current?.continuousStart();
+ event.preventDefault();
+ // const username = (document.getElementById('username') as HTMLInputElement)?.value;
+ // const password = (document.getElementById('password') as HTMLInputElement)?.value;
+ const username = usernameRef.current?.value;
+ const password = passwordRef.current?.value;
+ if (!username?.length || !password?.length)
+ return;
+
+ const result = await post('/api/login', { username, password });
+
+ if (![200, 302].includes(result.status))
+ {
+ ref.current?.complete();
+ setFail(true);
+ return;
+ }
+
+ if (!result.data?.twoFactor)
+ {
+ setUser(await fetchUser());
+ ref.current?.complete();
+ return navigate('/home');
+ }
+ ref.current?.complete();
+ return navigate('verify');
+
+ };
+
+ return
+
+
+
+
+
+
+
Log in
+ {fail ?
Invalid credentials
: null}
+
+
+
+
+
+
+
+
+
Alternate login methods
+
+ {loginMethods.map((method: OAuthProvider) =>
+ {
+ window.location.pathname = `/api/login/${method.name}`;
+ }}>
+
+
{method.name}
+
)}
+
+
+
;
+};
+
+const TwoFactor = () =>
+{
+
+ const [fail, setFail] = useState(false);
+ const [, setUser] = useLoginContext();
+ const navigate = useNavigate();
+ const mfaCode = useRef(null);
+ const ref = useRef(null);
+
+ const twoFactorClick: React.MouseEventHandler = async (event) =>
+ {
+ event.preventDefault();
+ // const code = document.getElementById('2faCode').value;
+ const code = mfaCode.current?.value;
+ if (!code)
+ return;
+ ref.current?.continuousStart();
+ const result = await post('/api/login/verify', { code });
+ if (result.status === 200)
+ {
+ setUser(await fetchUser());
+ ref.current?.complete();
+ return navigate('/home');
+ }
+ ref.current?.complete();
+ setFail(true);
+ };
+
+ return
+
+
+
+
+
+
+
Verification Code
+ {fail ?
Invalid code
: null}
+
+
+
;
+};
+
+const Login = () =>
+{
+
+ return
+
+
+
+ } />
+ } />
+
+
+
+
;
+
+};
+
export default Login;
\ No newline at end of file
diff --git a/src/pages/Register.tsx b/src/pages/Register.tsx
index 16e59e4..bb5e88f 100644
--- a/src/pages/Register.tsx
+++ b/src/pages/Register.tsx
@@ -1,66 +1,70 @@
-import React, { useState, useRef } from "react";
-import { useNavigate, useSearchParams } from "react-router-dom";
-import { post } from "../util/Util";
-import '../css/pages/Register.css';
-import logoImg from '../img/logo.png';
-import LoadingBar, {LoadingBarRef} from 'react-top-loading-bar';
-
-const Register = () => {
-
- const [error, setError] = useState();
- const [params] = useSearchParams();
- const navigate = useNavigate();
- const ref = useRef(null);
- const usernameRef = useRef(null);
- const passwordRef = useRef(null);
-
- document.body.classList.add('bg-triangles');
- const code = params.get('code');
-
- const submit: React.MouseEventHandler = async (event) => {
- ref.current?.continuousStart();
- event.preventDefault();
- const username = usernameRef.current?.value;
- const password = passwordRef.current?.value;
- if (!username?.length || !password?.length) {
- ref.current?.complete();
- return;
- }
- const response = await post('/api/register', { username, password, code });
- if (response.status !== 200) {
- ref.current?.complete();
- return setError(response.message as string || 'unknown error');
- }
- ref.current?.complete();
- navigate('/login');
- };
-
- return
-
-
-
-
-
-
-
-
-
Register
-
- {error &&
{error}
}
-
-
-
-
-
-
-
-
;
-};
-
+import React, { useState, useRef } from "react";
+import { useNavigate, useSearchParams } from "react-router-dom";
+import { post } from "../util/Util";
+import '../css/pages/Register.css';
+import logoImg from '../img/logo.png';
+import LoadingBar, {LoadingBarRef} from 'react-top-loading-bar';
+
+const Register = () =>
+{
+
+ const [error, setError] = useState();
+ const [params] = useSearchParams();
+ const navigate = useNavigate();
+ const ref = useRef(null);
+ const usernameRef = useRef(null);
+ const passwordRef = useRef(null);
+
+ document.body.classList.add('bg-triangles');
+ const code = params.get('code');
+
+ const submit: React.MouseEventHandler = async (event) =>
+ {
+ ref.current?.continuousStart();
+ event.preventDefault();
+ const username = usernameRef.current?.value;
+ const password = passwordRef.current?.value;
+ if (!username?.length || !password?.length)
+ {
+ ref.current?.complete();
+ return;
+ }
+ const response = await post('/api/register', { username, password, code });
+ if (response.status !== 200)
+ {
+ ref.current?.complete();
+ return setError(response.message as string || 'unknown error');
+ }
+ ref.current?.complete();
+ navigate('/login');
+ };
+
+ return
+
+
+
+
+
+
+
+
+
Register
+
+ {error &&
{error}
}
+
+
+
+
+
+
+
+
;
+};
+
export default Register;
\ No newline at end of file
diff --git a/src/pages/admin/Roles.tsx b/src/pages/admin/Roles.tsx
index c04f2e8..8e83e91 100644
--- a/src/pages/admin/Roles.tsx
+++ b/src/pages/admin/Roles.tsx
@@ -9,12 +9,14 @@ import { get, patch, post } from "../../util/Util";
import { Permissions as Perms, Role as R } from "../../@types/ApiStructures";
import { ToggleSwitch } from "../../components/InputElements";
-const Role = ({ role }: {role: R}) => {
+const Role = ({ role }: {role: R}) =>
+{
// const perms = { ...role.permissions };
const [perms, updatePerms] = useState(role.permissions);
- const commitPerms = async () => {
+ const commitPerms = async () =>
+ {
await patch(`/api/roles/${role.id}`, perms);
};
@@ -35,9 +37,9 @@ const Role = ({ role }: {role: R}) => {
Created: {new Date(role.createdTimestamp).toDateString()}
{/* Disabled: {role.disabled.toString()}
*/}
-
+
Disabled
-
+
@@ -57,21 +59,25 @@ const Role = ({ role }: {role: R}) => {
;
};
-const CreateRoleField = ({ numRoles, className, addRole }: { numRoles: number, className?: string, addRole: (role: R) => void }) => {
+const CreateRoleField = ({ numRoles, className, addRole }: { numRoles: number, className?: string, addRole: (role: R) => void }) =>
+{
const [error, setError] = useState(null);
const nameRef = useRef(null);
const positionRef = useRef(null);
- const createRole: React.MouseEventHandler = async (event) => {
+ const createRole: React.MouseEventHandler = async (event) =>
+ {
event.preventDefault();
- if (!nameRef.current || !positionRef.current) return setError('Must supply name and position');
+ 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');
+ if (!response.success)
+ return setError(response.message || 'Unknown error');
addRole(response.data as R);
};
@@ -94,7 +100,8 @@ const CreateRoleField = ({ numRoles, className, addRole }: { numRoles: number, c
};
-const Roles = () => {
+const Roles = () =>
+{
const [error, setError] = useState(null);
const [roles, setRoles] = useState([]);
@@ -102,41 +109,50 @@ const Roles = () => {
const [pages, setPages] = useState(1);
const [loading, setLoading] = useState(true);
- useEffect(() => {
- (async () => {
+ useEffect(() =>
+ {
+ (async () =>
+ {
const result = await get('/api/roles', { page });
- if (result.success && result.data) {
+ if (result.success && result.data)
+ {
setError(null);
setRoles(result.data.roles as R[]);
setPages(result.data.pages);
- } else {
+ }
+ else
+ {
setError(result.message || 'Unknown error');
}
setLoading(false);
})();
}, [page]);
- const RoleWrapper = () => {
+ const RoleWrapper = () =>
+ {
const { id } = useParams();
const role = roles.find(r => r.id === id);
- if(!role) return Unknown role
;
+ if(!role)
+ return Unknown role
;
return ;
};
const navigate = useNavigate();
- const RoleListWrapper = () => {
+ const RoleListWrapper = () =>
+ {
return
All Roles
{roles.map(role => {
- navigate(role.id);
- }}
- key={role.id}
- item={role}
- itemKeys={['name', 'id']}
- />)}
+ onClick={() =>
+ {
+ navigate(role.id);
+ }}
+ key={role.id}
+ item={role}
+ itemKeys={['name', 'id']}
+ />)}
diff --git a/src/pages/admin/Users.tsx b/src/pages/admin/Users.tsx
index 02358e9..c1020a3 100644
--- a/src/pages/admin/Users.tsx
+++ b/src/pages/admin/Users.tsx
@@ -1,321 +1,359 @@
-import React, { useContext, useEffect, useState } from "react";
-import { Route, Routes, useNavigate, useParams } from "react-router";
-import ErrorBoundary from "../../util/ErrorBoundary";
-import { get, post } from '../../util/Util';
-import '../../css/pages/Users.css';
-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/InputElements";
-
-type PartialUser = {
- apps: App[]
-}
-const SelectedUserContext = React.createContext<{
- user: PartialUser | null,
- updateUser:(user: PartialUser) => void
-}>({
- user: null,
- updateUser: () => { /** */ }
-});
-const Context = ({ children }: { children: React.ReactNode }) => {
- const [user, updateUser] = useState
(null);
- return
- {children}
- ;
-};
-
-const ApplicationList = ({ apps }: { apps: App[] }) => {
-
- const navigate = useNavigate();
- return
- {apps.map(app => {
- navigate(`applications/${app.id}`);
- }}
- key={app.id}
- item={app}
- itemKeys={['name', 'id']}
- />)}
-
;
-
-};
-
-const Application = ({ app }: { app: App }) => {
-
- const commitPerms = async () => {
- await post(`/api/applications/${app.id}/permissions`, perms);
- };
- const [perms, updatePerms] = useState(app.permissions);
- const descProps = { defaultValue: app.description || '', placeholder: 'Describe your application' };
- return
-
-
-
-
-
Application {app.name} ({app.id})
-
-
-
Created: {new Date(app.createdTimestamp).toDateString()}
-
- Disabled:
-
-
-
Description
-
-
-
-
-
-
-
;
-};
-
-const RoleComp = ({ role, onClick }: { role: Role, onClick: React.ReactEventHandler }) => {
- return
- {role.name} X
-
;
-};
-
-const Roles = ({ userRoles, roles }: { userRoles: Role[], roles: Role[] }) => {
-
- const [unequippedRoles, updateUnequipped] = useState(roles.filter(role => !userRoles.some(r => role.id === r.id)));
- const [equippedRoles, updateEquipped] = useState(userRoles);
-
- const roleSelected = (role: Role) => {
- updateEquipped([...equippedRoles, role]);
- updateUnequipped(unequippedRoles.filter(r => r.id !== role.id));
- };
- const roleDeselected = (role: Role) => {
- updateEquipped(equippedRoles.filter(r => r.id !== role.id));
- updateUnequipped([...unequippedRoles, role]);
- };
- return
-
-
- {equippedRoles.map(role => {
- event.preventDefault();
- roleDeselected(role);
- }} />)}
-
-
-
-
- {unequippedRoles.map(role => {
- roleSelected(role);
- }}
- >
- {role.name}
- )}
-
-
-
- ;
-};
-
-const User = ({ user, roles }: { user: APIUser, roles: Role[] }) => {
-
- // const [apps, updateApps] = useState([]);
- const [perms, updatePerms] = useState(user.permissions);
- const userContext = useContext(SelectedUserContext);
-
- useEffect(() => {
- (async () => {
- const appsResponse = await get(`/api/users/${user.id}/applications`);
- if (appsResponse.success) {
- const a = appsResponse.data as App[];
- // updateApps(a);
- userContext.updateUser({ apps: a });
+import React, { useContext, useEffect, useState } from "react";
+import { Route, Routes, useNavigate, useParams } from "react-router";
+import ErrorBoundary from "../../util/ErrorBoundary";
+import { get, post } from '../../util/Util';
+import '../../css/pages/Users.css';
+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/InputElements";
+
+type PartialUser = {
+ apps: App[]
+}
+const SelectedUserContext = React.createContext<{
+ user: PartialUser | null,
+ updateUser:(user: PartialUser) => void
+ }>({
+ user: null,
+ updateUser: () =>
+ { /** */ }
+ });
+const Context = ({ children }: { children: React.ReactNode }) =>
+{
+ const [user, updateUser] = useState(null);
+ return
+ {children}
+ ;
+};
+
+const ApplicationList = ({ apps }: { apps: App[] }) =>
+{
+
+ const navigate = useNavigate();
+ return
+ {apps.map(app =>
+ {
+ navigate(`applications/${app.id}`);
+ }}
+ key={app.id}
+ item={app}
+ itemKeys={['name', 'id']}
+ />)}
+
;
+
+};
+
+const Application = ({ app }: { app: App }) =>
+{
+
+ const commitPerms = async () =>
+ {
+ await post(`/api/applications/${app.id}/permissions`, perms);
+ };
+ const [perms, updatePerms] = useState(app.permissions);
+ const descProps = { defaultValue: app.description || '', placeholder: 'Describe your application' };
+ return
+
+
+
+
+
Application {app.name} ({app.id})
+
+
+
Created: {new Date(app.createdTimestamp).toDateString()}
+
+ Disabled:
+
+
+
Description
+
+
+
+
+
+
+
;
+};
+
+const RoleComp = ({ role, onClick }: { role: Role, onClick: React.ReactEventHandler }) =>
+{
+ return
+ {role.name} X
+
;
+};
+
+const Roles = ({ userRoles, roles }: { userRoles: Role[], roles: Role[] }) =>
+{
+
+ const [unequippedRoles, updateUnequipped] = useState(roles.filter(role => !userRoles.some(r => role.id === r.id)));
+ const [equippedRoles, updateEquipped] = useState(userRoles);
+
+ const roleSelected = (role: Role) =>
+ {
+ updateEquipped([...equippedRoles, role]);
+ updateUnequipped(unequippedRoles.filter(r => r.id !== role.id));
+ };
+ const roleDeselected = (role: Role) =>
+ {
+ updateEquipped(equippedRoles.filter(r => r.id !== role.id));
+ updateUnequipped([...unequippedRoles, role]);
+ };
+ return
+
+
+ {equippedRoles.map(role =>
+ {
+ event.preventDefault();
+ roleDeselected(role);
+ }} />)}
+
+
+
+
+ {unequippedRoles.map(role =>
+ {
+ roleSelected(role);
+ }}
+ >
+ {role.name}
+ )}
+
+
+
+ ;
+};
+
+const User = ({ user, roles }: { user: APIUser, roles: Role[] }) =>
+{
+
+ // const [apps, updateApps] = useState([]);
+ const [perms, updatePerms] = useState(user.permissions);
+ const userContext = useContext(SelectedUserContext);
+
+ useEffect(() =>
+ {
+ (async () =>
+ {
+ const appsResponse = await get(`/api/users/${user.id}/applications`);
+ if (appsResponse.success)
+ {
+ const a = appsResponse.data as App[];
+ // updateApps(a);
+ userContext.updateUser({ apps: a });
+ }
+ })();
+ }, []);
+
+ const commitPerms = async () =>
+ {
+ await post(`/api/users/${user.id}/permissions`, perms);
+ };
+
+ return
+
+
+
+
+
User {user.displayName} ({user.id})
+
+
+
Created: {new Date(user.createdTimestamp).toDateString()}
+
2FA: {(user.twoFactor && Disable) || 'Disabled'}
+
+ Login Disabled:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/*
+ Save User
+ */}
+
+
+
+
+
+
+
+
+
Applications
+ {userContext.user &&
}
+
+
+
+
;
+};
+
+const CreateUserField = ({ className }: { className: string }) =>
+{
+
+ const [link, setLink] = useState(null);
+ const getSignupCode = async () =>
+ {
+ const response = await get('/api/register/code');
+ if (response.status === 200 && response.data)
+ {
+ const link = `${window.location.origin}/register?code=${response.data.code}`;
+ setLink(link);
+ }
+ };
+
+ const copyToClipboard: React.MouseEventHandler = (event) =>
+ {
+ const element = event.target as HTMLElement;
+ const { parentElement } = element;
+ navigator.clipboard.writeText(element.innerText);
+ if (parentElement)
+ (parentElement.childNodes[parentElement.childNodes.length - 1] as HTMLElement).style.visibility = "visible";
+ };
+
+ const closeToolTip: React.MouseEventHandler = (event) =>
+ {
+ (event.target as HTMLElement).style.visibility = "hidden";
+ };
+
+ // TODO: Finish this
+ return
+
One-Click Sign-Up
+ {link ?
+
:
+
Create sign-up link}
+
+
Create User
+
+
;
+
+};
+
+const Users = () =>
+{
+
+ const [users, setUsers] = useState([]);
+ const [roles, updateRoles] = useState([]);
+ const [page, setPage] = useState(1);
+ const [pages, setPages] = useState(1);
+ const [error, setError] = useState(null);
+ const [loading, setLoading] = useState(true);
+
+ useEffect(() =>
+ {
+ (async () =>
+ {
+ const result = await get('/api/users', { page });
+ if (result.success && result.data)
+ {
+ setError(null);
+ setUsers(result.data.users as APIUser[]);
+ setPages(result.data.pages);
}
- })();
- }, []);
-
- const commitPerms = async () => {
- await post(`/api/users/${user.id}/permissions`, perms);
- };
-
- return
-
-
-
-
-
User {user.displayName} ({user.id})
-
-
-
Created: {new Date(user.createdTimestamp).toDateString()}
-
2FA: {(user.twoFactor && Disable) || 'Disabled'}
-
- Login Disabled:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {/*
- Save User
- */}
-
-
-
-
-
-
-
-
-
Applications
- {userContext.user &&
}
-
-
-
-
;
-};
-
-const CreateUserField = ({ className }: { className: string }) => {
-
- const [link, setLink] = useState(null);
- const getSignupCode = async () => {
- const response = await get('/api/register/code');
- if (response.status === 200 && response.data) {
- const link = `${window.location.origin}/register?code=${response.data.code}`;
- setLink(link);
- }
- };
-
- const copyToClipboard: React.MouseEventHandler = (event) => {
- const element = event.target as HTMLElement;
- const { parentElement } = element;
- navigator.clipboard.writeText(element.innerText);
- if (parentElement)
- (parentElement.childNodes[parentElement.childNodes.length - 1] as HTMLElement).style.visibility = "visible";
- };
-
- const closeToolTip: React.MouseEventHandler = (event) => {
- (event.target as HTMLElement).style.visibility = "hidden";
- };
-
- // TODO: Finish this
- return
-
One-Click Sign-Up
- {link ?
-
:
-
Create sign-up link}
-
-
Create User
-
-
;
-
-};
-
-const Users = () => {
-
- const [users, setUsers] = useState([]);
- const [roles, updateRoles] = useState([]);
- const [page, setPage] = useState(1);
- const [pages, setPages] = useState(1);
- const [error, setError] = useState(null);
- const [loading, setLoading] = useState(true);
-
- useEffect(() => {
- (async () => {
- const result = await get('/api/users', { page });
- if (result.success && result.data) {
- setError(null);
- setUsers(result.data.users as APIUser[]);
- setPages(result.data.pages);
- } else setError(result.message || 'Unknown error');
- setLoading(false);
- const rolesResponse = await get('/api/roles', { all: true });
- if (rolesResponse.success && rolesResponse.data)
- updateRoles(rolesResponse.data.roles as Role[]);
- })();
- }, [page]);
-
- const UserWrapper = () => {
- const { id } = useParams();
- const user = users.find(u => u.id === id);
- if (!user) return Unknown user
;
- return ;
- };
-
- const navigate = useNavigate();
- const UserListWrapper = () => {
- return
-
-
All Users
-
- {users.map(user => {
- navigate(user.id);
- }}
- key={user.id}
- item={user}
- itemKeys={['name', 'id']} />)}
-
-
-
-
-
-
-
-
;
- };
-
- const ApplicationWrapper = () => {
- const { appid } = useParams();
- if (!appid) return null;
-
- const userContext = useContext(SelectedUserContext);
- const [app, setApp] = useState(userContext.user?.apps.find(app => app.id === appid) || null);
- useEffect(() => {
- (async () => {
- if (userContext.user) return;
- const result = await get(`/api/applications/${appid}`);
- if (result.success)
- setApp(result.data as App);
- })();
- }, [appid]);
-
- if (!app) return Loading...
;
- return ;
- };
-
- return
- {error &&
{error}
}
-
-
-
- } />
- } />
- } />
-
-
-
-
;
-
-};
-
+ else
+ setError(result.message || 'Unknown error');
+ setLoading(false);
+ const rolesResponse = await get('/api/roles', { all: true });
+ if (rolesResponse.success && rolesResponse.data)
+ updateRoles(rolesResponse.data.roles as Role[]);
+ })();
+ }, [page]);
+
+ const UserWrapper = () =>
+ {
+ const { id } = useParams();
+ const user = users.find(u => u.id === id);
+ if (!user)
+ return Unknown user
;
+ return ;
+ };
+
+ const navigate = useNavigate();
+ const UserListWrapper = () =>
+ {
+ return
+
+
All Users
+
+ {users.map(user =>
+ {
+ navigate(user.id);
+ }}
+ key={user.id}
+ item={user}
+ itemKeys={['name', 'id']} />)}
+
+
+
+
+
+
+
+
;
+ };
+
+ const ApplicationWrapper = () =>
+ {
+ const { appid } = useParams();
+ if (!appid)
+ return null;
+
+ const userContext = useContext(SelectedUserContext);
+ const [app, setApp] = useState(userContext.user?.apps.find(app => app.id === appid) || null);
+ useEffect(() =>
+ {
+ (async () =>
+ {
+ if (userContext.user)
+ return;
+ const result = await get(`/api/applications/${appid}`);
+ if (result.success)
+ setApp(result.data as App);
+ })();
+ }, [appid]);
+
+ if (!app)
+ return Loading...
;
+ return ;
+ };
+
+ return
+ {error &&
{error}
}
+
+
+
+ } />
+ } />
+ } />
+
+
+
+
;
+
+};
+
export default Users;
\ No newline at end of file
diff --git a/src/pages/home/Applications.tsx b/src/pages/home/Applications.tsx
index 0e16080..0f34850 100644
--- a/src/pages/home/Applications.tsx
+++ b/src/pages/home/Applications.tsx
@@ -1,108 +1,122 @@
-import React, { useEffect, useRef, useState } from "react";
-import { Route, Routes, useNavigate, useParams } from "react-router";
-import { Table, TableListEntry } from "../../components/Table";
-import ErrorBoundary from "../../util/ErrorBoundary";
-import { post, get, del } from "../../util/Util";
-import { Application as App } from "../../@types/ApiStructures";
-import { Res } from "../../@types/Other";
-
-const Application = ({ app }: {app: App}) => {
-
- const navigate = useNavigate();
-
- const deleteApp = async () => {
- // const response =
- await del(`/api/user/applications/${app.id}`);
- };
-
- return
-
navigate(-1)}>Back
-
{app.name}
-
{app.description}
-
{app.token}
-
Delete
-
;
-
-};
-
-const Applications = () => {
-
- const [applications, setApplications] = useState([]);
- const [loading, setLoading] = useState(true);
- const navigate = useNavigate();
-
- useEffect(() => {
- (async () => {
- const response = await get('/api/user/applications') as Res;
- if (response.status === 200) setApplications(response.data as App[]);
- setLoading(false);
- })();
- }, []);
-
- const descField = useRef(null);
- const nameField = useRef(null);
- const [error, setError] = useState(null);
-
- const createApp: React.MouseEventHandler = async (event) => {
- event.preventDefault();
- const button = event.target as HTMLButtonElement;
-
- if (!nameField.current || !descField.current) return;
-
- const name = nameField.current?.value;
- if (!name) return setError('Missing name');
-
- const description = descField.current?.value || null;
- nameField.current.value = '';
- descField.current.value = '';
- button.disabled = true;
- const response = await post('/api/user/applications', { name, description });
- if (response.status !== 200) setError(response.message || 'Unknown error');
- else setApplications((apps) => [...apps, response.data as App]);
-
- button.disabled = false;
- };
-
- const Main = () => {
- return
-
-
-
Applications
-
- {applications.map(application => navigate(application.id)}
- item={application}
- itemKeys={['name', 'id']}
- />)}
-
-
-
-
-
Create Application
- {error &&
{error}
}
-
-
-
;
- };
-
- const ApplicationWrapper = () => {
- const { id } = useParams();
- const application = applications.find(app => app.id === id);
- if(!application) return Unknown application
;
- return ;
- };
-
- return
-
- } />
- } />
-
- ;
-};
-
+import React, { useEffect, useRef, useState } from "react";
+import { Route, Routes, useNavigate, useParams } from "react-router";
+import { Table, TableListEntry } from "../../components/Table";
+import ErrorBoundary from "../../util/ErrorBoundary";
+import { post, get, del } from "../../util/Util";
+import { Application as App } from "../../@types/ApiStructures";
+import { Res } from "../../@types/Other";
+
+const Application = ({ app }: {app: App}) =>
+{
+
+ const navigate = useNavigate();
+
+ const deleteApp = async () =>
+ {
+ // const response =
+ await del(`/api/user/applications/${app.id}`);
+ };
+
+ return
+
navigate(-1)}>Back
+
{app.name}
+
{app.description}
+
{app.token}
+
Delete
+
;
+
+};
+
+const Applications = () =>
+{
+
+ const [applications, setApplications] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const navigate = useNavigate();
+
+ useEffect(() =>
+ {
+ (async () =>
+ {
+ const response = await get('/api/user/applications') as Res;
+ if (response.status === 200)
+ setApplications(response.data as App[]);
+ setLoading(false);
+ })();
+ }, []);
+
+ const descField = useRef(null);
+ const nameField = useRef(null);
+ const [error, setError] = useState(null);
+
+ const createApp: React.MouseEventHandler = async (event) =>
+ {
+ event.preventDefault();
+ const button = event.target as HTMLButtonElement;
+
+ if (!nameField.current || !descField.current)
+ return;
+
+ const name = nameField.current?.value;
+ if (!name)
+ return setError('Missing name');
+
+ const description = descField.current?.value || null;
+ nameField.current.value = '';
+ descField.current.value = '';
+ button.disabled = true;
+ const response = await post('/api/user/applications', { name, description });
+ if (response.status !== 200)
+ setError(response.message || 'Unknown error');
+ else
+ setApplications((apps) => [...apps, response.data as App]);
+
+ button.disabled = false;
+ };
+
+ const Main = () =>
+ {
+ return
+
+
+
Applications
+
+ {applications.map(application => navigate(application.id)}
+ item={application}
+ itemKeys={['name', 'id']}
+ />)}
+
+
+
+
+
Create Application
+ {error &&
{error}
}
+
+
+
;
+ };
+
+ const ApplicationWrapper = () =>
+ {
+ const { id } = useParams();
+ const application = applications.find(app => app.id === id);
+ if(!application)
+ return Unknown application
;
+ return ;
+ };
+
+ return
+
+ } />
+ } />
+
+ ;
+};
+
export default Applications;
\ No newline at end of file
diff --git a/src/pages/home/Profile.tsx b/src/pages/home/Profile.tsx
index 6740228..2f94054 100644
--- a/src/pages/home/Profile.tsx
+++ b/src/pages/home/Profile.tsx
@@ -1,189 +1,210 @@
-import React, { useRef, useState } from "react";
-import { capitalise, get, post } from "../../util/Util";
-import { useLoginContext } from "../../structures/UserContext";
-import { FileSelector } from "../../components/InputElements";
-import { ExternalProfile as EP, User } from "../../@types/ApiStructures";
-
-const TwoFactorControls = ({ user }: {user: User}) => {
-
- const [twoFactorError, set2FAError] = useState(null);
- const [displayInput, setDisplayInput] = useState(false);
-
- const [qr, setQr] = useState(null);
- const displayQr = async () => {
- const response = await get('/api/user/2fa');
- if (response.status !== 200) return set2FAError(response.message || 'Uknown error');
- setQr(response.message as string);
- };
-
- const codeRef = useRef(null);
- const disable2FA: React.MouseEventHandler = async (event) => {
- event.preventDefault();
- const code = codeRef.current?.value;
- if (!code) return;
- const response = await post('/api/user/2fa/disable', { code });
- if (response.status !== 200) return set2FAError(response.message || 'Unknown error');
- };
-
- const submitCode: React.MouseEventHandler = async (event) => {
- event.preventDefault();
- const code = codeRef.current?.value;
- if (!code) return;
- const response = await post('/api/user/2fa/verify', { code });
- if (response.status !== 200) return set2FAError(response.message || 'Unknown error');
- };
-
- let inner =
- {qr ?
-
-
-
-
:
-
Enable 2FA}
-
;
-
-
- if (user.twoFactor) inner =
- {displayInput ?
-
- : setDisplayInput(true)} className="button error">Disable 2FA}
- ;
-
- return
- {twoFactorError &&
{twoFactorError}
}
- {inner}
-
;
-
-};
-
-const ExternalProfile = ({ profile }: {profile: EP}) => {
- return
-
{capitalise(profile.provider)}
-
Username: {profile.username}
-
ID: {profile.id}
-
;
-};
-
-const ThirdPartyConnections = ({ user }: {user: User}) => {
-
- const { externalProfiles } = user;
-
- return
- {externalProfiles.map(profile => )}
-
;
-
-};
-
-const Profile = () => {
-
- const user = useLoginContext()[0] as User;
- const [file, setFile] = useState(null);
- const [error, setError] = useState(null);
-
- const usernameRef = useRef(null);
- const displayNameRef = useRef(null);
- const currPassRef = useRef(null);
- const newPassRef = useRef(null);
- const newPassRepeatRef = useRef(null);
-
- const submit: React.MouseEventHandler = async (event) => {
- if (!file) return;
- const button = event.target as HTMLButtonElement;
- button.disabled = true;
-
- const body = new FormData();
- body.append('file', file, file.name);
-
- const response = await post('/api/user/avatar', body, {
- headers: null
- });
- if (!response.success) setError(response.message || 'Unknown error');
- else setFile(null);
- button.disabled = false;
- };
-
- const updateSettings: React.MouseEventHandler = async (event) => {
- event.preventDefault();
- const button = event.target as HTMLButtonElement;
-
- const username = usernameRef.current?.value;
- const displayName = displayNameRef.current?.value;
- const currentPassword = currPassRef.current?.value;
- const newPassword = newPassRef.current?.value;
- const newPasswordRepeat = newPassRepeatRef.current?.value;
-
- if (!currentPassword) return setError('Missing password');
-
- button.disabled = true;
- const data: {username?: string, displayName?: string, password?: string, newPassword?: string} = { username, displayName, password: currentPassword };
- if (currentPassword && newPassword && newPasswordRepeat) {
- if (newPassword !== newPasswordRepeat) {
- button.disabled = false;
- return setError('Passwords do not match');
- }
- data.newPassword = newPassword;
- }
- const response = await post('/api/user/settings', data);
- console.log(response);
- button.disabled = false;
-
- };
-
- return
-
-
-
Profile
-
-
-
Profile Picture
-
-
-
-
Change Profile Picture
-
-
-
-
Third party connections
-
-
-
-
-
-
;
-};
-
+import React, { useRef, useState } from "react";
+import { capitalise, get, post } from "../../util/Util";
+import { useLoginContext } from "../../structures/UserContext";
+import { FileSelector } from "../../components/InputElements";
+import { ExternalProfile as EP, User } from "../../@types/ApiStructures";
+
+const TwoFactorControls = ({ user }: {user: User}) =>
+{
+
+ const [twoFactorError, set2FAError] = useState(null);
+ const [displayInput, setDisplayInput] = useState(false);
+
+ const [qr, setQr] = useState(null);
+ const displayQr = async () =>
+ {
+ const response = await get('/api/user/2fa');
+ if (response.status !== 200)
+ return set2FAError(response.message || 'Uknown error');
+ setQr(response.message as string);
+ };
+
+ const codeRef = useRef(null);
+ const disable2FA: React.MouseEventHandler = async (event) =>
+ {
+ event.preventDefault();
+ const code = codeRef.current?.value;
+ if (!code)
+ return;
+ const response = await post('/api/user/2fa/disable', { code });
+ if (response.status !== 200)
+ return set2FAError(response.message || 'Unknown error');
+ };
+
+ const submitCode: React.MouseEventHandler = async (event) =>
+ {
+ event.preventDefault();
+ const code = codeRef.current?.value;
+ if (!code)
+ return;
+ const response = await post('/api/user/2fa/verify', { code });
+ if (response.status !== 200)
+ return set2FAError(response.message || 'Unknown error');
+ };
+
+ let inner =
+ {qr ?
+
+
+
+
:
+
Enable 2FA}
+
;
+
+
+ if (user.twoFactor)
+ inner =
+ {displayInput ?
+
+ : setDisplayInput(true)} className="button error">Disable 2FA}
+ ;
+
+ return
+ {twoFactorError &&
{twoFactorError}
}
+ {inner}
+
;
+
+};
+
+const ExternalProfile = ({ profile }: {profile: EP}) =>
+{
+ return
+
{capitalise(profile.provider)}
+
Username: {profile.username}
+
ID: {profile.id}
+
;
+};
+
+const ThirdPartyConnections = ({ user }: {user: User}) =>
+{
+
+ const { externalProfiles } = user;
+
+ return
+ {externalProfiles.map(profile => )}
+
;
+
+};
+
+const Profile = () =>
+{
+
+ const user = useLoginContext()[0] as User;
+ const [file, setFile] = useState(null);
+ const [error, setError] = useState(null);
+
+ const usernameRef = useRef(null);
+ const displayNameRef = useRef(null);
+ const currPassRef = useRef(null);
+ const newPassRef = useRef(null);
+ const newPassRepeatRef = useRef(null);
+
+ const submit: React.MouseEventHandler = async (event) =>
+ {
+ if (!file)
+ return;
+ const button = event.target as HTMLButtonElement;
+ button.disabled = true;
+
+ const body = new FormData();
+ body.append('file', file, file.name);
+
+ const response = await post('/api/user/avatar', body, {
+ headers: null
+ });
+ if (!response.success)
+ setError(response.message || 'Unknown error');
+ else
+ setFile(null);
+ button.disabled = false;
+ };
+
+ const updateSettings: React.MouseEventHandler = async (event) =>
+ {
+ event.preventDefault();
+ const button = event.target as HTMLButtonElement;
+
+ const username = usernameRef.current?.value;
+ const displayName = displayNameRef.current?.value;
+ const currentPassword = currPassRef.current?.value;
+ const newPassword = newPassRef.current?.value;
+ const newPasswordRepeat = newPassRepeatRef.current?.value;
+
+ if (!currentPassword)
+ return setError('Missing password');
+
+ button.disabled = true;
+ const data: {username?: string, displayName?: string, password?: string, newPassword?: string} = { username, displayName, password: currentPassword };
+ if (currentPassword && newPassword && newPasswordRepeat)
+ {
+ if (newPassword !== newPasswordRepeat)
+ {
+ button.disabled = false;
+ return setError('Passwords do not match');
+ }
+ data.newPassword = newPassword;
+ }
+ const response = await post('/api/user/settings', data);
+ console.log(response);
+ button.disabled = false;
+
+ };
+
+ return
+
+
+
Profile
+
+
+
Profile Picture
+
+
+
+
Change Profile Picture
+
+
+
+
Third party connections
+
+
+
+
+
+
;
+};
+
export default Profile;
\ No newline at end of file
diff --git a/src/structures/NavLink.tsx b/src/structures/NavLink.tsx
index 1979d92..82ad526 100644
--- a/src/structures/NavLink.tsx
+++ b/src/structures/NavLink.tsx
@@ -1,29 +1,30 @@
-import React from 'react';
-import { NavLink as BaseNavLink } from 'react-router-dom';
-
-type NavLinkOptions = {
- activeClassName?: string,
- activeStyle?: object,
- children?: React.ReactNode,
- className?: string,
- style?: object,
- to: string,
- onClick?: React.MouseEventHandler
-}
-
-// eslint-disable-next-line react/display-name
-const NavLink = React.forwardRef(({ activeClassName, activeStyle, ...props }: NavLinkOptions, ref: React.ForwardedRef) => {
- return [props.className, isActive ? activeClassName : null].filter(Boolean).join(' ')}
- style={({ isActive }) => ({
- ...props.style,
- ...isActive ? activeStyle : null
- })}>
- {props?.children}
- ;
-
-});
-
+import React from 'react';
+import { NavLink as BaseNavLink } from 'react-router-dom';
+
+type NavLinkOptions = {
+ activeClassName?: string,
+ activeStyle?: object,
+ children?: React.ReactNode,
+ className?: string,
+ style?: object,
+ to: string,
+ onClick?: React.MouseEventHandler
+}
+
+// eslint-disable-next-line react/display-name
+const NavLink = React.forwardRef(({ activeClassName, activeStyle, ...props }: NavLinkOptions, ref: React.ForwardedRef) =>
+{
+ return [props.className, isActive ? activeClassName : null].filter(Boolean).join(' ')}
+ style={({ isActive }) => ({
+ ...props.style,
+ ...isActive ? activeStyle : null
+ })}>
+ {props?.children}
+ ;
+
+});
+
export default NavLink;
\ No newline at end of file
diff --git a/src/structures/PrivateRoute.tsx b/src/structures/PrivateRoute.tsx
index 7ff2c27..91cd675 100644
--- a/src/structures/PrivateRoute.tsx
+++ b/src/structures/PrivateRoute.tsx
@@ -1,10 +1,12 @@
-import React from 'react';
-import { Navigate, useLocation } from "react-router-dom";
-import { getUser } from "../util/Util";
-
-export const PrivateRoute = ({ children }: {children: JSX.Element}) => {
- const user = getUser();
- const location = useLocation();
- if (!user) return ;
- return children;
+import React from 'react';
+import { Navigate, useLocation } from "react-router-dom";
+import { getUser } from "../util/Util";
+
+export const PrivateRoute = ({ children }: {children: JSX.Element}) =>
+{
+ const user = getUser();
+ const location = useLocation();
+ if (!user)
+ return ;
+ return children;
};
\ No newline at end of file
diff --git a/src/structures/UnauthedRoute.tsx b/src/structures/UnauthedRoute.tsx
index d15d477..35a3682 100644
--- a/src/structures/UnauthedRoute.tsx
+++ b/src/structures/UnauthedRoute.tsx
@@ -1,9 +1,11 @@
-import React from 'react';
-import { Navigate } from "react-router-dom";
-import { getUser } from "../util/Util";
-
-export const UnauthedRoute = ({ children }: {children: JSX.Element}) => {
- const user = getUser();
- if (user) return ;
- return children;
+import React from 'react';
+import { Navigate } from "react-router-dom";
+import { getUser } from "../util/Util";
+
+export const UnauthedRoute = ({ children }: {children: JSX.Element}) =>
+{
+ const user = getUser();
+ if (user)
+ return ;
+ return children;
};
\ No newline at end of file
diff --git a/src/structures/UserContext.tsx b/src/structures/UserContext.tsx
index eb28975..de9fa71 100644
--- a/src/structures/UserContext.tsx
+++ b/src/structures/UserContext.tsx
@@ -1,31 +1,35 @@
-import React, { useContext, useState } from 'react';
-import { getUser } from '../util/Util';
-import {User} from '../@types/ApiStructures';
-
-type UpdateFunc = ((user?: User | null) => void)
-
-const LoginContext = React.createContext(null);
-const LoginUpdateContext = React.createContext(() => { /* */ });
-
-// Hook
-export const useLoginContext = (): [User | null, UpdateFunc] => {
- return [useContext(LoginContext), useContext(LoginUpdateContext)];
-};
-
-// Component
-export const UserContext = ({ children }: {children: React.ReactNode}) => {
-
- const [user, setLoginState] = useState(getUser());
- const updateLoginState = () => {
- setLoginState(getUser());
- };
-
- return (
-
-
- {children}
-
-
- );
-
+import React, { useContext, useState } from 'react';
+import { getUser } from '../util/Util';
+import {User} from '../@types/ApiStructures';
+
+type UpdateFunc = ((user?: User | null) => void)
+
+const LoginContext = React.createContext(null);
+const LoginUpdateContext = React.createContext(() =>
+{ /* */ });
+
+// Hook
+export const useLoginContext = (): [User | null, UpdateFunc] =>
+{
+ return [useContext(LoginContext), useContext(LoginUpdateContext)];
+};
+
+// Component
+export const UserContext = ({ children }: {children: React.ReactNode}) =>
+{
+
+ const [user, setLoginState] = useState(getUser());
+ const updateLoginState = () =>
+ {
+ setLoginState(getUser());
+ };
+
+ return (
+
+
+ {children}
+
+
+ );
+
};
\ No newline at end of file
diff --git a/src/util/ClickDetector.tsx b/src/util/ClickDetector.tsx
index 2dc088b..5f983c9 100644
--- a/src/util/ClickDetector.tsx
+++ b/src/util/ClickDetector.tsx
@@ -1,41 +1,47 @@
-import React, { useEffect, useRef } from "react";
-
-// Listens for mouse clicks outside of the wrapped element
-const alerter = (ref: React.RefObject, callback: () => void) => {
- useEffect(() => {
-
- const listener = (event: MouseEvent) => {
- if (ref.current && !ref.current.contains(event.target as Node)) {
- return callback();
- }
- };
-
- document.addEventListener('mousedown', listener);
-
- return () => {
- document.removeEventListener('mousedown', listener);
- };
-
- }, [ref]);
-};
-
-/**
- * Component wrapper to enable listening for clicks outside of the given component
- *
- * @param {{children: React.ReactNode, callback: () => void}} { children, callback }
- * @return {*}
- */
-const ClickDetector = ({ children, callback }: {children: React.ReactNode, callback: () => void}) => {
-
- const wrapperRef = useRef(null);
- alerter(wrapperRef, callback);
-
- return (
-
- {children}
-
- );
-
-};
-
+import React, { useEffect, useRef } from "react";
+
+// Listens for mouse clicks outside of the wrapped element
+const alerter = (ref: React.RefObject, callback: () => void) =>
+{
+ useEffect(() =>
+ {
+
+ const listener = (event: MouseEvent) =>
+ {
+ if (ref.current && !ref.current.contains(event.target as Node))
+ {
+ return callback();
+ }
+ };
+
+ document.addEventListener('mousedown', listener);
+
+ return () =>
+ {
+ document.removeEventListener('mousedown', listener);
+ };
+
+ }, [ref]);
+};
+
+/**
+ * Component wrapper to enable listening for clicks outside of the given component
+ *
+ * @param {{children: React.ReactNode, callback: () => void}} { children, callback }
+ * @return {*}
+ */
+const ClickDetector = ({ children, callback }: {children: React.ReactNode, callback: () => void}) =>
+{
+
+ const wrapperRef = useRef(null);
+ alerter(wrapperRef, callback);
+
+ return (
+
+ {children}
+
+ );
+
+};
+
export default ClickDetector;
\ No newline at end of file
diff --git a/src/util/ErrorBoundary.tsx b/src/util/ErrorBoundary.tsx
index 72b02fe..af94d2b 100644
--- a/src/util/ErrorBoundary.tsx
+++ b/src/util/ErrorBoundary.tsx
@@ -1,34 +1,39 @@
-import React from "react";
-
-type BoundaryProps = {
- [key: string]: unknown,
- fallback?: React.ReactNode,
- children: React.ReactNode
-}
-type StateProps = {
- error: boolean
-}
-
-class ErrorBoundary extends React.Component {
-
- fallback?: React.ReactNode;
-
- constructor(props: BoundaryProps) {
- super(props);
- this.state = { error: false };
- this.fallback = props.fallback;
- }
-
- override componentDidCatch(error: Error, errorInfo: unknown) {
- this.setState({error: true});
- console.error(error, errorInfo);
- }
-
- override render() {
- if (this.state.error) return this.fallback || Something went wrong :/
;
- return this.props.children;
- }
-
-}
-
+import React from "react";
+
+type BoundaryProps = {
+ [key: string]: unknown,
+ fallback?: React.ReactNode,
+ children: React.ReactNode
+}
+type StateProps = {
+ error: boolean
+}
+
+class ErrorBoundary extends React.Component
+{
+
+ fallback?: React.ReactNode;
+
+ constructor(props: BoundaryProps)
+ {
+ super(props);
+ this.state = { error: false };
+ this.fallback = props.fallback;
+ }
+
+ override componentDidCatch(error: Error, errorInfo: unknown)
+ {
+ this.setState({error: true});
+ console.error(error, errorInfo);
+ }
+
+ override render()
+ {
+ if (this.state.error)
+ return this.fallback || Something went wrong :/
;
+ return this.props.children;
+ }
+
+}
+
export default ErrorBoundary;
\ No newline at end of file
diff --git a/src/views/PermissionsView.tsx b/src/views/PermissionsView.tsx
index 5363343..91be8ea 100644
--- a/src/views/PermissionsView.tsx
+++ b/src/views/PermissionsView.tsx
@@ -1,11 +1,14 @@
import React, { useRef } from "react";
import { Permissions as Perms } from "../@types/ApiStructures";
-export const Permission = ({ name, value, chain, updatePerms }: {name: string, value: number | Perms, chain: string, updatePerms: (chain: string, perm: string) => void}) => {
+export const Permission = ({ name, value, chain, updatePerms }: {name: string, value: number | Perms, chain: string, updatePerms: (chain: string, perm: string) => void}) =>
+{
const inputRef = useRef(null);
- const onChange = () => {
+ const onChange = () =>
+ {
const val = inputRef.current?.value || null;
- if (val === null) return;
+ if (val === null)
+ return;
updatePerms(chain, val);
};
@@ -15,13 +18,17 @@ export const Permission = ({ name, value, chain, updatePerms }: {name: string, v
;
};
-export const PermissionGroup = ({ name, value, chain, updatePerms }: {name: string, value: number | Perms, chain: string, updatePerms: (chain: string, perm: string) => void}) => {
+export const PermissionGroup = ({ name, value, chain, updatePerms }: {name: string, value: number | Perms, chain: string, updatePerms: (chain: string, perm: string) => void}) =>
+{
const elements = [];
- for (const [perm, val] of Object.entries(value)) {
+ for (const [perm, val] of Object.entries(value))
+ {
const props = { key: perm, name: perm, value: val, updatePerms, chain: `${chain}:${perm}` };
- if(typeof val ==='object') elements.push();
- else elements.push();
+ if(typeof val ==='object')
+ elements.push();
+ else
+ elements.push();
}
return
@@ -33,39 +40,48 @@ export const PermissionGroup = ({ name, value, chain, updatePerms }: {name: stri
;
};
-export const Permissions = ({ perms, onUpdate }: { perms: Perms, onUpdate: (perms: Perms) => void }) => {
+export const Permissions = ({ perms, onUpdate }: { perms: Perms, onUpdate: (perms: Perms) => void }) =>
+{
- const updatePerms = (chain: string, raw: string) => {
+ const updatePerms = (chain: string, raw: string) =>
+ {
const value = parseInt(raw);
- if (isNaN(value)) return;
+ 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;
+ 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 keys = Object.keys(perms);
- for (const perm of keys) {
+ for (const perm of keys)
+ {
const props = { key: perm, name: perm, value: perms[perm], chain: perm, updatePerms };
let Elem = null;
- switch (typeof perms[perm]) {
- case 'number':
- Elem = Permission;
- break;
- case 'object':
- Elem = PermissionGroup;
- break;
- default:
- // eslint-disable-next-line react/display-name
- Elem = () => {
- return
Uknown permission structure
;
- };
- break;
+ switch (typeof perms[perm])
+ {
+ case 'number':
+ Elem = Permission;
+ break;
+ case 'object':
+ Elem = PermissionGroup;
+ break;
+ default:
+ // eslint-disable-next-line react/display-name
+ Elem = () =>
+ {
+ return
Uknown permission structure
;
+ };
+ break;
}
elements.push(
);
}
diff --git a/src/views/RateLimitsView.tsx b/src/views/RateLimitsView.tsx
index 05fe840..dd49aa5 100644
--- a/src/views/RateLimitsView.tsx
+++ b/src/views/RateLimitsView.tsx
@@ -1,38 +1,41 @@
-import React, {Fragment} from "react";
-import { RateLimit as RL, RateLimits as RLs } from "../@types/ApiStructures";
-
-const RateLimit = ({route, limit}: {route: string, limit: RL}) => {
-
- return
- {route}
-
-
-
-
-
-
-
-
- ;
-};
-
-export const RateLimits = ({ rateLimits }: {rateLimits: RLs}) => {
-
- const routes = Object.keys(rateLimits);
- const elements = routes.map((route, index) => {
- return
-
-
;
- });
-
- return
-
Rate Limits
-
- {elements}
-
-
-
- {JSON.stringify(rateLimits, null, 4)}
-
-
;
+import React, {Fragment} from "react";
+import { RateLimit as RL, RateLimits as RLs } from "../@types/ApiStructures";
+
+const RateLimit = ({route, limit}: {route: string, limit: RL}) =>
+{
+
+ return
+ {route}
+
+
+
+
+
+
+
+
+ ;
+};
+
+export const RateLimits = ({ rateLimits }: {rateLimits: RLs}) =>
+{
+
+ const routes = Object.keys(rateLimits);
+ const elements = routes.map((route, index) =>
+ {
+ return
+
+
;
+ });
+
+ return
+
Rate Limits
+
+ {elements}
+
+
+
+ {JSON.stringify(rateLimits, null, 4)}
+
+
;
};
\ No newline at end of file