mirror of
				https://github.com/Telecominfraproject/wlan-cloud-ucentralgw-ui.git
				synced 2025-10-31 02:38:01 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			364 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			364 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| /* eslint-disable jsx-a11y/no-static-element-interactions */
 | |
| /* eslint-disable jsx-a11y/click-events-have-key-events */
 | |
| import React, { useEffect, useState } from 'react';
 | |
| import { ArrowRightIcon, ArrowLeftIcon, ChevronRightIcon, ChevronLeftIcon } from '@chakra-ui/icons';
 | |
| import {
 | |
|   Table,
 | |
|   Tbody,
 | |
|   Td,
 | |
|   Th,
 | |
|   Thead,
 | |
|   Tooltip,
 | |
|   Tr,
 | |
|   Flex,
 | |
|   IconButton,
 | |
|   Text,
 | |
|   Select,
 | |
|   NumberInput,
 | |
|   NumberInputField,
 | |
|   NumberInputStepper,
 | |
|   NumberIncrementStepper,
 | |
|   NumberDecrementStepper,
 | |
|   useColorModeValue,
 | |
|   Box,
 | |
|   Center,
 | |
|   Spinner,
 | |
|   Heading,
 | |
|   useBreakpoint,
 | |
| } from '@chakra-ui/react';
 | |
| // @ts-ignore
 | |
| import { useTranslation } from 'react-i18next';
 | |
| import {
 | |
|   useTable,
 | |
|   usePagination,
 | |
|   useSortBy,
 | |
|   Row,
 | |
|   UsePaginationInstanceProps,
 | |
|   UseSortByInstanceProps,
 | |
|   UsePaginationState,
 | |
|   TableInstance,
 | |
| } from 'react-table';
 | |
| import { v4 as uuid } from 'uuid';
 | |
| import SortIcon from './SortIcon';
 | |
| import { isColumnSorted, isSortedDesc, onSortClick } from './utils';
 | |
| import { LoadingOverlay } from 'components/LoadingOverlay';
 | |
| import { Column, PageInfo, SortInfo } from 'models/Table';
 | |
| 
 | |
| interface Props {
 | |
|   columns: Column<object>[];
 | |
|   data: object[];
 | |
|   count?: number;
 | |
|   setPageInfo?: React.Dispatch<React.SetStateAction<PageInfo | undefined>>;
 | |
|   sortInfo: SortInfo;
 | |
|   setSortInfo: React.Dispatch<React.SetStateAction<SortInfo>>;
 | |
|   isLoading?: boolean;
 | |
|   obj: string;
 | |
|   sortBy?: { id: string; desc: boolean }[];
 | |
|   hiddenColumns?: string[];
 | |
|   hideControls?: boolean;
 | |
|   minHeight?: string;
 | |
|   fullScreen?: boolean;
 | |
|   isManual?: boolean;
 | |
|   saveSettingsId?: string;
 | |
| }
 | |
| 
 | |
| type TableInstanceWithHooks<T extends object> = TableInstance<T> &
 | |
|   UsePaginationInstanceProps<T> &
 | |
|   UseSortByInstanceProps<T> & {
 | |
|     state: UsePaginationState<T>;
 | |
|   };
 | |
| 
 | |
| const defaultProps = {
 | |
|   count: undefined,
 | |
|   setPageInfo: undefined,
 | |
|   isLoading: false,
 | |
|   minHeight: undefined,
 | |
|   fullScreen: false,
 | |
|   sortBy: [],
 | |
|   hiddenColumns: [],
 | |
|   hideControls: false,
 | |
|   isManual: false,
 | |
|   saveSettingsId: undefined,
 | |
| };
 | |
| 
 | |
