Files
Docker/Downloads/Voltaserve/ui/src/components/common/user-selector.tsx
2024-04-21 14:42:52 +02:00

232 lines
6.4 KiB
TypeScript

import { useCallback, useEffect, useState } from 'react'
import {
Button,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
useDisclosure,
Table,
Tr,
Tbody,
Td,
Avatar,
Radio,
} from '@chakra-ui/react'
import cx from 'classnames'
import UserAPI, { SortOrder, User } from '@/client/api/user'
import { swrConfig } from '@/client/options'
import userToString from '@/helpers/user-to-string'
import { SectionSpinner, Pagination, SearchInput } from '@/lib'
export type UserSelectorProps = {
value?: User
organizationId?: string
groupId?: string
nonGroupMembersOnly?: boolean
onConfirm?: (user: User) => void
}
const UserSelector = ({
value,
organizationId,
groupId,
nonGroupMembersOnly,
onConfirm,
}: UserSelectorProps) => {
const { isOpen, onOpen, onClose } = useDisclosure()
const [page, setPage] = useState(1)
const [query, setQuery] = useState('')
const [selected, setSelected] = useState<User>()
const {
data: list,
error,
mutate,
} = UserAPI.useList(
{
query,
organizationId,
groupId,
nonGroupMembersOnly,
page,
size: 5,
sortOrder: SortOrder.Desc,
},
swrConfig(),
)
useEffect(() => {
mutate()
}, [page, query, mutate])
useEffect(() => {
if (!isOpen) {
setPage(1)
setSelected(undefined)
setQuery('')
}
}, [isOpen])
const handleConfirm = useCallback(() => {
if (selected) {
onConfirm?.(selected)
onClose()
}
}, [selected, onConfirm, onClose])
return (
<>
<Button
variant="outline"
className={cx(
'w-full',
{ 'text-black': value },
{ 'dark:text-white': value },
{ 'text-gray-500': !value },
{ 'dark:text-gray-500': !value },
)}
onClick={onOpen}
>
{value ? userToString(value) : 'Select User'}
</Button>
<Modal
size="xl"
isOpen={isOpen}
onClose={onClose}
closeOnOverlayClick={false}
>
<ModalOverlay />
<ModalContent>
<ModalHeader>Select User</ModalHeader>
<ModalCloseButton />
<ModalBody>
<div className={cx('flex', 'flex-col', 'gap-1.5')}>
<SearchInput
query={query}
onChange={(value) => setQuery(value)}
/>
{!list && error && (
<div
className={cx(
'flex',
'items-center',
'justify-center',
'h-[300px]',
)}
>
<span>Failed to load users.</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',
'items-center',
'gap-1.5',
)}
>
<span>There are no users.</span>
</div>
</div>
)}
{list && list.data.length > 0 && (
<Table variant="simple" size="sm">
<colgroup>
<col className={cx('w-[40px]')} />
<col className={cx('w-[auto]')} />
</colgroup>
<Tbody>
{list.data.map((u) => (
<Tr
key={u.id}
className={cx(
'cursor-pointer',
{ 'bg-gray-100': selected?.id === u.id },
{ 'dark:bg-gray-600': selected?.id === u.id },
{ 'bg-transparent': selected?.id !== u.id },
)}
onClick={() => setSelected(u)}
>
<Td className={cx('px-0.5', 'text-center')}>
<Radio size="md" isChecked={selected?.id === u.id} />
</Td>
<Td className={cx('p-0.5')}>
<div
className={cx(
'flex',
'flex-row',
'items-center',
'gap-1.5',
)}
>
<Avatar
name={u.fullName}
size="sm"
className={cx('w-[40px]', 'h-[40px]')}
/>
<span className={cx('text-base')}>
{userToString(u)}
</span>
</div>
</Td>
</Tr>
))}
</Tbody>
</Table>
)}
{list && (
<div className={cx('self-end')}>
{list.totalPages > 1 ? (
<Pagination
uiSize="md"
maxButtons={3}
page={page}
totalPages={list.totalPages}
onPageChange={(value) => setPage(value)}
/>
) : null}
</div>
)}
</div>
</ModalBody>
<ModalFooter>
<div className={cx('flex', 'flex-row', 'items-center', 'gap-1')}>
<Button
type="button"
variant="outline"
colorScheme="blue"
onClick={onClose}
>
Cancel
</Button>
<Button
variant="solid"
colorScheme="blue"
isDisabled={!selected}
onClick={handleConfirm}
>
Confirm
</Button>
</div>
</ModalFooter>
</ModalContent>
</Modal>
</>
)
}
export default UserSelector