import PropTypes from "prop-types"; import { useState, useEffect } from "react"; import { useTheme } from "@emotion/react"; import { TableContainer, Paper, Table, TableHead, TableRow, TableCell, TableBody, TablePagination, Box, Typography, Stack, Button, } from "@mui/material"; import { useDispatch, useSelector } from "react-redux"; import { setRowsPerPage } from "../../Features/UI/uiSlice"; import LeftArrowDouble from "../../assets/icons/left-arrow-double.svg?react"; import RightArrowDouble from "../../assets/icons/right-arrow-double.svg?react"; import LeftArrow from "../../assets/icons/left-arrow.svg?react"; import RightArrow from "../../assets/icons/right-arrow.svg?react"; import SelectorVertical from "../../assets/icons/selector-vertical.svg?react"; import "./index.css"; /** * Component for pagination actions (first, previous, next, last). * * @component * @param {Object} props * @param {number} props.count - Total number of items. * @param {number} props.page - Current page number. * @param {number} props.rowsPerPage - Number of rows per page. * @param {function} props.onPageChange - Callback function to handle page change. * * @returns {JSX.Element} Pagination actions component. */ const TablePaginationActions = (props) => { const { count, page, rowsPerPage, onPageChange } = props; const handleFirstPageButtonClick = (event) => { onPageChange(event, 0); }; const handleBackButtonClick = (event) => { onPageChange(event, page - 1); }; const handleNextButtonClick = (event) => { onPageChange(event, page + 1); }; const handleLastPageButtonClick = (event) => { onPageChange(event, Math.max(0, Math.ceil(count / rowsPerPage) - 1)); }; return ( ); }; TablePaginationActions.propTypes = { count: PropTypes.number.isRequired, page: PropTypes.number.isRequired, rowsPerPage: PropTypes.number.isRequired, onPageChange: PropTypes.func.isRequired, }; /** * BasicTable Component * Renders a table with optional pagination. * * @component * @param {Object} props - Component props. * @param {Object} props.data - Data for the table including columns and rows. * @param {Array} props.data.cols - Array of objects for column headers. * @param {number} props.data.cols[].id - Unique identifier for the column. * @param {string} props.data.cols[].name - Name of the column to display as header. * @param {Array} props.data.rows - Array of row objects. * @param {number} props.data.rows[].id - Unique identifier for the row. * @param {Array} props.data.rows[].data - Array of cell data objects for the row. * @param {number} props.data.rows[].data[].id - Unique identifier for the cell. * @param {JSX.Element} props.data.rows[].data[].data - The content to display in the cell. * @param {function} props.data.rows.data.handleClick - Function to call when the row is clicked. * @param {boolean} [props.paginated=false] - Flag to enable pagination. * @param {boolean} [props.reversed=false] - Flag to enable reverse order. * @param {number} props.rowsPerPage- Number of rows per page (table). * * @example * const data = { * cols: [ * { id: 1, name: "First Col" }, * { id: 2, name: "Second Col" }, * { id: 3, name: "Third Col" }, * { id: 4, name: "Fourth Col" }, * ], * rows: [ * { * id: 1, * data: [ * { id: 1, data:
Data for Row 1 Col 1
}, * { id: 2, data:
Data for Row 1 Col 2
}, * { id: 3, data:
Data for Row 1 Col 3
}, * { id: 4, data:
Data for Row 1 Col 4
}, * ], * }, * { * id: 2, * data: [ * { id: 5, data:
Data for Row 2 Col 1
}, * { id: 6, data:
Data for Row 2 Col 2
}, * { id: 7, data:
Data for Row 2 Col 3
}, * { id: 8, data:
Data for Row 2 Col 4
}, * ], * }, * ], * }; * * */ const BasicTable = ({ data, paginated, reversed, table }) => { const DEFAULT_ROWS_PER_PAGE = 5; const theme = useTheme(); const dispatch = useDispatch(); const uiState = useSelector((state) => state.ui); let rowsPerPage = uiState?.[table]?.rowsPerPage ?? DEFAULT_ROWS_PER_PAGE; const [page, setPage] = useState(0); useEffect(() => { setPage(0); }, [data]); const handleChangePage = (event, newPage) => { setPage(newPage); }; const handleChangeRowsPerPage = (event) => { dispatch( setRowsPerPage({ value: parseInt(event.target.value, 10), table: table, }) ); setPage(0); }; let displayData = []; if (data && data.rows) { let rows = reversed ? [...data.rows].reverse() : data.rows; displayData = paginated ? rows.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) : rows; } if (!data || !data.cols || !data.rows) { return
No data
; } /** * Helper function to calculate the range of displayed rows. * @returns {string} */ const getRange = () => { let start = page * rowsPerPage + 1; let end = Math.min(page * rowsPerPage + rowsPerPage, data.rows.length); return `${start} - ${end}`; }; return ( <> {data.cols.map((col) => ( {col.name} ))} {displayData.map((row) => { return ( {row.data.map((cell) => { return {cell.data}; })} ); })}
{paginated && ( Showing {getRange()} of {data.rows.length} monitor(s) `Page ${page + 1} of ${Math.max(0, Math.ceil(count / rowsPerPage))}` } slotProps={{ select: { MenuProps: { keepMounted: true, PaperProps: { className: "pagination-dropdown", sx: { mt: 0, mb: theme.spacing(2), }, }, transformOrigin: { vertical: "bottom", horizontal: "left" }, anchorOrigin: { vertical: "top", horizontal: "left" }, sx: { mt: theme.spacing(-2) }, }, inputProps: { id: "pagination-dropdown" }, IconComponent: SelectorVertical, sx: { ml: theme.spacing(4), mr: theme.spacing(12), minWidth: theme.spacing(20), textAlign: "left", "&.Mui-focused > div": { backgroundColor: theme.palette.background.main, }, }, }, }} sx={{ mt: theme.spacing(6), color: theme.palette.text.secondary, "& svg path": { stroke: theme.palette.text.tertiary, strokeWidth: 1.3, }, "& .MuiSelect-select": { border: 1, borderColor: theme.palette.border.light, borderRadius: theme.shape.borderRadius, }, }} /> )} ); }; BasicTable.propTypes = { data: PropTypes.object.isRequired, paginated: PropTypes.bool, reversed: PropTypes.bool, rowPerPage: PropTypes.number, table: PropTypes.string, }; export default BasicTable;