| const SortableDataTable: React.FC<Props> = ({
 | |
|   columns,
 | |
|   data,
 | |
|   isLoading,
 | |
|   obj,
 | |
|   minHeight,
 | |
|   fullScreen,
 | |
|   sortBy,
 | |
|   hiddenColumns,
 | |
|   hideControls,
 | |
|   count,
 | |
|   sortInfo,
 | |
|   setSortInfo,
 | |
|   setPageInfo,
 | |
|   isManual,
 | |
|   saveSettingsId,
 | |
| }) => {
 | |
|   const { t } = useTranslation();
 | |
|   const breakpoint = useBreakpoint();
 | |
|   const textColor = useColorModeValue('gray.700', 'white');
 | |
|   const getPageSize = () => {
 | |
|     const saved = saveSettingsId ? localStorage.getItem(saveSettingsId) : undefined;
 | |
|     if (saved) return Number.parseInt(saved, 10);
 | |
|     return 10;
 | |
|   };
 | |
|   const [queryPageSize, setQueryPageSize] = useState(getPageSize());
 | |
| 
 | |
|   const {
 | |
|     getTableProps,
 | |
|     getTableBodyProps,
 | |
|     headerGroups,
 | |
|     prepareRow,
 | |
|     page,
 | |
|     canPreviousPage,
 | |
|     canNextPage,
 | |
|     pageOptions,
 | |
|     pageCount,
 | |
|     gotoPage,
 | |
|     nextPage,
 | |
|     previousPage,
 | |
|     setPageSize,
 | |
|     setHiddenColumns,
 | |
|     state: { pageIndex, pageSize },
 | |
|   } = useTable(
 | |
|     {
 | |
|       // @ts-ignore
 | |
|       columns,
 | |
|       data, // @ts-ignore
 | |
|       initialState: { sortBy, pagination: !hideControls, pageSize: queryPageSize },
 | |
|       manualPagination: isManual,
 | |
|       pageCount: isManual && count !== undefined ? Math.ceil(count / queryPageSize) : undefined,
 | |
|     },
 | |
|     useSortBy,
 | |
|     usePagination,
 | |
|   ) as TableInstanceWithHooks<object>;
 | |
| 
 | |
|   useEffect(() => {
 | |
|     if (setPageInfo && pageIndex !== undefined) setPageInfo({ index: pageIndex, limit: queryPageSize });
 | |
|   }, [queryPageSize, pageIndex]);
 | |
| 
 | |
|   useEffect(() => {
 | |
|     // @ts-ignore
 | |
|     if (saveSettingsId) localStorage.setItem(saveSettingsId, pageSize);
 | |
|     setQueryPageSize(pageSize);
 | |
|   }, [pageSize]);
 | |
| 
 | |
|   useEffect(() => {
 | |
|     if (hiddenColumns) setHiddenColumns(hiddenColumns);
 | |
|   }, [hiddenColumns]);
 | |
| 
 | |
|   // If this is a manual DataTable, with a page index that is higher than 0 and higher than the max possible page, we send to index 0
 | |
|   useEffect(() => {
 | |
|     if (
 | |
|       isManual &&
 | |
|       setPageInfo &&
 | |
|       data &&
 | |
|       isManual &&
 | |
|       pageIndex > 0 &&
 | |
|       count !== undefined &&
 | |
|       Math.ceil(count / queryPageSize) - 1 < pageIndex
 | |
|     ) {
 | |
|       gotoPage(0);
 | |
|       setPageInfo({ index: 0, limit: queryPageSize });
 | |
|     }
 | |
|   }, [count, queryPageSize, page, data]);
 | |
| 
 | |
|   const computedMinHeight = () => {
 | |
|     if (fullScreen) return { base: 'calc(100vh - 360px)', md: 'calc(100vh - 288px)' };
 | |
|     return minHeight;
 | |
|   };
 | |
| 
 | |
|   if (isLoading && data.length === 0) {
 | |
|     return (
 | |
|       <Center>
 | |
|         <Spinner size="xl" />
 | |
|       </Center>
 | |
|     );
 | |
|   }
 | |
|   // Render the UI for your table
 | |
|   return (
 | |
|     <>
 | |
|       <Box minHeight={computedMinHeight()} position="relative">
 | |
|         <LoadingOverlay isLoading={isManual !== undefined && isManual && isLoading !== undefined && isLoading}>
 | |
|           <Table {...getTableProps()} size="small" textColor={textColor} w="100%">
 | |
|             <Thead fontSize="14px">
 | |
|               {headerGroups.map((group) => (
 | |
|                 <Tr {...group.getHeaderGroupProps()}>
 | |
|                   {group.headers.map((column) => (
 | |
|                     <Th
 | |
|                       color="gray.400"
 | |
|                       {...column.getHeaderProps()}
 | |
|                       // @ts-ignore
 | |
|                       minWidth={column.customMinWidth ?? null}
 | |
|                       // @ts-ignore
 | |
|                       maxWidth={column.customMaxWidth ?? null}
 | |
|                       // @ts-ignore
 | |
|                       width={column.customWidth ?? null}
 | |
|                     >
 | |
|                       <div
 | |
|                         onClick={() => onSortClick(column.id, sortInfo, setSortInfo)}
 | |
|                         style={{ alignContent: 'center', overflow: 'hidden', whiteSpace: 'nowrap' }}
 | |
|                       >
 | |
|                         {column.render('Header')}
 | |
|                         <SortIcon
 | |
|                           // @ts-ignore
 | |
|                           isSorted={isColumnSorted(column.id, sortInfo)}
 | |
|                           // @ts-ignore
 | |
|                           isSortedDesc={isSortedDesc(column.id, sortInfo)}
 | |
|                           // @ts-ignore
 | |
|                           canSort={column.canSort}
 | |
|                         />
 | |
|                       </div>
 | |
|                     </Th>
 | |
|                   ))}
 | |
|                 </Tr>
 | |
|               ))}
 | |
|             </Thead>
 | |
|             {data.length > 0 && (
 | |
|               <Tbody {...getTableBodyProps()}>
 | |
|                 {page.map((row: Row) => {
 | |
|                   prepareRow(row);
 | |
|                   return (
 | |
|                     <Tr {...row.getRowProps()} key={uuid()}>
 | |
|                       {
 | |
|                         // @ts-ignore
 | |
|                         row.cells.map((cell) => (
 | |
|                           <Td
 | |
|                             key={uuid()}
 | |
|                             px={1}
 | |
|                             // @ts-ignore
 | |
|                             minWidth={cell.column.customMinWidth ?? undefined}
 | |
|                             // @ts-ignore
 | |
|                             maxWidth={cell.column.customMaxWidth ?? undefined}
 | |
|                             // @ts-ignore
 | |
|                             width={cell.column.customWidth ?? undefined}
 | |
|                             textOverflow="ellipsis"
 | |
|                             overflow="hidden"
 | |
|                             whiteSpace="nowrap"
 | |
|                             fontSize="14px"
 | |
|                             // @ts-ignore
 | |
|                             textAlign={cell.column.isCentered ? 'center' : undefined}
 | |
|                             // @ts-ignore
 | |
|                             fontFamily={cell.column.isMonospace ? 'monospace' : undefined}
 | |
|                           >
 | |
|                             {cell.render('Cell')}
 | |
|                           </Td>
 | |
|                         ))
 | |
|                       }
 | |
|                     </Tr>
 | |
|                   );
 | |
|                 })}
 | |
|               </Tbody>
 | |
|             )}
 | |
|           </Table>
 | |
|           {!isLoading && data.length === 0 && (
 | |
|             <Center>
 | |
|               <Heading pt={12}>{t('common.no_obj_found', { obj })}</Heading>
 | |
|             </Center>
 | |
|           )}
 | |
|         </LoadingOverlay>
 | |
|       </Box>
 | |
|       {!hideControls && (
 | |
|         <Flex justifyContent="space-between" m={4} alignItems="center">
 | |
|           <Flex>
 | |
|             <Tooltip label={t('table.first_page')}>
 | |
|               <IconButton
 | |
|                 aria-label="Go to first page"
 | |
|                 onClick={() => gotoPage(0)}
 | |
|                 isDisabled={!canPreviousPage}
 | |
|                 icon={<ArrowLeftIcon h={3} w={3} />}
 | |
|                 mr={4}
 | |
|               />
 | |
|             </Tooltip>
 | |
|             <Tooltip label={t('table.previous_page')}>
 | |
|               <IconButton
 | |
|                 aria-label="Previous page"
 | |
|                 onClick={previousPage}
 | |
|                 isDisabled={!canPreviousPage}
 | |
|                 icon={<ChevronLeftIcon h={6} w={6} />}
 | |
|               />
 | |
|             </Tooltip>
 | |
|           </Flex>
 | |
| 
 | |
|           <Flex alignItems="center">
 | |
|             {breakpoint !== 'base' && (
 | |
|               <>
 | |
|                 <Text flexShrink={0} mr={8}>
 | |
|                   {t('table.page')}{' '}
 | |
|                   <Text fontWeight="bold" as="span">
 | |
|                     {pageIndex + 1}
 | |
|                   </Text>{' '}
 | |
|                   {t('common.of')}{' '}
 | |
|                   <Text fontWeight="bold" as="span">
 | |
|                     {pageOptions.length}
 | |
|                   </Text>
 | |
|                 </Text>
 | |
|                 <Text flexShrink={0}>{t('table.go_to_page')}</Text>{' '}
 | |
|                 <NumberInput
 | |
|                   ml={2}
 | |
|                   mr={8}
 | |
|                   w={28}
 | |
|                   min={1}
 | |
|                   max={pageOptions.length}
 | |
|                   onChange={(_: unknown, numberValue: number) => {
 | |
|                     const newPage = numberValue ? numberValue - 1 : 0;
 | |
|                     gotoPage(newPage);
 | |
|                   }}
 | |
|                   defaultValue={pageIndex + 1}
 | |
|                 >
 | |
|                   <NumberInputField />
 | |
|                   <NumberInputStepper>
 | |
|                     <NumberIncrementStepper />
 | |
|                     <NumberDecrementStepper />
 | |
|                   </NumberInputStepper>
 | |
|                 </NumberInput>
 | |
|               </>
 | |
|             )}
 | |
|             <Select
 | |
|               w={32}
 | |
|               value={pageSize}
 | |
|               onChange={(e: React.ChangeEvent<HTMLSelectElement>) => {
 | |
|                 setPageSize(Number(e.target.value));
 | |
|               }}
 | |
|             >
 | |
|               {[10, 20, 30, 40, 50].map((opt) => (
 | |
|                 <option key={uuid()} value={opt}>
 | |
|                   {t('common.show')} {opt}
 | |
|                 </option>
 | |
|               ))}
 | |
|             </Select>
 | |
|           </Flex>
 | |
| 
 | |
|           <Flex>
 | |
|             <Tooltip label={t('table.next_page')}>
 | |
|               <IconButton
 | |
|                 aria-label="Go to next page"
 | |
|                 onClick={nextPage}
 | |
|                 isDisabled={!canNextPage}
 | |
|                 icon={<ChevronRightIcon h={6} w={6} />}
 | |
|               />
 | |
|             </Tooltip>
 | |
|             <Tooltip label={t('table.last_page')}>
 | |
|               <IconButton
 | |
|                 aria-label="Go to last page"
 | |
|                 onClick={() => gotoPage(pageCount - 1)}
 | |
|                 isDisabled={!canNextPage}
 | |
|                 icon={<ArrowRightIcon h={3} w={3} />}
 | |
|                 ml={4}
 | |
|               />
 | |
|             </Tooltip>
 | |
|           </Flex>
 | |
|         </Flex>
 | |
|       )}
 | |
|     </>
 | |
|   );
 | |
| };
 | |
| 
 | |
| SortableDataTable.defaultProps = defaultProps;
 | |
| 
 | |
| export default SortableDataTable;
 | 
