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