Add table to admin/UsersContainer.tsx
This commit is contained in:
parent
0ddf806100
commit
95c55e7d28
9 changed files with 223 additions and 22 deletions
39
resources/scripts/api/admin/users/getUsers.ts
Normal file
39
resources/scripts/api/admin/users/getUsers.ts
Normal file
|
@ -0,0 +1,39 @@
|
|||
import http, { getPaginationSet, PaginatedResult } from '@/api/http';
|
||||
import { rawDataToUser } from '@/api/transformers';
|
||||
import { createContext, useContext } from 'react';
|
||||
import useSWR from 'swr';
|
||||
|
||||
export interface User {
|
||||
id: number;
|
||||
externalId: string;
|
||||
uuid: string;
|
||||
username: string;
|
||||
email: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
language: string;
|
||||
rootAdmin: boolean;
|
||||
tfa: boolean;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
interface ctx {
|
||||
page: number;
|
||||
setPage: (value: number | ((s: number) => number)) => void;
|
||||
}
|
||||
|
||||
export const Context = createContext<ctx>({ page: 1, setPage: () => 1 });
|
||||
|
||||
export default () => {
|
||||
const { page } = useContext(Context);
|
||||
|
||||
return useSWR<PaginatedResult<User>>([ 'users', page ], async () => {
|
||||
const { data } = await http.get('/api/application/users', { params: { page } });
|
||||
|
||||
return ({
|
||||
items: (data.data || []).map(rawDataToUser),
|
||||
pagination: getPaginationSet(data.meta.pagination),
|
||||
});
|
||||
});
|
||||
};
|
|
@ -1,6 +1,7 @@
|
|||
import { Egg } from '@/api/admin/nests/eggs/getEggs';
|
||||
import { Nest } from '@/api/admin/nests/getNests';
|
||||
import { Role } from '@/api/admin/roles/getRoles';
|
||||
import { User } from '@/api/admin/users/getUsers';
|
||||
import { Allocation } from '@/api/server/getServer';
|
||||
import { FractalResponseData } from '@/api/http';
|
||||
import { FileObject } from '@/api/server/files/loadDirectory';
|
||||
|
@ -117,3 +118,18 @@ export const rawDataToEgg = ({ attributes }: FractalResponseData): Egg => ({
|
|||
createdAt: new Date(attributes.created_at),
|
||||
updatedAt: new Date(attributes.updated_at),
|
||||
});
|
||||
|
||||
export const rawDataToUser = ({ attributes }: FractalResponseData): User => ({
|
||||
id: attributes.id,
|
||||
externalId: attributes.external_id,
|
||||
uuid: attributes.uuid,
|
||||
username: attributes.username,
|
||||
email: attributes.email,
|
||||
firstName: attributes.first_name,
|
||||
lastName: attributes.last_name,
|
||||
language: attributes.language,
|
||||
rootAdmin: attributes.root_admin,
|
||||
tfa: attributes['2fa'],
|
||||
createdAt: new Date(attributes.created_at),
|
||||
updatedAt: new Date(attributes.updated_at),
|
||||
});
|
||||
|
|
|
@ -102,7 +102,7 @@ const NestsContainer = () => {
|
|||
|
||||
<td css={tw`px-6 text-sm text-neutral-200 text-left whitespace-nowrap`}>{nest.id}</td>
|
||||
<td css={tw`px-6 text-sm text-neutral-200 text-left whitespace-nowrap`}>
|
||||
<NavLink to={`${match.url}/${nest.id}`}>
|
||||
<NavLink to={`${match.url}/${nest.id}`} css={tw`text-primary-400 hover:text-primary-300`}>
|
||||
{nest.name}
|
||||
</NavLink>
|
||||
</td>
|
||||
|
|
|
@ -103,7 +103,7 @@ export default () => {
|
|||
|
||||
<td css={tw`px-6 text-sm text-neutral-200 text-left whitespace-nowrap`}>{role.id}</td>
|
||||
<td css={tw`px-6 text-sm text-neutral-200 text-left whitespace-nowrap`}>
|
||||
<NavLink to={`${match.url}/${role.id}`}>
|
||||
<NavLink to={`${match.url}/${role.id}`} css={tw`text-primary-400 hover:text-primary-300`}>
|
||||
{role.name}
|
||||
</NavLink>
|
||||
</td>
|
||||
|
|
|
@ -1,12 +1,67 @@
|
|||
import AdminCheckbox from '@/components/admin/AdminCheckbox';
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import getUsers, { Context as UsersContext } from '@/api/admin/users/getUsers';
|
||||
import AdminTable, { ContentWrapper, Loading, NoItems, Pagination, TableBody, TableHead, TableHeader, TableRow } from '@/components/admin/AdminTable';
|
||||
import Button from '@/components/elements/Button';
|
||||
import React from 'react';
|
||||
import FlashMessageRender from '@/components/FlashMessageRender';
|
||||
import useFlash from '@/plugins/useFlash';
|
||||
import { AdminContext } from '@/state/admin';
|
||||
import { NavLink, useRouteMatch } from 'react-router-dom';
|
||||
import tw from 'twin.macro';
|
||||
import AdminContentBlock from '@/components/admin/AdminContentBlock';
|
||||
|
||||
export default () => {
|
||||
const RowCheckbox = ({ id }: { id: number}) => {
|
||||
const isChecked = AdminContext.useStoreState(state => state.nests.selectedNests.indexOf(id) >= 0);
|
||||
const appendSelectedUser = AdminContext.useStoreActions(actions => actions.nests.appendSelectedNest);
|
||||
const removeSelectedUser = AdminContext.useStoreActions(actions => actions.nests.removeSelectedNest);
|
||||
|
||||
return (
|
||||
<AdminCheckbox
|
||||
name={id.toString()}
|
||||
checked={isChecked}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (e.currentTarget.checked) {
|
||||
appendSelectedUser(id);
|
||||
} else {
|
||||
removeSelectedUser(id);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const UsersContainer = () => {
|
||||
const match = useRouteMatch();
|
||||
|
||||
const { page, setPage } = useContext(UsersContext);
|
||||
const { clearFlashes, clearAndAddHttpError } = useFlash();
|
||||
const { data: users, error, isValidating } = getUsers();
|
||||
|
||||
useEffect(() => {
|
||||
if (!error) {
|
||||
clearFlashes('users');
|
||||
return;
|
||||
}
|
||||
|
||||
clearAndAddHttpError({ error, key: 'users' });
|
||||
}, [ error ]);
|
||||
|
||||
const length = users?.items?.length || 0;
|
||||
|
||||
const setSelectedUsers = AdminContext.useStoreActions(actions => actions.nests.setSelectedNests);
|
||||
const selectedUserLength = AdminContext.useStoreState(state => state.nests.selectedNests.length);
|
||||
|
||||
const onSelectAllClick = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setSelectedUsers(e.currentTarget.checked ? (users?.items?.map(user => user.id) || []) : []);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setSelectedUsers([]);
|
||||
}, [ page ]);
|
||||
|
||||
return (
|
||||
<AdminContentBlock>
|
||||
<div css={tw`w-full flex flex-row items-center`}>
|
||||
<div css={tw`w-full flex flex-row items-center mb-8`}>
|
||||
<div css={tw`flex flex-col`}>
|
||||
<h2 css={tw`text-2xl text-neutral-50 font-header font-medium`}>Users</h2>
|
||||
<p css={tw`text-base text-neutral-400`}>All registered users on the system.</p>
|
||||
|
@ -16,6 +71,66 @@ export default () => {
|
|||
New User
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<FlashMessageRender byKey={'users'} css={tw`mb-4`}/>
|
||||
|
||||
<AdminTable>
|
||||
{ users === undefined || (error && isValidating) ?
|
||||
<Loading/>
|
||||
:
|
||||
length < 1 ?
|
||||
<NoItems/>
|
||||
:
|
||||
<ContentWrapper
|
||||
checked={selectedUserLength === (length === 0 ? -1 : length)}
|
||||
onSelectAllClick={onSelectAllClick}
|
||||
>
|
||||
<Pagination data={users} onPageSelect={setPage}>
|
||||
<div css={tw`overflow-x-auto`}>
|
||||
<table css={tw`w-full table-auto`}>
|
||||
<TableHead>
|
||||
<TableHeader name={'ID'}/>
|
||||
<TableHeader name={'Email'}/>
|
||||
<TableHeader name={'Username'}/>
|
||||
<TableHeader name={'Client Name'}/>
|
||||
</TableHead>
|
||||
|
||||
<TableBody>
|
||||
{
|
||||
users.items.map(user => (
|
||||
<TableRow key={user.id}>
|
||||
<td css={tw`pl-6`}>
|
||||
<RowCheckbox id={user.id}/>
|
||||
</td>
|
||||
|
||||
<td css={tw`px-6 text-sm text-neutral-200 text-left whitespace-nowrap`}>{user.id}</td>
|
||||
<td css={tw`px-6 text-sm text-neutral-200 text-left whitespace-nowrap`}>
|
||||
<NavLink to={`${match.url}/${user.id}`} css={tw`text-primary-400 hover:text-primary-300`}>
|
||||
{user.email}
|
||||
</NavLink>
|
||||
</td>
|
||||
<td css={tw`px-6 text-sm text-neutral-200 text-left whitespace-nowrap`}>{user.username}</td>
|
||||
<td css={tw`px-6 text-sm text-neutral-200 text-left whitespace-nowrap`}>{user.lastName}, {user.firstName}</td>
|
||||
</TableRow>
|
||||
))
|
||||
}
|
||||
</TableBody>
|
||||
</table>
|
||||
</div>
|
||||
</Pagination>
|
||||
</ContentWrapper>
|
||||
}
|
||||
</AdminTable>
|
||||
</AdminContentBlock>
|
||||
);
|
||||
};
|
||||
|
||||
export default () => {
|
||||
const [ page, setPage ] = useState<number>(1);
|
||||
|
||||
return (
|
||||
<UsersContext.Provider value={{ page, setPage }}>
|
||||
<UsersContainer/>
|
||||
</UsersContext.Provider>
|
||||
);
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue