Typings + some refactoring
Refactored applications page WIP Type params to http method functions
This commit is contained in:
parent
a908013b6d
commit
a2ccd0c00e
@ -56,7 +56,8 @@ export type Application = {
|
|||||||
|
|
||||||
export type Role = {
|
export type Role = {
|
||||||
position: number,
|
position: number,
|
||||||
rateLimits: RateLimits
|
rateLimits: RateLimits,
|
||||||
|
tag: boolean,
|
||||||
} & APIEntity
|
} & APIEntity
|
||||||
|
|
||||||
export type FlagType = string | number | boolean | number[] | null
|
export type FlagType = string | number | boolean | number[] | null
|
||||||
|
@ -6,10 +6,10 @@ export type RequestOptions = {
|
|||||||
headers?: { [key: string]: string },
|
headers?: { [key: string]: string },
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Res = {
|
export type Res<T> = {
|
||||||
success: boolean,
|
success: boolean,
|
||||||
status: number,
|
status: number,
|
||||||
data?: { [key: string]: any },
|
data?: T, //{ [key: string]: any },
|
||||||
message?: string
|
message?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ const App = () =>
|
|||||||
{
|
{
|
||||||
(async () =>
|
(async () =>
|
||||||
{
|
{
|
||||||
const result = await get('/api/user');
|
const result = await get<{twoFactor: boolean}>('/api/user');
|
||||||
if (result.status === 200)
|
if (result.status === 200)
|
||||||
{
|
{
|
||||||
setSession(result.data as User);
|
setSession(result.data as User);
|
||||||
|
@ -83,7 +83,7 @@ export const DataCard = ({ id, className, children = [] }: DataCardProps) =>
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type PaginatedCardUrlProps = {
|
type PaginatedCardUrlProps<T> = {
|
||||||
path: string,
|
path: string,
|
||||||
query?: {[key: string | number]: unknown}
|
query?: {[key: string | number]: unknown}
|
||||||
dataKey?: string,
|
dataKey?: string,
|
||||||
@ -91,14 +91,14 @@ type PaginatedCardUrlProps = {
|
|||||||
tableDivClass?: string,
|
tableDivClass?: string,
|
||||||
children?: React.ReactNode | React.ReactNode[],
|
children?: React.ReactNode | React.ReactNode[],
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
formatter: (entry: any, onClick?: (...args: any[]) => void, idx?: number) => JSX.Element
|
formatter: (entry: T, callback?: (...args: any[]) => void, idx?: number) => JSX.Element
|
||||||
// FormatterCallback
|
// FormatterCallback
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
callback?: (...args: any[]) => void,
|
callback?: (...args: any[]) => void,
|
||||||
pageSize?: number,
|
pageSize?: number,
|
||||||
refresh?: boolean
|
refresh?: boolean
|
||||||
}
|
}
|
||||||
export const PaginatedCard = ({
|
export const PaginatedCard = <T, >({
|
||||||
// id = randomString(),
|
// id = randomString(),
|
||||||
className,
|
className,
|
||||||
tableDivClass,
|
tableDivClass,
|
||||||
@ -108,14 +108,14 @@ export const PaginatedCard = ({
|
|||||||
formatter,
|
formatter,
|
||||||
callback,
|
callback,
|
||||||
pageSize = 10,
|
pageSize = 10,
|
||||||
query = {},
|
query,
|
||||||
refresh
|
refresh
|
||||||
}: PaginatedCardUrlProps) =>
|
}: PaginatedCardUrlProps<T>) =>
|
||||||
{
|
{
|
||||||
if (!(children instanceof Array))
|
if (!(children instanceof Array))
|
||||||
children = [ children ];
|
children = [ children ];
|
||||||
|
|
||||||
const [ data, setData ] = useState<unknown[] | null>(null);
|
const [ data, setData ] = useState<T[] | null>(null);
|
||||||
const [ page, setPage ] = useState(1);
|
const [ page, setPage ] = useState(1);
|
||||||
const [ pages, setPages ] = useState(1);
|
const [ pages, setPages ] = useState(1);
|
||||||
const [ loadState, setLoadState ] = useState(RefresherState.loading);
|
const [ loadState, setLoadState ] = useState(RefresherState.loading);
|
||||||
@ -131,7 +131,7 @@ export const PaginatedCard = ({
|
|||||||
setRefresh(val => !val);
|
setRefresh(val => !val);
|
||||||
};
|
};
|
||||||
const deps: (string | number | object | boolean)[] = [ page, path, refreshLocal ];
|
const deps: (string | number | object | boolean)[] = [ page, path, refreshLocal ];
|
||||||
if(query && Object.keys(query).length)
|
if(query)
|
||||||
deps.push(query);
|
deps.push(query);
|
||||||
if (typeof refresh !== 'undefined')
|
if (typeof refresh !== 'undefined')
|
||||||
deps.push(refresh);
|
deps.push(refresh);
|
||||||
@ -141,19 +141,28 @@ export const PaginatedCard = ({
|
|||||||
(async () =>
|
(async () =>
|
||||||
{
|
{
|
||||||
setLoadState(RefresherState.loading);
|
setLoadState(RefresherState.loading);
|
||||||
|
if(query)
|
||||||
|
{
|
||||||
const keys = Object.keys(query);
|
const keys = Object.keys(query);
|
||||||
for (const key of keys)
|
for (const key of keys)
|
||||||
if (typeof query[key] === 'undefined')
|
if (typeof query[key] === 'undefined')
|
||||||
delete query[key];
|
delete query[key];
|
||||||
const response = await get(path, { page, pageSize, ...query });
|
}
|
||||||
|
const response = await get<{ [key: string]: number | T[], pages: number }>(path, { page, pageSize, ...query });
|
||||||
|
// if (!response.data?.bruh)
|
||||||
|
// throw new Error('bruh');
|
||||||
if (!response.success || !response.data)
|
if (!response.success || !response.data)
|
||||||
{
|
{
|
||||||
setLoadState(RefresherState.error);
|
setLoadState(RefresherState.error);
|
||||||
return errorToast(response.message);
|
return errorToast(response.message);
|
||||||
}
|
}
|
||||||
// return setError(response.message ?? null);
|
// return setError(response.message ?? null);
|
||||||
setData(response.data[dataKey]);
|
setData(response.data[dataKey] as T[]);
|
||||||
setPages(response.data.pages ?? 1);
|
setPages(response.data.pages ?? 1);
|
||||||
|
if(response.data.pages && page > response.data.pages)
|
||||||
|
setPage(response.data.pages == 0 ? 1 : response.data.pages);
|
||||||
|
else if(response.data.pages == 0)
|
||||||
|
setPage(1);
|
||||||
setLoadState(RefresherState.success);
|
setLoadState(RefresherState.success);
|
||||||
})();
|
})();
|
||||||
}, deps);
|
}, deps);
|
||||||
@ -184,7 +193,9 @@ type BasePaginatedCardProps = {
|
|||||||
onRefresh?: () => void, // Callback that is fired whenever the refresher is clicked
|
onRefresh?: () => void, // Callback that is fired whenever the refresher is clicked
|
||||||
// onlineList?: string
|
// onlineList?: string
|
||||||
}
|
}
|
||||||
export const BasePaginatedCard = ({ children, data, className, id, tableDivClass, formatter, callback, page, pages, setPage, loadState, onRefresh }: BasePaginatedCardProps) =>
|
export const BasePaginatedCard = ({ children, data,
|
||||||
|
className, id, tableDivClass, formatter, callback,
|
||||||
|
page, pages, setPage, loadState, onRefresh }: BasePaginatedCardProps) =>
|
||||||
{
|
{
|
||||||
if (!(children instanceof Array))
|
if (!(children instanceof Array))
|
||||||
children = [ children ];
|
children = [ children ];
|
||||||
@ -235,7 +246,7 @@ export const TableCard = ({ id, children, className = '', tableDivClass = '', da
|
|||||||
{/* <p>{'Loading...'}</p> */}
|
{/* <p>{'Loading...'}</p> */}
|
||||||
</DataCard>;
|
</DataCard>;
|
||||||
|
|
||||||
return <DataCard id={id} className={className}>
|
return <DataCard id={id} className={`${className} content-nm content-bm-2`}>
|
||||||
{cardHeader}
|
{cardHeader}
|
||||||
<div className={tableDivClass}>
|
<div className={tableDivClass}>
|
||||||
<table className='striped table-pd'>
|
<table className='striped table-pd'>
|
||||||
|
@ -31,7 +31,7 @@ const CredentialFields = () =>
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
button.disabled = true;
|
button.disabled = true;
|
||||||
const result = await post('/api/login', { username, password });
|
const result = await post<{twoFactor: boolean}>('/api/login', { username, password });
|
||||||
button.disabled = false;
|
button.disabled = false;
|
||||||
if (![ 200, 302 ].includes(result.status))
|
if (![ 200, 302 ].includes(result.status))
|
||||||
{
|
{
|
||||||
|
@ -321,7 +321,7 @@ const Main = () =>
|
|||||||
if (listView)
|
if (listView)
|
||||||
query.all = true;
|
query.all = true;
|
||||||
|
|
||||||
const response = await get('/api/flags', query);
|
const response = await get<{pages: number, flags: APIFlag[], tags: string[]}>('/api/flags', query);
|
||||||
if (!response.success || !response.data)
|
if (!response.success || !response.data)
|
||||||
{
|
{
|
||||||
setFlags([]);
|
setFlags([]);
|
||||||
@ -329,7 +329,7 @@ const Main = () =>
|
|||||||
return errorToast(response.message);
|
return errorToast(response.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
setFlags(response.data.flags as APIFlag[]);
|
setFlags(response.data.flags);
|
||||||
setPages(response.data.pages);
|
setPages(response.data.pages);
|
||||||
if (availableFilters === null)
|
if (availableFilters === null)
|
||||||
setAvailableFilters(response.data.tags);
|
setAvailableFilters(response.data.tags);
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import React, { useContext, useEffect, useRef, useState } from 'react';
|
import React, { useContext, useEffect, useRef, useState } from 'react';
|
||||||
import { Route, Routes, useNavigate, useParams } from 'react-router';
|
import { Route, Routes, useNavigate, useParams } from 'react-router';
|
||||||
import ErrorBoundary from '../../util/ErrorBoundary';
|
import ErrorBoundary from '../../util/ErrorBoundary';
|
||||||
import { Permissions as PermissionsStruct, Role as RoleStruct, User } from '../../@types/ApiStructures';
|
import { Role as RoleStruct, User } from '../../@types/ApiStructures';
|
||||||
import { BasePaginatedCard, DataCard, PaginatedCard, RefresherState } from '../../components/PageElements';
|
import { BasePaginatedCard, DataCard, PaginatedCard, RefresherState } from '../../components/PageElements';
|
||||||
import { dateString, del, errorToast, get, patch, post, successToast } from '../../util/Util';
|
import { dateString, del, errorToast, get, patch, post, successToast } from '../../util/Util';
|
||||||
import { BackButton } from '../../components/PageControls';
|
import { BackButton } from '../../components/PageControls';
|
||||||
import { ClickToEdit } from '../../components/InputElements';
|
import { ClickToEdit, ToggleSwitch } from '../../components/InputElements';
|
||||||
import { Permissions } from '../../views/PermissionsView';
|
import { Permissions } from '../../views/PermissionsView';
|
||||||
import { useSubtitle } from '../../components/TitledPage';
|
import { useSubtitle } from '../../components/TitledPage';
|
||||||
|
|
||||||
@ -58,13 +58,13 @@ const Role = ({ refreshList }: RoleProps) =>
|
|||||||
<p>Loading...</p>
|
<p>Loading...</p>
|
||||||
</div>;
|
</div>;
|
||||||
|
|
||||||
const updatePerms = (perms: PermissionsStruct) =>
|
const updateRoleStruct = (data: Partial<RoleStruct>) =>
|
||||||
{
|
{
|
||||||
setRole(role =>
|
setRole(role =>
|
||||||
{
|
{
|
||||||
if (!role)
|
if (!role)
|
||||||
return role;
|
return role;
|
||||||
return { ...role, permissions: perms };
|
return { ...role, ...data };
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -79,6 +79,14 @@ const Role = ({ refreshList }: RoleProps) =>
|
|||||||
successToast('Permissions updated');
|
successToast('Permissions updated');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const updateRole = async ({ tag }: Partial<RoleStruct>) =>
|
||||||
|
{
|
||||||
|
const response = await patch<RoleStruct>(`/api/roles/${roleId}`, { tag });
|
||||||
|
if (!response.success || !response.data)
|
||||||
|
return errorToast(response.message);
|
||||||
|
updateRoleStruct(response.data);
|
||||||
|
};
|
||||||
|
|
||||||
const deleteRole = async (e: React.MouseEvent) =>
|
const deleteRole = async (e: React.MouseEvent) =>
|
||||||
{
|
{
|
||||||
const button = e.target as HTMLButtonElement;
|
const button = e.target as HTMLButtonElement;
|
||||||
@ -136,7 +144,8 @@ const Role = ({ refreshList }: RoleProps) =>
|
|||||||
<td>{dateString(role.createdTimestamp)}</td>
|
<td>{dateString(role.createdTimestamp)}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
<th>Tag</th>
|
||||||
|
<td><ToggleSwitch defaultValue={role.tag} onChange={({ target }) => updateRole({ tag: target.checked })} /></td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@ -160,7 +169,7 @@ const Role = ({ refreshList }: RoleProps) =>
|
|||||||
<div>Permissions</div>
|
<div>Permissions</div>
|
||||||
<div>
|
<div>
|
||||||
<div className='scrollable h-max-80-vh'>
|
<div className='scrollable h-max-80-vh'>
|
||||||
<Permissions perms={role.permissions} onUpdate={updatePerms} />
|
<Permissions perms={role.permissions} onUpdate={(permissions) => updateRoleStruct({ permissions })} />
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr />
|
||||||
<div className='ml-4'>
|
<div className='ml-4'>
|
||||||
@ -289,14 +298,14 @@ const Main = () =>
|
|||||||
(async () =>
|
(async () =>
|
||||||
{
|
{
|
||||||
setLoadState(RefresherState.loading);
|
setLoadState(RefresherState.loading);
|
||||||
const response = await get('/api/roles', { page, pageSize: 15, ...filters });
|
const response = await get<{pages: number, roles: RoleStruct[]}>('/api/roles', { page, pageSize: 15, ...filters });
|
||||||
if (!response.success || !response.data)
|
if (!response.success || !response.data)
|
||||||
{
|
{
|
||||||
setLoadState(RefresherState.error);
|
setLoadState(RefresherState.error);
|
||||||
return errorToast(response.message);
|
return errorToast(response.message);
|
||||||
}
|
}
|
||||||
setPages(response.data.pages);
|
setPages(response.data.pages);
|
||||||
setRoles(new Map(response.data.roles.map((role: RoleStruct) => [ role.id, role ])));
|
setRoles(new Map(response.data.roles.map((role) => [ role.id, role ])));
|
||||||
setLoadState(RefresherState.success);
|
setLoadState(RefresherState.success);
|
||||||
})();
|
})();
|
||||||
}, [ page, filters, refresh ]);
|
}, [ page, filters, refresh ]);
|
||||||
|
@ -204,7 +204,7 @@ const UserData = ({ user, updateUser }: {user: UserStruct | null, updateUser: (u
|
|||||||
{
|
{
|
||||||
const button = e.target as HTMLButtonElement;
|
const button = e.target as HTMLButtonElement;
|
||||||
button.disabled = true;
|
button.disabled = true;
|
||||||
const response = await get(`/api/users/${user.id}/passwdreset`);
|
const response = await get<{token: string}>(`/api/users/${user.id}/passwdreset`);
|
||||||
button.disabled = false;
|
button.disabled = false;
|
||||||
if (!response.success || !response.data)
|
if (!response.success || !response.data)
|
||||||
return errorToast(response.message);
|
return errorToast(response.message);
|
||||||
@ -418,7 +418,7 @@ const UserList = ({ page, pages, setPage, loadState, onRefresh }: UserListProps)
|
|||||||
|
|
||||||
const getSignupCode = async () =>
|
const getSignupCode = async () =>
|
||||||
{
|
{
|
||||||
const response = await get('/api/register/code');
|
const response = await get<{code: string}>('/api/register/code');
|
||||||
if (!response.success || !response.data)
|
if (!response.success || !response.data)
|
||||||
return errorToast(response.message);
|
return errorToast(response.message);
|
||||||
const link = `${window.location.origin}/register?code=${response.data.code}`;
|
const link = `${window.location.origin}/register?code=${response.data.code}`;
|
||||||
@ -533,14 +533,14 @@ const Main = () =>
|
|||||||
(async () =>
|
(async () =>
|
||||||
{
|
{
|
||||||
setLoadState(RefresherState.loading);
|
setLoadState(RefresherState.loading);
|
||||||
const response = await get('/api/users', { page, pageSize: 15, ...searchFilter });
|
const response = await get<{pages: number, users: UserStruct[]}>('/api/users', { page, pageSize: 15, ...searchFilter });
|
||||||
if (!response.success || !response.data)
|
if (!response.success || !response.data)
|
||||||
{
|
{
|
||||||
setLoadState(RefresherState.error);
|
setLoadState(RefresherState.error);
|
||||||
return errorToast(response.message);
|
return errorToast(response.message);
|
||||||
}
|
}
|
||||||
setPages(response.data.pages);
|
setPages(response.data.pages);
|
||||||
setUsers(new Map(response.data.users.map((user: UserStruct) => [ user.id, user ])));
|
setUsers(new Map(response.data.users.map((user) => [ user.id, user ])));
|
||||||
setLoadState(RefresherState.success);
|
setLoadState(RefresherState.success);
|
||||||
})();
|
})();
|
||||||
}, [ page, searchFilter, refresh ]);
|
}, [ page, searchFilter, refresh ]);
|
||||||
@ -549,10 +549,10 @@ const Main = () =>
|
|||||||
{
|
{
|
||||||
(async () =>
|
(async () =>
|
||||||
{
|
{
|
||||||
const response = await get('/api/roles', { all: true });
|
const response = await get<{roles: RoleStruct[]}>('/api/roles', { all: true });
|
||||||
if (!response.success || !response.data)
|
if (!response.success || !response.data)
|
||||||
return errorToast(response.message);
|
return errorToast(response.message);
|
||||||
setRoles(new Map(response.data.roles.map((role: RoleStruct) => [ role.id, role ])));
|
setRoles(new Map(response.data.roles.map((role) => [ role.id, role ])));
|
||||||
})();
|
})();
|
||||||
}, [ refresh ]);
|
}, [ refresh ]);
|
||||||
|
|
||||||
|
@ -1,20 +1,36 @@
|
|||||||
import React, { useEffect, useRef, 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 { Table, TableListEntry } from '../../components/Table';
|
|
||||||
import ErrorBoundary from '../../util/ErrorBoundary';
|
import ErrorBoundary from '../../util/ErrorBoundary';
|
||||||
import { post, get, del, successToast, patch, errorToast } from '../../util/Util';
|
import { post, del, successToast, patch, errorToast, get } from '../../util/Util';
|
||||||
import { Application as App } from '../../@types/ApiStructures';
|
import { Application as App, Permissions as Perms } from '../../@types/ApiStructures';
|
||||||
import { Res } from '../../@types/Other';
|
import { DataCard, PaginatedCard } from '../../components/PageElements';
|
||||||
import { DataCard } from '../../components/PageElements';
|
|
||||||
import { BackButton } from '../../components/PageControls';
|
import { BackButton } from '../../components/PageControls';
|
||||||
import { ClickToEdit } from '../../components/InputElements';
|
import { ClickToEdit } from '../../components/InputElements';
|
||||||
import { Permissions } from '../../views/PermissionsView';
|
import { Permissions } from '../../views/PermissionsView';
|
||||||
import { useSubtitle } from '../../components/TitledPage';
|
import { useSubtitle } from '../../components/TitledPage';
|
||||||
|
|
||||||
const Application = ({ app }: {app: App}) =>
|
const Application = () =>
|
||||||
{
|
{
|
||||||
const [ perms, updatePerms ] = useState(app.permissions);
|
const { id } = useParams();
|
||||||
|
const [ app, setApp ] = useState<App | null>(null);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
(async () =>
|
||||||
|
{
|
||||||
|
const response = await get<App>(`/api/users/applications/${id}`);
|
||||||
|
if (!response.success || !response.data)
|
||||||
|
return errorToast(response.message);
|
||||||
|
setApp(response.data);
|
||||||
|
});
|
||||||
|
}, [ id ]);
|
||||||
|
|
||||||
|
if (!app)
|
||||||
|
return <div>
|
||||||
|
<BackButton />
|
||||||
|
<p>Loading...</p>
|
||||||
|
</div>;
|
||||||
|
|
||||||
const deleteApp = async () =>
|
const deleteApp = async () =>
|
||||||
{
|
{
|
||||||
@ -33,7 +49,7 @@ const Application = ({ app }: {app: App}) =>
|
|||||||
const button = e.target as HTMLButtonElement;
|
const button = e.target as HTMLButtonElement;
|
||||||
button.disabled = true;
|
button.disabled = true;
|
||||||
const response = await patch(`/api/user/applications/${app.id}`, {
|
const response = await patch(`/api/user/applications/${app.id}`, {
|
||||||
permissions: perms
|
permissions: app.permissions
|
||||||
});
|
});
|
||||||
button.disabled = false;
|
button.disabled = false;
|
||||||
if (!response.success)
|
if (!response.success)
|
||||||
@ -41,18 +57,28 @@ const Application = ({ app }: {app: App}) =>
|
|||||||
return successToast('Successfully updated permissions');
|
return successToast('Successfully updated permissions');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const updatePerms = (permissions: Perms) =>
|
||||||
|
{
|
||||||
|
setApp(app =>
|
||||||
|
{
|
||||||
|
if (!app)
|
||||||
|
return app;
|
||||||
|
return { ...app, permissions };
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const resetPerms = async (e: React.MouseEvent) =>
|
const resetPerms = async (e: React.MouseEvent) =>
|
||||||
{
|
{
|
||||||
const button = e.target as HTMLButtonElement;
|
const button = e.target as HTMLButtonElement;
|
||||||
button.disabled = true;
|
button.disabled = true;
|
||||||
const response = await patch(`/api/user/applications/${app.id}`, {
|
const response = await patch<App>(`/api/user/applications/${app.id}`, {
|
||||||
permissions: null
|
permissions: null
|
||||||
});
|
});
|
||||||
button.disabled = false;
|
button.disabled = false;
|
||||||
if (!response.success)
|
if (!response.success || !response.data)
|
||||||
return errorToast(response.message);
|
return errorToast(response.message);
|
||||||
successToast('Successfully reset permissions');
|
successToast('Successfully reset permissions');
|
||||||
updatePerms(response.data?.permissions);
|
updatePerms(response.data.permissions);
|
||||||
};
|
};
|
||||||
|
|
||||||
return <div>
|
return <div>
|
||||||
@ -95,7 +121,7 @@ const Application = ({ app }: {app: App}) =>
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className='scrollable h-max-80-vh'>
|
<div className='scrollable h-max-80-vh'>
|
||||||
<Permissions perms={perms} onUpdate={updatePerms} />
|
<Permissions perms={app.permissions} onUpdate={updatePerms} />
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr />
|
||||||
<div className='ml-4'>
|
<div className='ml-4'>
|
||||||
@ -109,23 +135,22 @@ const Application = ({ app }: {app: App}) =>
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const applicationFormatter = (app: App, callback?: (id: string) => void) =>
|
||||||
|
{
|
||||||
|
return <tr key={app.id} onClick={() => callback && callback(app.id)}>
|
||||||
|
<td>{app.name}</td>
|
||||||
|
<td>{app.id}</td>
|
||||||
|
</tr>;
|
||||||
|
};
|
||||||
|
|
||||||
const Applications = () =>
|
const Applications = () =>
|
||||||
{
|
{
|
||||||
const subtitle = useSubtitle();
|
const subtitle = useSubtitle();
|
||||||
const [ applications, setApplications ] = useState<App[]>([]);
|
|
||||||
const [ loading, setLoading ] = useState(true);
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
subtitle.set('Applications');
|
subtitle.set('Applications');
|
||||||
(async () =>
|
|
||||||
{
|
|
||||||
const response = await get('/api/user/applications') as Res;
|
|
||||||
if (response.status === 200)
|
|
||||||
setApplications(response.data as App[]);
|
|
||||||
setLoading(false);
|
|
||||||
})();
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const descField = useRef<HTMLTextAreaElement>(null);
|
const descField = useRef<HTMLTextAreaElement>(null);
|
||||||
@ -151,8 +176,6 @@ const Applications = () =>
|
|||||||
const response = await post('/api/user/applications', { name, description });
|
const response = await post('/api/user/applications', { name, description });
|
||||||
if (response.status !== 200)
|
if (response.status !== 200)
|
||||||
errorToast(response.message);
|
errorToast(response.message);
|
||||||
else
|
|
||||||
setApplications((apps) => [ ...apps, response.data as App ]);
|
|
||||||
|
|
||||||
button.disabled = false;
|
button.disabled = false;
|
||||||
};
|
};
|
||||||
@ -161,7 +184,26 @@ const Applications = () =>
|
|||||||
{
|
{
|
||||||
return <div className="row">
|
return <div className="row">
|
||||||
|
|
||||||
<div className={`col ld ${loading && 'loading'}`}>
|
<PaginatedCard
|
||||||
|
path='/api/user/applications'
|
||||||
|
formatter={applicationFormatter}
|
||||||
|
callback={(id) =>
|
||||||
|
{
|
||||||
|
navigate(id);
|
||||||
|
}}
|
||||||
|
query={{}}
|
||||||
|
dataKey='applications'
|
||||||
|
className='col'
|
||||||
|
tableDivClass='scrollable-table'
|
||||||
|
>
|
||||||
|
Applications
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>ID</th>
|
||||||
|
</tr>
|
||||||
|
</PaginatedCard>
|
||||||
|
|
||||||
|
{/* <div className={`col ld ${loading && 'loading'}`}>
|
||||||
<h2>Applications</h2>
|
<h2>Applications</h2>
|
||||||
<Table headerItems={[ 'Name', 'ID' ]}>
|
<Table headerItems={[ 'Name', 'ID' ]}>
|
||||||
{applications.map(application => <TableListEntry
|
{applications.map(application => <TableListEntry
|
||||||
@ -171,7 +213,7 @@ const Applications = () =>
|
|||||||
itemKeys={[ 'name', 'id' ]}
|
itemKeys={[ 'name', 'id' ]}
|
||||||
/>)}
|
/>)}
|
||||||
</Table>
|
</Table>
|
||||||
</div>
|
</div> */}
|
||||||
|
|
||||||
<div className="col">
|
<div className="col">
|
||||||
<h2>Create Application</h2>
|
<h2>Create Application</h2>
|
||||||
@ -184,19 +226,19 @@ const Applications = () =>
|
|||||||
</div>;
|
</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ApplicationWrapper = () =>
|
// const ApplicationWrapper = () =>
|
||||||
{
|
// {
|
||||||
const { id } = useParams();
|
// const { id } = useParams();
|
||||||
const application = applications.find(app => app.id === id);
|
// const application = applications.find(app => app.id === id);
|
||||||
if(!application)
|
// if(!application)
|
||||||
return <p>Unknown application</p>;
|
// return <p>Unknown application</p>;
|
||||||
return <Application app={application} />;
|
// return <Application app={application} />;
|
||||||
};
|
// };
|
||||||
|
|
||||||
return <ErrorBoundary>
|
return <ErrorBoundary>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" element={<Main />} />
|
<Route path="/" element={<Main />} />
|
||||||
<Route path="/:id" element={<ApplicationWrapper />} />
|
<Route path="/:id" element={<Application />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</ErrorBoundary>;
|
</ErrorBoundary>;
|
||||||
};
|
};
|
||||||
|
@ -155,7 +155,7 @@ const Profile = () =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
button.disabled = true;
|
button.disabled = true;
|
||||||
const response = await post('/api/user/settings', data);
|
const response = await post<{ reAuth: boolean }>('/api/user/settings', data);
|
||||||
button.disabled = false;
|
button.disabled = false;
|
||||||
if (response.data?.reAuth)
|
if (response.data?.reAuth)
|
||||||
{
|
{
|
||||||
|
@ -184,7 +184,7 @@ export const setSetting = async (name: string, value: unknown) =>
|
|||||||
await post('/api/settings', settings.user);
|
await post('/api/settings', settings.user);
|
||||||
};
|
};
|
||||||
|
|
||||||
const parseResponse = async (response: Response): Promise<Res> =>
|
const parseResponse = async <T = unknown>(response: Response): Promise<Res<T>> =>
|
||||||
{
|
{
|
||||||
const { headers: rawHeaders, status } = response;
|
const { headers: rawHeaders, status } = response;
|
||||||
// Fetch returns heders as an interable for some reason
|
// Fetch returns heders as an interable for some reason
|
||||||
@ -214,7 +214,7 @@ const parseResponse = async (response: Response): Promise<Res> =>
|
|||||||
return { ...base, message: (await response.text() || response.statusText) };
|
return { ...base, message: (await response.text() || response.statusText) };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const post = async (url: string, body?: object | string, opts: ReqOptions = {}) =>
|
export const post = async <T>(url: string, body?: object | string, opts: ReqOptions = {}) =>
|
||||||
{
|
{
|
||||||
const options: HttpOpts & RequestOptions = {
|
const options: HttpOpts & RequestOptions = {
|
||||||
method: 'POST'
|
method: 'POST'
|
||||||
@ -235,10 +235,10 @@ export const post = async (url: string, body?: object | string, opts: ReqOptions
|
|||||||
|
|
||||||
// console.log(url, options);
|
// console.log(url, options);
|
||||||
const response = await fetch(url, options);
|
const response = await fetch(url, options);
|
||||||
return parseResponse(response);
|
return parseResponse<T>(response);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const patch = async (url: string, body: object | string, opts: RequestOptions = {}) =>
|
export const patch = async <T>(url: string, body: object | string, opts: RequestOptions = {}) =>
|
||||||
{
|
{
|
||||||
const options: HttpOpts & RequestOptions = {
|
const options: HttpOpts & RequestOptions = {
|
||||||
method: 'PATCH'
|
method: 'PATCH'
|
||||||
@ -259,18 +259,18 @@ export const patch = async (url: string, body: object | string, opts: RequestOpt
|
|||||||
|
|
||||||
// console.log(url, options);
|
// console.log(url, options);
|
||||||
const response = await fetch(url, options);
|
const response = await fetch(url, options);
|
||||||
return parseResponse(response);
|
return parseResponse<T>(response);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const get = async (url: string, params?: {[key: string]: boolean | string | number | string[] | number[]}) =>
|
export const get = async <T = unknown>(url: string, params?: {[key: string]: boolean | string | number | string[] | number[]}) =>
|
||||||
{
|
{
|
||||||
if (params)
|
if (params)
|
||||||
url += '?' + Object.entries(params)
|
url += '?' + Object.entries(params)
|
||||||
.map(([ key, val ]) => `${key}=${val instanceof Array ? val.join(',') : val}`)
|
.map(([ key, val ]) => val !== null && val !== '' && typeof val !== 'undefined' ? `${key}=${val instanceof Array ? val.join(',') : val}` : key)
|
||||||
.join('&');
|
.join('&');
|
||||||
const response = await fetch(url);
|
const response = await fetch(url);
|
||||||
return parseResponse(response);
|
return parseResponse<T>(response);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const del = async (url: string, body?: object | string, opts: RequestOptions = {}) =>
|
export const del = async (url: string, body?: object | string, opts: RequestOptions = {}) =>
|
||||||
|
Loading…
Reference in New Issue
Block a user