gitscope
https://dev.kronoz.win/r/marzneshin.git
app/dashboard/src/pages/users/table/index.tsx · v0.1.3
Source
1 912788f Mardin 2y ago
2 912788f Mardin 2y ago import {
3 912788f Mardin 2y ago Accordion,
4 912788f Mardin 2y ago AccordionButton,
5 912788f Mardin 2y ago AccordionItem,
6 912788f Mardin 2y ago AccordionPanel,
7 912788f Mardin 2y ago Box,
8 912788f Mardin 2y ago HStack,
9 912788f Mardin 2y ago IconButton,
10 912788f Mardin 2y ago Table,
11 912788f Mardin 2y ago TableProps,
12 912788f Mardin 2y ago Tbody,
13 912788f Mardin 2y ago Td,
14 912788f Mardin 2y ago Text,
15 912788f Mardin 2y ago Th,
16 912788f Mardin 2y ago Thead,
17 912788f Mardin 2y ago Tooltip,
18 912788f Mardin 2y ago Tr,
19 912788f Mardin 2y ago useBreakpointValue,
20 912788f Mardin 2y ago VStack,
21 912788f Mardin 2y ago } from '@chakra-ui/react';
22 912788f Mardin 2y ago import classNames from 'classnames';
23 5bb2712 Mardin 2y ago import { statusColors } from 'constants/Settings';
24 63b702d Mardin 2y ago import { FC, Fragment, useState } from 'react';
25 912788f Mardin 2y ago import { useTranslation } from 'react-i18next';
26 5825ea4 Mardin 2y ago import { OnlineBadge } from 'components/online-badge';
27 5825ea4 Mardin 2y ago import { OnlineStatus } from 'components/online-status';
28 5825ea4 Mardin 2y ago import { StatusBadge } from 'components/status-badge';
29 4205669 Mardin 2y ago import {
30 89f8f93 Mardin 2y ago Pagination,
31 89f8f93 Mardin 2y ago EmptySection,
32 4205669 Mardin 2y ago AccordionArrowIcon,
33 63b702d Mardin 2y ago handleSort,
34 4205669 Mardin 2y ago EditIcon,
35 89f8f93 Mardin 2y ago StatusSortSelect,
36 89f8f93 Mardin 2y ago Sort,
37 89f8f93 Mardin 2y ago } from 'components/table';
38 bedeeff Mardin 2y ago import { UsageSlider, UsageSliderCompact } from './usage-slider';
39 a61ba53 Mrdn 2y ago import { fetchUsers, useUsers } from 'stores';
40 63b702d Mardin 2y ago import { ActionButtons } from './action-buttons';
41 6aab6f0 Mrdn 2y ago import { pageSizeManagers } from 'utils/userPreferenceStorage';
42 16e5ea1 Mrdn 2y ago import { useQuery } from '@tanstack/react-query';
43 a61ba53 Mrdn 2y ago import { User } from 'types';
44 a61ba53 Mrdn 2y ago import { queryIds } from 'constants/query-ids';
45 912788f Mardin 2y ago
46 912788f Mardin 2y ago type ExpandedIndex = number | number[];
47 912788f Mardin 2y ago
48 912788f Mardin 2y ago type UsersTableProps = {} & TableProps;
49 912788f Mardin 2y ago
50 912788f Mardin 2y ago export const UsersTable: FC<UsersTableProps> = (props) => {
51 912788f Mardin 2y ago const {
52 912788f Mardin 2y ago usersFilters: filters,
53 912788f Mardin 2y ago onEditingUser,
54 4205669 Mardin 2y ago onCreateUser,
55 912788f Mardin 2y ago onFilterChange,
56 4205669 Mardin 2y ago } = useUsers();
57 a61ba53 Mrdn 2y ago const { data } = useQuery({
58 16e5ea1 Mrdn 2y ago queryKey: [queryIds.users],
59 a61ba53 Mrdn 2y ago queryFn: () => { return fetchUsers(filters) },
60 a61ba53 Mrdn 2y ago initialData: { total: 0, users: [] },
61 a61ba53 Mrdn 2y ago });
62 912788f Mardin 2y ago const { t } = useTranslation();
63 912788f Mardin 2y ago const [selectedRow, setSelectedRow] = useState<ExpandedIndex | undefined>(
64 912788f Mardin 2y ago undefined
65 912788f Mardin 2y ago );
66 912788f Mardin 2y ago const useTable = useBreakpointValue({ base: false, md: true });
67 912788f Mardin 2y ago
68 912788f Mardin 2y ago const handleStatusFilter = (e: any) => {
69 912788f Mardin 2y ago onFilterChange({
70 912788f Mardin 2y ago status: e.target.value.length > 0 ? e.target.value : undefined,
71 912788f Mardin 2y ago });
72 912788f Mardin 2y ago };
73 a61ba53 Mrdn 2y ago const isFiltered = data?.total !== data?.users.length;
74 912788f Mardin 2y ago const toggleAccordion = (index: number) => {
75 912788f Mardin 2y ago setSelectedRow(index === selectedRow ? undefined : index);
76 912788f Mardin 2y ago };
77 912788f Mardin 2y ago return (
78 912788f Mardin 2y ago <Box id="users-table" overflowX={{ base: 'unset', md: 'unset' }}>
79 912788f Mardin 2y ago <Accordion
80 912788f Mardin 2y ago allowMultiple
81 912788f Mardin 2y ago display={{ base: 'block', md: 'none' }}
82 912788f Mardin 2y ago index={selectedRow}
83 912788f Mardin 2y ago >
84 912788f Mardin 2y ago <Table orientation="vertical" zIndex="docked" {...props}>
85 912788f Mardin 2y ago <Thead zIndex="docked" position="relative">
86 912788f Mardin 2y ago <Tr>
87 912788f Mardin 2y ago <Th
88 912788f Mardin 2y ago position="sticky"
89 912788f Mardin 2y ago minW="120px"
90 912788f Mardin 2y ago pl={4}
91 912788f Mardin 2y ago pr={4}
92 912788f Mardin 2y ago cursor={'pointer'}
93 63b702d Mardin 2y ago onClick={handleSort.bind(null, filters, 'username', onFilterChange)}
94 912788f Mardin 2y ago >
95 912788f Mardin 2y ago <HStack>
96 912788f Mardin 2y ago <span>{t('users')}</span>
97 912788f Mardin 2y ago <Sort sort={filters.sort} column="username" />
98 912788f Mardin 2y ago </HStack>
99 912788f Mardin 2y ago </Th>
100 912788f Mardin 2y ago <Th
101 912788f Mardin 2y ago position="sticky"
102 912788f Mardin 2y ago minW="50px"
103 912788f Mardin 2y ago pl={0}
104 912788f Mardin 2y ago pr={0}
105 912788f Mardin 2y ago w="140px"
106 912788f Mardin 2y ago cursor={'pointer'}
107 912788f Mardin 2y ago >
108 912788f Mardin 2y ago <HStack spacing={0} position="relative">
109 912788f Mardin 2y ago <Text
110 912788f Mardin 2y ago position="absolute"
111 912788f Mardin 2y ago _dark={{
112 912788f Mardin 2y ago bg: 'gray.750',
113 912788f Mardin 2y ago }}
114 912788f Mardin 2y ago _light={{
115 912788f Mardin 2y ago bg: '#F9FAFB',
116 912788f Mardin 2y ago }}
117 912788f Mardin 2y ago userSelect="none"
118 912788f Mardin 2y ago pointerEvents="none"
119 912788f Mardin 2y ago zIndex={1}
120 912788f Mardin 2y ago w="100%"
121 912788f Mardin 2y ago >
122 912788f Mardin 2y ago {t('usersTable.status')}
123 912788f Mardin 2y ago {filters.status ? ': ' + filters.status : ''}
124 912788f Mardin 2y ago </Text>
125 5bb2712 Mardin 2y ago <StatusSortSelect filters={filters} handleStatusFilter={handleStatusFilter} options={['active', 'disabled', 'limited', 'expired', 'on_hold']} />
126 912788f Mardin 2y ago </HStack>
127 912788f Mardin 2y ago </Th>
128 912788f Mardin 2y ago <Th
129 912788f Mardin 2y ago position="sticky"
130 912788f Mardin 2y ago minW="100px"
131 912788f Mardin 2y ago cursor={'pointer'}
132 912788f Mardin 2y ago pr={0}
133 63b702d Mardin 2y ago onClick={handleSort.bind(null, filters, 'used_traffic', onFilterChange)}
134 912788f Mardin 2y ago >
135 912788f Mardin 2y ago <HStack>
136 912788f Mardin 2y ago <span>{t('usersTable.dataUsage')}</span>
137 912788f Mardin 2y ago <Sort sort={filters.sort} column="used_traffic" />
138 912788f Mardin 2y ago </HStack>
139 912788f Mardin 2y ago </Th>
140 912788f Mardin 2y ago <Th
141 912788f Mardin 2y ago position="sticky"
142 912788f Mardin 2y ago minW="32px"
143 912788f Mardin 2y ago w="32px"
144 912788f Mardin 2y ago p={0}
145 912788f Mardin 2y ago cursor={'pointer'}
146 912788f Mardin 2y ago ></Th>
147 912788f Mardin 2y ago </Tr>
148 912788f Mardin 2y ago </Thead>
149 912788f Mardin 2y ago <Tbody>
150 912788f Mardin 2y ago {!useTable &&
151 a61ba53 Mrdn 2y ago data?.users.map((user, i) => {
152 4205669 Mardin 2y ago return (
153 4205669 Mardin 2y ago <Fragment key={user.username}>
154 4205669 Mardin 2y ago <Tr
155 4205669 Mardin 2y ago onClick={toggleAccordion.bind(null, i)}
156 4205669 Mardin 2y ago cursor="pointer"
157 4205669 Mardin 2y ago >
158 4205669 Mardin 2y ago <Td borderBottom={0} minW="100px" pl={4} pr={4}>
159 4205669 Mardin 2y ago <div className="flex-status">
160 4205669 Mardin 2y ago <OnlineBadge lastOnline={user.online_at} />
161 4205669 Mardin 2y ago {user.username}
162 4205669 Mardin 2y ago </div>
163 4205669 Mardin 2y ago </Td>
164 4205669 Mardin 2y ago <Td borderBottom={0} minW="50px" pl={0} pr={0}>
165 4205669 Mardin 2y ago <StatusBadge
166 4205669 Mardin 2y ago compact
167 4205669 Mardin 2y ago showDetail={false}
168 4205669 Mardin 2y ago expiryDate={user.expire}
169 4205669 Mardin 2y ago status={user.status}
170 4205669 Mardin 2y ago />
171 4205669 Mardin 2y ago </Td>
172 4205669 Mardin 2y ago <Td borderBottom={0} minW="100px" pr={0}>
173 4205669 Mardin 2y ago <UsageSliderCompact
174 4205669 Mardin 2y ago totalUsedTraffic={user.lifetime_used_traffic}
175 4205669 Mardin 2y ago dataLimitResetStrategy={
176 4205669 Mardin 2y ago user.data_limit_reset_strategy
177 4205669 Mardin 2y ago }
178 4205669 Mardin 2y ago used={user.used_traffic}
179 4205669 Mardin 2y ago total={user.data_limit}
180 4205669 Mardin 2y ago colorScheme={statusColors[user.status].bandWidthColor}
181 4205669 Mardin 2y ago />
182 4205669 Mardin 2y ago </Td>
183 4205669 Mardin 2y ago <Td p={0} borderBottom={0} w="32px" minW="32px">
184 4205669 Mardin 2y ago <AccordionArrowIcon
185 4205669 Mardin 2y ago color="gray.600"
186 4205669 Mardin 2y ago _dark={{
187 4205669 Mardin 2y ago color: 'gray.400',
188 4205669 Mardin 2y ago }}
189 4205669 Mardin 2y ago transition="transform .2s ease-out"
190 4205669 Mardin 2y ago transform={
191 4205669 Mardin 2y ago selectedRow === i ? 'rotate(180deg)' : '0deg'
192 4205669 Mardin 2y ago }
193 4205669 Mardin 2y ago />
194 4205669 Mardin 2y ago </Td>
195 4205669 Mardin 2y ago </Tr>
196 4205669 Mardin 2y ago <Tr
197 4205669 Mardin 2y ago className="collapsible"
198 4205669 Mardin 2y ago onClick={toggleAccordion.bind(null, i)}
199 4205669 Mardin 2y ago >
200 4205669 Mardin 2y ago <Td p={0} colSpan={4}>
201 4205669 Mardin 2y ago <AccordionItem border={0}>
202 4205669 Mardin 2y ago <AccordionButton display="none"></AccordionButton>
203 4205669 Mardin 2y ago <AccordionPanel
204 4205669 Mardin 2y ago border={0}
205 4205669 Mardin 2y ago cursor="pointer"
206 4205669 Mardin 2y ago px={6}
207 4205669 Mardin 2y ago py={3}
208 4205669 Mardin 2y ago >
209 4205669 Mardin 2y ago <VStack justifyContent="space-between" spacing="4">
210 4205669 Mardin 2y ago <VStack
211 4205669 Mardin 2y ago alignItems="flex-start"
212 4205669 Mardin 2y ago w="full"
213 4205669 Mardin 2y ago spacing={-1}
214 4205669 Mardin 2y ago >
215 4205669 Mardin 2y ago <Text
216 4205669 Mardin 2y ago textTransform="capitalize"
217 4205669 Mardin 2y ago fontSize="xs"
218 4205669 Mardin 2y ago fontWeight="bold"
219 4205669 Mardin 2y ago color="gray.600"
220 4205669 Mardin 2y ago _dark={{
221 4205669 Mardin 2y ago color: 'gray.400',
222 4205669 Mardin 2y ago }}
223 4205669 Mardin 2y ago >
224 4205669 Mardin 2y ago {t('usersTable.dataUsage')}
225 4205669 Mardin 2y ago </Text>
226 4205669 Mardin 2y ago <Box width="full" minW="230px">
227 4205669 Mardin 2y ago <UsageSlider
228 4205669 Mardin 2y ago totalUsedTraffic={
229 4205669 Mardin 2y ago user.lifetime_used_traffic
230 4205669 Mardin 2y ago }
231 4205669 Mardin 2y ago dataLimitResetStrategy={
232 4205669 Mardin 2y ago user.data_limit_reset_strategy
233 4205669 Mardin 2y ago }
234 4205669 Mardin 2y ago used={user.used_traffic}
235 4205669 Mardin 2y ago total={user.data_limit}
236 4205669 Mardin 2y ago colorScheme={
237 4205669 Mardin 2y ago statusColors[user.status].bandWidthColor
238 4205669 Mardin 2y ago }
239 4205669 Mardin 2y ago />
240 4205669 Mardin 2y ago </Box>
241 4205669 Mardin 2y ago </VStack>
242 4205669 Mardin 2y ago <HStack w="full" justifyContent="space-between">
243 4205669 Mardin 2y ago <Box width="full">
244 4205669 Mardin 2y ago <StatusBadge
245 4205669 Mardin 2y ago compact
246 4205669 Mardin 2y ago expiryDate={user.expire}
247 4205669 Mardin 2y ago status={user.status}
248 4205669 Mardin 2y ago />
249 4205669 Mardin 2y ago <OnlineStatus lastOnline={user.online_at} />
250 4205669 Mardin 2y ago </Box>
251 4205669 Mardin 2y ago <HStack>
252 4205669 Mardin 2y ago <ActionButtons user={user} />
253 4205669 Mardin 2y ago <Tooltip
254 4205669 Mardin 2y ago label={t('userDialog.editUser')}
255 4205669 Mardin 2y ago placement="top"
256 912788f Mardin 2y ago >
257 4205669 Mardin 2y ago <IconButton
258 4205669 Mardin 2y ago p="0 !important"
259 4205669 Mardin 2y ago aria-label="Edit user"
260 4205669 Mardin 2y ago bg="transparent"
261 4205669 Mardin 2y ago _dark={{
262 4205669 Mardin 2y ago _hover: {
263 4205669 Mardin 2y ago bg: 'gray.700',
264 4205669 Mardin 2y ago },
265 4205669 Mardin 2y ago }}
266 4205669 Mardin 2y ago size={{
267 4205669 Mardin 2y ago base: 'sm',
268 4205669 Mardin 2y ago md: 'md',
269 4205669 Mardin 2y ago }}
270 4205669 Mardin 2y ago onClick={(e) => {
271 4205669 Mardin 2y ago e.stopPropagation();
272 4205669 Mardin 2y ago onEditingUser(user);
273 4205669 Mardin 2y ago }}
274 4205669 Mardin 2y ago >
275 4205669 Mardin 2y ago <EditIcon />
276 4205669 Mardin 2y ago </IconButton>
277 4205669 Mardin 2y ago </Tooltip>
278 4205669 Mardin 2y ago </HStack>
279 4205669 Mardin 2y ago </HStack>
280 4205669 Mardin 2y ago </VStack>
281 4205669 Mardin 2y ago </AccordionPanel>
282 4205669 Mardin 2y ago </AccordionItem>
283 4205669 Mardin 2y ago </Td>
284 4205669 Mardin 2y ago </Tr>
285 4205669 Mardin 2y ago </Fragment>
286 4205669 Mardin 2y ago );
287 4205669 Mardin 2y ago })}
288 912788f Mardin 2y ago </Tbody>
289 912788f Mardin 2y ago </Table>
290 912788f Mardin 2y ago </Accordion>
291 912788f Mardin 2y ago <Table
292 912788f Mardin 2y ago orientation="vertical"
293 912788f Mardin 2y ago display={{ base: 'none', md: 'table' }}
294 912788f Mardin 2y ago {...props}
295 912788f Mardin 2y ago >
296 912788f Mardin 2y ago <Thead zIndex="docked" position="relative">
297 912788f Mardin 2y ago <Tr>
298 912788f Mardin 2y ago <Th
299 912788f Mardin 2y ago position="sticky"
300 912788f Mardin 2y ago minW="140px"
301 912788f Mardin 2y ago cursor={'pointer'}
302 63b702d Mardin 2y ago onClick={handleSort.bind(null, filters, 'username', onFilterChange)}
303 912788f Mardin 2y ago >
304 912788f Mardin 2y ago <HStack>
305 912788f Mardin 2y ago <span>{t('username')}</span>
306 912788f Mardin 2y ago <Sort sort={filters.sort} column="username" />
307 912788f Mardin 2y ago </HStack>
308 912788f Mardin 2y ago </Th>
309 912788f Mardin 2y ago <Th
310 912788f Mardin 2y ago position="sticky"
311 912788f Mardin 2y ago width="400px"
312 912788f Mardin 2y ago minW="150px"
313 912788f Mardin 2y ago cursor={'pointer'}
314 912788f Mardin 2y ago >
315 912788f Mardin 2y ago <HStack spacing={0} position="relative">
316 912788f Mardin 2y ago <Text
317 912788f Mardin 2y ago position="absolute"
318 912788f Mardin 2y ago _dark={{
319 912788f Mardin 2y ago bg: 'gray.750',
320 912788f Mardin 2y ago }}
321 912788f Mardin 2y ago _light={{
322 912788f Mardin 2y ago bg: '#F9FAFB',
323 912788f Mardin 2y ago }}
324 912788f Mardin 2y ago userSelect="none"
325 912788f Mardin 2y ago pointerEvents="none"
326 912788f Mardin 2y ago zIndex={1}
327 912788f Mardin 2y ago w="100%"
328 912788f Mardin 2y ago >
329 912788f Mardin 2y ago {t('usersTable.status')}
330 912788f Mardin 2y ago {filters.status ? ': ' + filters.status : ''}
331 912788f Mardin 2y ago </Text>
332 5bb2712 Mardin 2y ago <StatusSortSelect filters={filters} handleStatusFilter={handleStatusFilter} options={['active', 'disabled', 'limited', 'expired', 'on_hold']} />
333 912788f Mardin 2y ago </HStack>
334 912788f Mardin 2y ago </Th>
335 912788f Mardin 2y ago <Th
336 912788f Mardin 2y ago position="sticky"
337 912788f Mardin 2y ago width="350px"
338 912788f Mardin 2y ago minW="230px"
339 912788f Mardin 2y ago cursor={'pointer'}
340 63b702d Mardin 2y ago onClick={handleSort.bind(null, filters, 'used_traffic', onFilterChange)}
341 912788f Mardin 2y ago >
342 912788f Mardin 2y ago <HStack>
343 912788f Mardin 2y ago <span>{t('usersTable.dataUsage')}</span>
344 912788f Mardin 2y ago <Sort sort={filters.sort} column="used_traffic" />
345 912788f Mardin 2y ago </HStack>
346 912788f Mardin 2y ago </Th>
347 912788f Mardin 2y ago <Th
348 912788f Mardin 2y ago position="sticky"
349 912788f Mardin 2y ago width="200px"
350 912788f Mardin 2y ago minW="180px"
351 912788f Mardin 2y ago />
352 912788f Mardin 2y ago </Tr>
353 912788f Mardin 2y ago </Thead>
354 912788f Mardin 2y ago <Tbody>
355 912788f Mardin 2y ago {useTable &&
356 a61ba53 Mrdn 2y ago data?.users.map((user: User, i) => {
357 4205669 Mardin 2y ago return (
358 4205669 Mardin 2y ago <Tr
359 4205669 Mardin 2y ago key={user.username}
360 4205669 Mardin 2y ago className={classNames('interactive', {
361 a61ba53 Mrdn 2y ago 'last-row': i === data?.users.length - 1,
362 4205669 Mardin 2y ago })}
363 4205669 Mardin 2y ago onClick={() => onEditingUser(user)}
364 4205669 Mardin 2y ago >
365 4205669 Mardin 2y ago <Td minW="140px">
366 4205669 Mardin 2y ago <div className="flex-status">
367 4205669 Mardin 2y ago <OnlineBadge lastOnline={user.online_at} />
368 4205669 Mardin 2y ago {user.username}
369 4205669 Mardin 2y ago <OnlineStatus lastOnline={user.online_at} />
370 4205669 Mardin 2y ago </div>
371 4205669 Mardin 2y ago </Td>
372 4205669 Mardin 2y ago <Td width="400px" minW="150px">
373 4205669 Mardin 2y ago <StatusBadge
374 4205669 Mardin 2y ago expiryDate={user.expire}
375 4205669 Mardin 2y ago status={user.status}
376 4205669 Mardin 2y ago />
377 4205669 Mardin 2y ago </Td>
378 4205669 Mardin 2y ago <Td width="350px" minW="230px">
379 4205669 Mardin 2y ago <UsageSlider
380 4205669 Mardin 2y ago totalUsedTraffic={user.lifetime_used_traffic}
381 4205669 Mardin 2y ago dataLimitResetStrategy={user.data_limit_reset_strategy}
382 4205669 Mardin 2y ago used={user.used_traffic}
383 4205669 Mardin 2y ago total={user.data_limit}
384 4205669 Mardin 2y ago colorScheme={statusColors[user.status].bandWidthColor}
385 4205669 Mardin 2y ago />
386 4205669 Mardin 2y ago </Td>
387 4205669 Mardin 2y ago <Td width="200px" minW="180px">
388 4205669 Mardin 2y ago <ActionButtons user={user} />
389 4205669 Mardin 2y ago </Td>
390 4205669 Mardin 2y ago </Tr>
391 4205669 Mardin 2y ago );
392 4205669 Mardin 2y ago })}
393 a61ba53 Mrdn 2y ago {data?.users.length == 0 && (
394 912788f Mardin 2y ago <Tr>
395 912788f Mardin 2y ago <Td colSpan={4}>
396 4205669 Mardin 2y ago <EmptySection
397 4205669 Mardin 2y ago isFiltered={isFiltered}
398 4205669 Mardin 2y ago noObjectT="usersTable.noUser"
399 4205669 Mardin 2y ago noObjectMatchedT='usersTable.noUserMatched'
400 4205669 Mardin 2y ago createObjectT="createUser"
401 4205669 Mardin 2y ago onCreateObject={onCreateUser}
402 4205669 Mardin 2y ago />
403 912788f Mardin 2y ago </Td>
404 912788f Mardin 2y ago </Tr>
405 912788f Mardin 2y ago )}
406 912788f Mardin 2y ago </Tbody>
407 912788f Mardin 2y ago </Table>
408 a61ba53 Mrdn 2y ago <Pagination filters={filters} total={data?.total || 0} onFilterChange={onFilterChange} pageSizeManager={pageSizeManagers.users} />
409 912788f Mardin 2y ago </Box>
410 912788f Mardin 2y ago );
411 912788f Mardin 2y ago };