160 lines
4.3 KiB
TypeScript
160 lines
4.3 KiB
TypeScript
import { useEffect } from 'react'
|
|
import {
|
|
Link,
|
|
useLocation,
|
|
useNavigate,
|
|
useSearchParams,
|
|
} from 'react-router-dom'
|
|
import {
|
|
Heading,
|
|
Link as ChakraLink,
|
|
Table,
|
|
Tbody,
|
|
Td,
|
|
Th,
|
|
Thead,
|
|
Tr,
|
|
Avatar,
|
|
Badge,
|
|
} from '@chakra-ui/react'
|
|
import cx from 'classnames'
|
|
import { Helmet } from 'react-helmet-async'
|
|
import GroupAPI, { SortOrder } from '@/client/api/group'
|
|
import { swrConfig } from '@/client/options'
|
|
import { CreateGroupButton } from '@/components/top-bar/top-bar-buttons'
|
|
import prettyDate from '@/helpers/pretty-date'
|
|
import { decodeQuery } from '@/helpers/query'
|
|
import { groupPaginationStorage } from '@/infra/pagination'
|
|
import { SectionSpinner, PagePagination, usePagePagination } from '@/lib'
|
|
|
|
const GroupListPage = () => {
|
|
const navigate = useNavigate()
|
|
const location = useLocation()
|
|
const [searchParams] = useSearchParams()
|
|
const query = decodeQuery(searchParams.get('q') as string)
|
|
const { page, size, steps, setPage, setSize } = usePagePagination({
|
|
navigate,
|
|
location,
|
|
storage: groupPaginationStorage(),
|
|
})
|
|
const {
|
|
data: list,
|
|
error,
|
|
mutate,
|
|
} = GroupAPI.useList(
|
|
{ query, page, size, sortOrder: SortOrder.Desc },
|
|
swrConfig(),
|
|
)
|
|
|
|
useEffect(() => {
|
|
mutate()
|
|
}, [query, page, size, mutate])
|
|
|
|
return (
|
|
<>
|
|
<Helmet>
|
|
<title>Groups</title>
|
|
</Helmet>
|
|
<div className={cx('flex', 'flex-col', 'gap-3.5', 'pb-3.5')}>
|
|
<Heading className={cx('pl-2', 'text-heading')}>Groups</Heading>
|
|
{error && (
|
|
<div
|
|
className={cx(
|
|
'flex',
|
|
'items-center',
|
|
'justify-center',
|
|
'h-[300px]',
|
|
)}
|
|
>
|
|
<span>Failed to load groups.</span>
|
|
</div>
|
|
)}
|
|
{!list && !error && <SectionSpinner />}
|
|
{list && list.data.length === 0 && (
|
|
<div
|
|
className={cx(
|
|
'flex',
|
|
'items-center',
|
|
'justify-center',
|
|
'h-[300px]',
|
|
)}
|
|
>
|
|
<div className={cx('flex', 'flex-col', 'gap-1.5')}>
|
|
<span>There are no groups.</span>
|
|
<CreateGroupButton />
|
|
</div>
|
|
</div>
|
|
)}
|
|
{list && list.data.length > 0 && (
|
|
<Table variant="simple">
|
|
<Thead>
|
|
<Tr>
|
|
<Th>Name</Th>
|
|
<Th>Organization</Th>
|
|
<Th>Permission</Th>
|
|
<Th>Date</Th>
|
|
</Tr>
|
|
</Thead>
|
|
<Tbody>
|
|
{list.data.map((g) => (
|
|
<Tr key={g.id}>
|
|
<Td>
|
|
<div
|
|
className={cx(
|
|
'flex',
|
|
'flex-row',
|
|
'items-center',
|
|
'gap-1.5',
|
|
)}
|
|
>
|
|
<Avatar
|
|
name={g.name}
|
|
size="sm"
|
|
className={cx('w-[40px]', 'h-[40px]')}
|
|
/>
|
|
<ChakraLink
|
|
as={Link}
|
|
to={`/group/${g.id}/member`}
|
|
className={cx('no-underline')}
|
|
>
|
|
{g.name}
|
|
</ChakraLink>
|
|
</div>
|
|
</Td>
|
|
<Td>
|
|
<ChakraLink
|
|
as={Link}
|
|
to={`/organization/${g.organization.id}/member`}
|
|
className={cx('no-underline')}
|
|
>
|
|
{g.organization.name}
|
|
</ChakraLink>
|
|
</Td>
|
|
<Td>
|
|
<Badge>{g.permission}</Badge>
|
|
</Td>
|
|
<Td>{prettyDate(g.createTime)}</Td>
|
|
</Tr>
|
|
))}
|
|
</Tbody>
|
|
</Table>
|
|
)}
|
|
{list && (
|
|
<PagePagination
|
|
style={{ alignSelf: 'end' }}
|
|
totalElements={list.totalElements}
|
|
totalPages={list.totalPages}
|
|
page={page}
|
|
size={size}
|
|
steps={steps}
|
|
setPage={setPage}
|
|
setSize={setSize}
|
|
/>
|
|
)}
|
|
</div>
|
|
</>
|
|
)
|
|
}
|
|
|
|
export default GroupListPage
|