2022-03-12 20:50:53 +00:00
|
|
|
import React, { Fragment, useEffect, useState } from 'react';
|
2022-02-27 18:32:22 +00:00
|
|
|
import { UUID } from '@/api/definitions';
|
2022-03-12 17:05:09 +00:00
|
|
|
import { User } from '@definitions/admin';
|
2022-02-27 18:32:22 +00:00
|
|
|
import { Transition } from '@/components/elements/transitions';
|
2022-02-27 16:04:57 +00:00
|
|
|
import { LockOpenIcon, PlusIcon, SupportIcon, TrashIcon } from '@heroicons/react/solid';
|
2022-02-26 22:05:30 +00:00
|
|
|
import { Button } from '@/components/elements/button/index';
|
2022-02-27 02:51:40 +00:00
|
|
|
import { Checkbox, InputField } from '@/components/elements/inputs';
|
2022-02-27 16:04:57 +00:00
|
|
|
import UserTableRow from '@/components/admin/users/UserTableRow';
|
2022-03-12 17:05:09 +00:00
|
|
|
import { useGetUsers } from '@/api/admin/users';
|
|
|
|
import TFootPaginated from '@/components/elements/table/TFootPaginated';
|
2022-03-12 20:50:53 +00:00
|
|
|
import extractSearchFilters from '@/helpers/extractSearchFilters';
|
|
|
|
import useDebouncedState from '@/plugins/useDebouncedState';
|
2022-02-27 21:45:23 +00:00
|
|
|
|
|
|
|
const filters = [ 'id', 'uuid', 'external_id', 'username', 'email' ] as const;
|
|
|
|
|
2022-02-27 21:15:11 +00:00
|
|
|
const UsersContainer = () => {
|
2022-03-12 20:50:53 +00:00
|
|
|
const [ search, setSearch ] = useDebouncedState('', 500);
|
2022-02-27 18:32:22 +00:00
|
|
|
const [ selected, setSelected ] = useState<UUID[]>([]);
|
2022-03-12 20:50:53 +00:00
|
|
|
const { data: users } = useGetUsers(extractSearchFilters(search, filters));
|
2022-02-27 18:32:22 +00:00
|
|
|
|
2022-02-21 00:10:58 +00:00
|
|
|
useEffect(() => {
|
|
|
|
document.title = 'Admin | Users';
|
|
|
|
}, []);
|
|
|
|
|
2022-02-27 18:32:22 +00:00
|
|
|
const onRowChange = (user: User, checked: boolean) => {
|
|
|
|
setSelected((state) => {
|
|
|
|
return checked ? [ ...state, user.uuid ] : selected.filter((uuid) => uuid !== user.uuid);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2022-02-27 21:15:11 +00:00
|
|
|
const selectAllChecked = users && users.items.length > 0 && selected.length > 0;
|
|
|
|
const onSelectAll = () => setSelected((state) => state.length > 0 ? [] : users?.items.map(({ uuid }) => uuid) || []);
|
2022-02-27 18:32:22 +00:00
|
|
|
|
2022-02-21 00:10:58 +00:00
|
|
|
return (
|
2022-02-26 22:05:30 +00:00
|
|
|
<div>
|
|
|
|
<div className={'flex justify-end mb-4'}>
|
|
|
|
<Button className={'shadow focus:ring-offset-2 focus:ring-offset-neutral-800'}>
|
|
|
|
Add User <PlusIcon className={'ml-2 w-5 h-5'}/>
|
|
|
|
</Button>
|
|
|
|
</div>
|
2022-02-27 03:06:47 +00:00
|
|
|
<div className={'relative flex items-center rounded-t bg-neutral-700 px-4 py-2'}>
|
2022-02-27 02:51:40 +00:00
|
|
|
<div className={'mr-6'}>
|
2022-02-27 18:32:22 +00:00
|
|
|
<Checkbox
|
|
|
|
checked={selectAllChecked}
|
2022-03-12 20:50:53 +00:00
|
|
|
disabled={!users?.items.length}
|
2022-02-27 21:15:11 +00:00
|
|
|
indeterminate={selected.length !== users?.items.length}
|
2022-02-27 18:32:22 +00:00
|
|
|
onChange={onSelectAll}
|
|
|
|
/>
|
2022-02-27 02:51:40 +00:00
|
|
|
</div>
|
|
|
|
<div className={'flex-1'}>
|
2022-02-27 16:04:57 +00:00
|
|
|
<InputField
|
|
|
|
type={'text'}
|
|
|
|
name={'filter'}
|
|
|
|
placeholder={'Begin typing to filter...'}
|
|
|
|
className={'w-56 focus:w-96'}
|
2022-03-12 20:50:53 +00:00
|
|
|
onChange={e => setSearch(e.currentTarget.value)}
|
2022-02-27 16:04:57 +00:00
|
|
|
/>
|
2022-02-27 02:51:40 +00:00
|
|
|
</div>
|
2022-02-27 18:32:22 +00:00
|
|
|
<Transition.Fade as={Fragment} show={selected.length > 0} duration={'duration-75'}>
|
|
|
|
<div className={'absolute rounded-t bg-neutral-700 w-full h-full top-0 left-0 flex items-center justify-end space-x-4 px-4'}>
|
|
|
|
<div className={'flex-1'}>
|
|
|
|
<Checkbox
|
|
|
|
checked={selectAllChecked}
|
2022-02-27 21:15:11 +00:00
|
|
|
indeterminate={selected.length !== users?.items.length}
|
2022-02-27 18:32:22 +00:00
|
|
|
onChange={onSelectAll}
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
<Button.Text square>
|
|
|
|
<SupportIcon className={'w-4 h-4'}/>
|
|
|
|
</Button.Text>
|
|
|
|
<Button.Text square>
|
|
|
|
<LockOpenIcon className={'w-4 h-4'}/>
|
|
|
|
</Button.Text>
|
|
|
|
<Button.Text square>
|
|
|
|
<TrashIcon className={'w-4 h-4'}/>
|
|
|
|
</Button.Text>
|
2022-02-27 03:06:47 +00:00
|
|
|
</div>
|
2022-02-27 18:32:22 +00:00
|
|
|
</Transition.Fade>
|
2022-02-27 02:51:40 +00:00
|
|
|
</div>
|
2022-02-26 22:05:30 +00:00
|
|
|
<table className={'min-w-full rounded bg-neutral-700'}>
|
2022-02-21 00:10:58 +00:00
|
|
|
<thead className={'bg-neutral-900'}>
|
|
|
|
<tr>
|
|
|
|
<th scope={'col'} className={'w-8'}/>
|
|
|
|
<th scope={'col'} className={'text-left px-6 py-2 w-full'}>Email</th>
|
|
|
|
<th scope={'col'}/>
|
2022-02-27 02:26:53 +00:00
|
|
|
<th scope={'col'}/>
|
2022-02-21 00:10:58 +00:00
|
|
|
</tr>
|
|
|
|
</thead>
|
|
|
|
<tbody>
|
2022-02-27 21:15:11 +00:00
|
|
|
{users?.items.map(user => (
|
2022-02-27 18:32:22 +00:00
|
|
|
<UserTableRow
|
|
|
|
key={user.uuid}
|
|
|
|
user={user}
|
|
|
|
selected={selected.includes(user.uuid)}
|
|
|
|
onRowChange={onRowChange}
|
|
|
|
/>
|
|
|
|
))}
|
2022-02-21 00:10:58 +00:00
|
|
|
</tbody>
|
2022-03-12 17:05:09 +00:00
|
|
|
{users && <TFootPaginated span={4} pagination={users.pagination}/>}
|
2022-02-21 00:10:58 +00:00
|
|
|
</table>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
2022-02-27 21:15:11 +00:00
|
|
|
export default UsersContainer;
|