gitscope
← marzneshin / v0.1.2 / app/dashboard/src/pages/hosts/table/index.tsx
https://dev.kronoz.win/r/marzneshin.git
app/dashboard/src/pages/hosts/table/index.tsx · TypeScript
Blame Raw
  1import {
  2  Accordion,
  3  AccordionButton,
  4  AccordionItem,
  5  AccordionPanel,
  6  Box,
  7  HStack,
  8  IconButton,
  9  Table,
 10  TableProps,
 11  Tbody,
 12  Td,
 13  Text,
 14  Th,
 15  Thead,
 16  Tooltip,
 17  Tr,
 18  useBreakpointValue,
 19  VStack,
 20} from '@chakra-ui/react';
 21import classNames from 'classnames';
 22import { FC, Fragment, useEffect, useState } from 'react';
 23import { useTranslation } from 'react-i18next';
 24import {
 25  EmptySection,
 26  AccordionArrowIcon,
 27  EditIcon,
 28  Sort,
 29  handleSort,
 30} from 'components/table';
 31import { useInbounds, fetchInboundHosts } from 'stores';
 32import { useQuery } from 'react-query';
 33
 34type ExpandedIndex = number | number[];
 35
 36type NodesTableProps = {} & TableProps;
 37
 38export const HostsTable: FC<NodesTableProps> = (props) => {
 39  const {
 40    hostsFilters: filters,
 41    selectedInbound,
 42    onEditingHost,
 43    onCreatingHost,
 44    onHostsFilterChange,
 45    refetchHosts,
 46  } = useInbounds();
 47  const { data: hosts } = useQuery('hosts', () => {
 48    if (selectedInbound !== null) {
 49      return fetchInboundHosts(selectedInbound.id);
 50    }
 51  });
 52
 53  useEffect(() => { refetchHosts() }, [selectedInbound]);
 54
 55  const { t } = useTranslation();
 56  const [selectedRow, setSelectedRow] = useState<ExpandedIndex | undefined>(
 57    undefined
 58  );
 59
 60  const useTable = useBreakpointValue({ base: false, md: true });
 61
 62  // TODO: Find a different mechancism to to detect ftiler
 63  // const isFiltered = users.length !== total;
 64  const toggleAccordion = (index: number) => {
 65    setSelectedRow(index === selectedRow ? undefined : index);
 66  };
 67
 68  return (
 69    <Box id="hosts-table" borderTopStartRadius="0" overflowX={{ base: 'unset', md: 'unset' }} >
 70      <Accordion
 71        allowMultiple
 72        display={{ base: 'block', md: 'none' }}
 73        index={selectedRow}
 74      >
 75        <Table orientation="vertical" zIndex="docked" {...props}>
 76          <Thead zIndex="docked" position="relative">
 77            <Tr>
 78              <Th
 79                minW="220px"
 80                pl={4}
 81                pr={4}
 82                cursor={'pointer'}
 83                onClick={handleSort.bind(null, filters, 'remark', onHostsFilterChange)}
 84              >
 85                <HStack>
 86                  <span>{t('remark')}</span>
 87                  <Sort sort={filters.sort} column="remark" />
 88                </HStack>
 89              </Th>
 90              <Th
 91                minW="100px"
 92                cursor={'pointer'}
 93                pr={0}
 94                onClick={handleSort.bind(null, filters, 'address', onHostsFilterChange)}
 95              >
 96                <HStack>
 97                  <span>{t('hostsTable.address')}</span>
 98                  <Sort sort={filters.sort} column="address" />
 99                </HStack>
100              </Th>
101              <Th
102                minW="100px"
103                cursor={'pointer'}
104                pr={0}
105                onClick={handleSort.bind(null, filters, 'port', onHostsFilterChange)}
106              >
107                <HStack>
108                  <span>{t('hostsTable.port')}</span>
109                  <Sort sort={filters.sort} column="port" />
110                </HStack>
111              </Th>
112              {/* Action buttons (certificate, core settings)*/}
113            </Tr>
114          </Thead>
115          <Tbody>
116            {!useTable && hosts?.map((host, i) => {
117              return (
118                <Fragment key={i}>
119                  <Tr
120                    onClick={toggleAccordion.bind(null, i)}
121                    cursor="pointer"
122                  >
123                    <Td borderBottom={0} minW="100px" pl={4} pr={4}>
124                      {host.remark}
125                    </Td>
126                    <Td borderBottom={0} minW="100px" pr={0}>
127                      {host.address}
128                    </Td>
129                    <Td p={0} borderBottom={0} w="32px" minW="32px">
130                      <AccordionArrowIcon
131                        color="gray.600"
132                        _dark={{
133                          color: 'gray.400',
134                        }}
135                        transition="transform .2s ease-out"
136                        transform={
137                          selectedRow === i ? 'rotate(180deg)' : '0deg'
138                        }
139                      />
140                    </Td>
141                  </Tr>
142                  <Tr
143                    className="collapsible"
144                    onClick={toggleAccordion.bind(null, i)}
145                  >
146                    <Td p={0} colSpan={4}>
147                      <AccordionItem border={0}>
148                        <AccordionButton display="none"></AccordionButton>
149                        <AccordionPanel
150                          border={0}
151                          cursor="pointer"
152                          px={6}
153                          py={3}
154                        >
155                          <VStack justifyContent="space-between" spacing="4">
156                            <VStack
157                              alignItems="flex-start"
158                              w="full"
159                              spacing={-1}
160                            >
161                              <Text
162                                textTransform="capitalize"
163                                fontSize="xs"
164                                fontWeight="bold"
165                                color="gray.600"
166                                _dark={{
167                                  color: 'gray.400',
168                                }}
169                              >
170                                {t('nodesTable.ipPort')}
171                              </Text>
172                              <Box width="full" minW="230px">
173                                {host.port}
174                              </Box>
175                            </VStack>
176                            <HStack w="full" justifyContent="space-between">
177                              <HStack>
178                                <Tooltip
179                                  label={t('nodesDialog.editNode')}
180                                  placement="top"
181                                >
182                                  <IconButton
183                                    p="0 !important"
184                                    aria-label="Edit user"
185                                    bg="transparent"
186                                    _dark={{
187                                      _hover: {
188                                        bg: 'gray.700',
189                                      },
190                                    }}
191                                    size={{
192                                      base: 'sm',
193                                      md: 'md',
194                                    }}
195                                    onClick={(e) => {
196                                      e.stopPropagation();
197                                      onEditingHost(true, host);
198                                    }}
199                                  >
200                                    <EditIcon />
201                                  </IconButton>
202                                </Tooltip>
203                              </HStack>
204                            </HStack>
205                          </VStack>
206                        </AccordionPanel>
207                      </AccordionItem>
208                    </Td>
209                  </Tr>
210                </Fragment>
211              );
212            })}
213          </Tbody>
214        </Table>
215      </Accordion>
216      <Table
217        orientation="vertical"
218        display={{ base: 'none', md: 'table' }}
219        {...props}
220      >
221        <Thead zIndex="docked" position="relative">
222          <Tr>
223            <Th
224              minW="120px"
225              pl={4}
226              pr={4}
227              cursor={'pointer'}
228              onClick={handleSort.bind(null, filters, 'remark', onHostsFilterChange)}
229            >
230              <HStack>
231                <span>{t('remark')}</span>
232                <Sort sort={filters.sort} column="remark" />
233              </HStack>
234            </Th>
235            <Th
236              minW="100px"
237              cursor={'pointer'}
238              pr={0}
239              onClick={handleSort.bind(null, filters, 'address', onHostsFilterChange)}
240            >
241              <HStack>
242                <span>{t('hostsTable.address')}</span>
243                <Sort sort={filters.sort} column="address" />
244              </HStack>
245            </Th>
246            <Th
247              minW="100px"
248              cursor={'pointer'}
249              pr={0}
250              onClick={handleSort.bind(null, filters, 'port', onHostsFilterChange)}
251            >
252              <HStack>
253                <span>{t('hostsTable.port')}</span>
254                <Sort sort={filters.sort} column="port" />
255              </HStack>
256            </Th>
257          </Tr>
258        </Thead>
259        <Tbody overflowY="auto">
260          {useTable && hosts?.map((host, i) => {
261            return (
262              <Tr
263                key={i}
264                className={classNames('interactive', {
265                  'last-row': i === hosts.length - 1,
266                })}
267                onClick={() => onEditingHost(true, host)}
268              >
269                <Td minW="140px">
270                  <div className="flex-status">
271                    {host.remark}
272                  </div>
273                </Td>
274                <Td width="400px" minW="150px">
275                  {host.address}
276                </Td>
277                <Td width="350px" minW="150px">
278                  {host.port}
279                </Td>
280              </Tr>
281            );
282          })}
283          {(hosts?.length === 0 || hosts === undefined) && (
284            <Tr>
285              <Td colSpan={5}>
286                <EmptySection
287                  isFiltered={/* isFiltered */ false}
288                  noObjectT="hostsTable.noHost"
289                  noObjectMatchedT='hostsTable.noHostMatched'
290                  createObjectT="addNode"
291                  onCreateObject={onCreatingHost}
292                />
293              </Td>
294            </Tr>
295          )}
296        </Tbody>
297      </Table>
298      {/* TODO:
299        * <Pagination filters={filters} total={hosts === undefined ? 0 : hosts.length} onHostsFilterChange={onFilterChange} pageSizeManager={pageSizeManagers.hosts} /> 
300        */}
301    </Box>
302  );
303};