mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-05-03 15:09:34 -05:00
feat: abstracting table pagination
This commit is contained in:
@@ -40,7 +40,7 @@ export const checkInfrastructureEndpointResolution = createAsyncThunk(
|
||||
const res = await networkService.checkEndpointResolution({
|
||||
authToken: authToken,
|
||||
monitorURL: monitorURL,
|
||||
})
|
||||
});
|
||||
return res.data;
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) {
|
||||
@@ -53,7 +53,7 @@ export const checkInfrastructureEndpointResolution = createAsyncThunk(
|
||||
return thunkApi.rejectWithValue(payload);
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
export const getInfrastructureMonitorById = createAsyncThunk(
|
||||
"infrastructureMonitors/getMonitorById",
|
||||
@@ -87,6 +87,7 @@ export const getInfrastructureMonitorsByTeamId = createAsyncThunk(
|
||||
authToken: token,
|
||||
teamId: user.teamId,
|
||||
types: ["hardware"],
|
||||
limit: 1,
|
||||
});
|
||||
return res.data;
|
||||
} catch (error) {
|
||||
@@ -112,7 +113,7 @@ export const updateInfrastructureMonitor = createAsyncThunk(
|
||||
description: monitor.description,
|
||||
interval: monitor.interval,
|
||||
notifications: monitor.notifications,
|
||||
threshold: monitor.threshold
|
||||
threshold: monitor.threshold,
|
||||
};
|
||||
const res = await networkService.updateMonitor({
|
||||
authToken: authToken,
|
||||
@@ -307,7 +308,9 @@ const infrastructureMonitorsSlice = createSlice({
|
||||
.addCase(getInfrastructureMonitorById.rejected, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.success = false;
|
||||
state.msg = action.payload ? action.payload.msg : "Failed to get infrastructure monitor";
|
||||
state.msg = action.payload
|
||||
? action.payload.msg
|
||||
: "Failed to get infrastructure monitor";
|
||||
})
|
||||
// *****************************************************
|
||||
// update Monitor
|
||||
@@ -401,6 +404,7 @@ const infrastructureMonitorsSlice = createSlice({
|
||||
},
|
||||
});
|
||||
|
||||
export const { setInfrastructureMonitors, clearInfrastructureMonitorState } = infrastructureMonitorsSlice.actions;
|
||||
export const { setInfrastructureMonitors, clearInfrastructureMonitorState } =
|
||||
infrastructureMonitorsSlice.actions;
|
||||
|
||||
export default infrastructureMonitorsSlice.reducer;
|
||||
|
||||
@@ -221,6 +221,7 @@ export const addDemoMonitors = createAsyncThunk(
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export const deleteAllMonitors = createAsyncThunk(
|
||||
"monitors/deleteAllMonitors",
|
||||
async (data, thunkApi) => {
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
import PropTypes from "prop-types";
|
||||
import { Box, Button } from "@mui/material";
|
||||
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";
|
||||
|
||||
TablePaginationActions.propTypes = {
|
||||
count: PropTypes.number.isRequired,
|
||||
page: PropTypes.number.isRequired,
|
||||
rowsPerPage: PropTypes.number.isRequired,
|
||||
onPageChange: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
function TablePaginationActions({ count, page, rowsPerPage, onPageChange }) {
|
||||
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 (
|
||||
<Box sx={{ flexShrink: 0, ml: "24px" }}>
|
||||
<Button
|
||||
variant="group"
|
||||
onClick={handleFirstPageButtonClick}
|
||||
disabled={page === 0}
|
||||
aria-label="first page"
|
||||
>
|
||||
<LeftArrowDouble />
|
||||
</Button>
|
||||
<Button
|
||||
variant="group"
|
||||
onClick={handleBackButtonClick}
|
||||
disabled={page === 0}
|
||||
aria-label="previous page"
|
||||
>
|
||||
<LeftArrow />
|
||||
</Button>
|
||||
<Button
|
||||
variant="group"
|
||||
onClick={handleNextButtonClick}
|
||||
disabled={page >= Math.ceil(count / rowsPerPage) - 1}
|
||||
aria-label="next page"
|
||||
>
|
||||
<RightArrow />
|
||||
</Button>
|
||||
<Button
|
||||
variant="group"
|
||||
onClick={handleLastPageButtonClick}
|
||||
disabled={page >= Math.ceil(count / rowsPerPage) - 1}
|
||||
aria-label="last page"
|
||||
>
|
||||
<RightArrowDouble />
|
||||
</Button>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export { TablePaginationActions };
|
||||
@@ -0,0 +1,117 @@
|
||||
import PropTypes from "prop-types";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { Stack, TablePagination, Typography } from "@mui/material";
|
||||
import { TablePaginationActions } from "./Actions";
|
||||
import SelectorVertical from "../../../../Assets/icons/selector-vertical.svg?react";
|
||||
|
||||
Pagination.propTypes = {
|
||||
monitorCount: PropTypes.number.isRequired, // Total number of items for pagination.
|
||||
page: PropTypes.number.isRequired, // Current page index.
|
||||
rowsPerPage: PropTypes.number.isRequired, // Number of rows displayed per page.
|
||||
handleChangePage: PropTypes.func.isRequired, // Function to handle page changes.
|
||||
handleChangeRowsPerPage: PropTypes.func.isRequired, // Function to handle changes in rows per page.
|
||||
};
|
||||
|
||||
const ROWS_PER_PAGE_OPTIONS = [5, 10, 15, 25];
|
||||
|
||||
/**
|
||||
* Pagination component for table navigation with customized styling and behavior.
|
||||
*
|
||||
* @param {object} props - Component properties.
|
||||
* @param {number} props.monitorCount - Total number of monitors to paginate.
|
||||
* @param {number} props.page - Current page index (0-based).
|
||||
* @param {number} props.rowsPerPage - Number of rows to display per page.
|
||||
* @param {function} props.handleChangePage - Callback for handling page changes.
|
||||
* @param {function} props.handleChangeRowsPerPage - Callback for handling changes to rows per page.
|
||||
* @returns {JSX.Element} The Pagination component.
|
||||
*/
|
||||
function Pagination({
|
||||
monitorCount,
|
||||
page,
|
||||
rowsPerPage,
|
||||
handleChangePage,
|
||||
handleChangeRowsPerPage,
|
||||
}) {
|
||||
const theme = useTheme();
|
||||
|
||||
const start = page * rowsPerPage + 1;
|
||||
const end = Math.min(page * rowsPerPage + rowsPerPage, monitorCount);
|
||||
const range = `${start} - ${end}`;
|
||||
|
||||
return (
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
px={theme.spacing(4)}
|
||||
marginTop={8}
|
||||
>
|
||||
<Typography
|
||||
px={theme.spacing(2)}
|
||||
variant="body2"
|
||||
sx={{ opacity: 0.7 }}
|
||||
>
|
||||
Showing {range} of {monitorCount} monitor(s)
|
||||
</Typography>
|
||||
<TablePagination
|
||||
component="div"
|
||||
count={monitorCount}
|
||||
page={page}
|
||||
onPageChange={handleChangePage}
|
||||
rowsPerPage={rowsPerPage}
|
||||
rowsPerPageOptions={ROWS_PER_PAGE_OPTIONS}
|
||||
onRowsPerPageChange={handleChangeRowsPerPage}
|
||||
ActionsComponent={TablePaginationActions}
|
||||
labelRowsPerPage="Rows per page"
|
||||
labelDisplayedRows={({ page, count }) =>
|
||||
`Page ${page + 1} of ${Math.max(0, Math.ceil(count / rowsPerPage))}`
|
||||
}
|
||||
slotProps={{
|
||||
select: {
|
||||
MenuProps: {
|
||||
keepMounted: true,
|
||||
disableScrollLock: 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={{
|
||||
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,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
export { Pagination };
|
||||
@@ -13,27 +13,22 @@ import {
|
||||
// TablePagination,
|
||||
// Typography,
|
||||
} from "@mui/material";
|
||||
import { Heading } from "../../Components/Heading";
|
||||
|
||||
import { useEffect } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import Greeting from "../../Utils/greeting";
|
||||
import Breadcrumbs from "../../Components/Breadcrumbs";
|
||||
import { StatusLabel } from "../../Components/Label";
|
||||
import { Gauge } from "../../Components/Charts/Gauge";
|
||||
import GearIcon from "../../Assets/icons/settings-bold.svg?react";
|
||||
import CPUChipIcon from "../../Assets/icons/cpu-chip.svg?react";
|
||||
import Breadcrumbs from "../../Components/Breadcrumbs";
|
||||
import { StatusLabel } from "../../Components/Label";
|
||||
import { Heading } from "../../Components/Heading";
|
||||
import { Gauge } from "../../Components/Charts/Gauge";
|
||||
import { getInfrastructureMonitorsByTeamId } from "../../Features/InfrastructureMonitors/infrastructureMonitorsSlice";
|
||||
import useUtils from "../Monitors/utils";
|
||||
import { Pagination } from "./components/TablePagination";
|
||||
|
||||
const mockedData = {
|
||||
ip: "https://192.168.1.30",
|
||||
status: "up",
|
||||
processor: "2Ghz",
|
||||
cpu: 80,
|
||||
mem: 50,
|
||||
disk: 70,
|
||||
};
|
||||
|
||||
const ROWS = Array.from(Array(20).keys()).map(() => mockedData);
|
||||
console.log(ROWS);
|
||||
// const ROWS = Array.from(Array(20).keys()).map(() => mockedData);
|
||||
|
||||
const columns = [
|
||||
{ label: "Host" },
|
||||
@@ -69,8 +64,6 @@ Apply to Monitor Table, and Account/Team.
|
||||
Analyze existing BasicTable
|
||||
*/
|
||||
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
/**
|
||||
* This is the Infrastructure monitoring page. This is a work in progress
|
||||
*
|
||||
@@ -78,9 +71,33 @@ import { useNavigate } from "react-router-dom";
|
||||
* @returns {JSX.Element} The infrastructure monitoring page.
|
||||
*/
|
||||
|
||||
function Infrastructure() {
|
||||
function Infrastructure(/* {isAdmin} */) {
|
||||
const theme = useTheme();
|
||||
const { determineState } = useUtils();
|
||||
const navigate = useNavigate();
|
||||
const dispatch = useDispatch();
|
||||
const { authToken } = useSelector((state) => state.auth);
|
||||
const { isLoading, monitorsSummary, msg, success, ...rest } = useSelector(
|
||||
(state) => state.infrastructureMonitors
|
||||
);
|
||||
|
||||
const { monitorCounts = {}, monitors = [] } = monitorsSummary;
|
||||
const { total: totalMonitors = 0 } = monitorCounts;
|
||||
console.log({ monitors });
|
||||
const monitorsAsRows = monitors.map((monitor) => ({
|
||||
ip: monitor.name,
|
||||
status: determineState(monitor),
|
||||
processor: "2Ghz" /* How to retrieve that? */,
|
||||
cpu: 80 /* How to retrieve that? */,
|
||||
mem: 50 /* How to retrieve that? */,
|
||||
disk: 70 /* How to retrieve that? */,
|
||||
}));
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(getInfrastructureMonitorsByTeamId(authToken));
|
||||
}, [authToken]);
|
||||
// console.log({ isLoading, monitorsSummary, msg, success, rest });
|
||||
|
||||
return (
|
||||
<Stack
|
||||
component="main"
|
||||
@@ -134,7 +151,7 @@ function Infrastructure() {
|
||||
borderColor={theme.palette.border.light}
|
||||
backgroundColor={theme.palette.background.accent}
|
||||
>
|
||||
5
|
||||
{totalMonitors}
|
||||
</Box>
|
||||
</Stack>
|
||||
<TableContainer
|
||||
@@ -170,142 +187,81 @@ function Infrastructure() {
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{ROWS.map((row, index) => (
|
||||
<TableRow key={index}>
|
||||
{/* TODO iterate over column and get column id, applying row[column.id] */}
|
||||
<TableCell>{row.ip}</TableCell>
|
||||
<TableCell align="center">
|
||||
<StatusLabel
|
||||
status={row.status}
|
||||
text={row.status}
|
||||
/* Use capitalize inside of Status Label */
|
||||
/* Update component so we don't need to pass text and status separately*/
|
||||
customStyles={{ textTransform: "capitalize" }}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell align="center">
|
||||
<Stack
|
||||
direction={"row"}
|
||||
justifyContent={"center"}
|
||||
alignItems={"center"}
|
||||
gap=".25rem"
|
||||
>
|
||||
<CPUChipIcon
|
||||
width={20}
|
||||
height={20}
|
||||
{
|
||||
/* ROWS */ monitorsAsRows.map((row, index) => (
|
||||
<TableRow key={index}>
|
||||
{/* TODO iterate over column and get column id, applying row[column.id] */}
|
||||
<TableCell>{row.ip}</TableCell>
|
||||
<TableCell align="center">
|
||||
<StatusLabel
|
||||
status={row.status}
|
||||
text={row.status}
|
||||
/* Use capitalize inside of Status Label */
|
||||
/* Update component so we don't need to pass text and status separately*/
|
||||
customStyles={{ textTransform: "capitalize" }}
|
||||
/>
|
||||
{row.processor}
|
||||
</Stack>
|
||||
</TableCell>
|
||||
<TableCell align="center">
|
||||
<Gauge
|
||||
progressValue={row.cpu}
|
||||
containerWidth={60}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell align="center">
|
||||
<Gauge
|
||||
progressValue={row.mem}
|
||||
containerWidth={60}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell align="center">
|
||||
<Gauge
|
||||
progressValue={row.disk}
|
||||
containerWidth={60}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell align="center">
|
||||
{/* Get ActionsMenu from Monitor Table and create a component */}
|
||||
<IconButton
|
||||
sx={{
|
||||
"& svg path": {
|
||||
stroke: theme.palette.other.icon,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<GearIcon
|
||||
width={20}
|
||||
height={20}
|
||||
</TableCell>
|
||||
<TableCell align="center">
|
||||
<Stack
|
||||
direction={"row"}
|
||||
justifyContent={"center"}
|
||||
alignItems={"center"}
|
||||
gap=".25rem"
|
||||
>
|
||||
<CPUChipIcon
|
||||
width={20}
|
||||
height={20}
|
||||
/>
|
||||
{row.processor}
|
||||
</Stack>
|
||||
</TableCell>
|
||||
<TableCell align="center">
|
||||
<Gauge
|
||||
progressValue={row.cpu}
|
||||
containerWidth={60}
|
||||
/>
|
||||
</IconButton>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableCell>
|
||||
<TableCell align="center">
|
||||
<Gauge
|
||||
progressValue={row.mem}
|
||||
containerWidth={60}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell align="center">
|
||||
<Gauge
|
||||
progressValue={row.disk}
|
||||
containerWidth={60}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell align="center">
|
||||
{/* Get ActionsMenu from Monitor Table and create a component */}
|
||||
<IconButton
|
||||
sx={{
|
||||
"& svg path": {
|
||||
stroke: theme.palette.other.icon,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<GearIcon
|
||||
width={20}
|
||||
height={20}
|
||||
/>
|
||||
</IconButton>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))
|
||||
}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
{/* <Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
marginTop={8}
|
||||
>
|
||||
<Typography
|
||||
// px={theme.spacing(2)}
|
||||
variant="body2"
|
||||
// sx={{ opacity: 0.7 }}
|
||||
>
|
||||
Showing {getRange()} of {monitorCount} monitor(s)
|
||||
</Typography>
|
||||
<TablePagination
|
||||
component="div"
|
||||
count={monitorCount}
|
||||
page={page}
|
||||
onPageChange={handleChangePage}
|
||||
rowsPerPage={rowsPerPage}
|
||||
rowsPerPageOptions={[5, 10, 15, 25]}
|
||||
onRowsPerPageChange={handleChangeRowsPerPage}
|
||||
ActionsComponent={TablePaginationActions}
|
||||
labelRowsPerPage="Rows per page"
|
||||
labelDisplayedRows={({ page, count }) =>
|
||||
`Page ${page + 1} of ${Math.max(0, Math.ceil(count / rowsPerPage))}`
|
||||
}
|
||||
slotProps={{
|
||||
select: {
|
||||
MenuProps: {
|
||||
keepMounted: true,
|
||||
disableScrollLock: 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={{
|
||||
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,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Stack> */}
|
||||
{/*
|
||||
TODO continue creating pagination component. It should change the current page, which will trigger refetch?
|
||||
|
||||
*/}
|
||||
{/* <Pagination
|
||||
// monitorCount={totalMonitors}
|
||||
page={0}
|
||||
/> */}
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,15 @@
|
||||
import PropTypes from "prop-types";
|
||||
import { useState, useEffect, memo, useCallback, useRef } from "react";
|
||||
import { useNavigate } from "react-router";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import useUtils from "../../utils";
|
||||
|
||||
import { setRowsPerPage } from "../../../../Features/UI/uiSlice";
|
||||
import { logger } from "../../../../Utils/Logger";
|
||||
import { jwtDecode } from "jwt-decode";
|
||||
import { networkService } from "../../../../main";
|
||||
|
||||
import {
|
||||
TableContainer,
|
||||
Table,
|
||||
@@ -8,106 +19,18 @@ import {
|
||||
TableBody,
|
||||
Paper,
|
||||
Box,
|
||||
TablePagination,
|
||||
Stack,
|
||||
Typography,
|
||||
Button,
|
||||
CircularProgress,
|
||||
} from "@mui/material";
|
||||
import ActionsMenu from "../actionsMenu";
|
||||
import Host from "../host";
|
||||
import { StatusLabel } from "../../../../Components/Label";
|
||||
import { TableBodySkeleton } from "./Skeleton";
|
||||
import BarChart from "../../../../Components/Charts/BarChart";
|
||||
|
||||
import ArrowDownwardRoundedIcon from "@mui/icons-material/ArrowDownwardRounded";
|
||||
import ArrowUpwardRoundedIcon from "@mui/icons-material/ArrowUpwardRounded";
|
||||
|
||||
import { setRowsPerPage } from "../../../../Features/UI/uiSlice";
|
||||
import { useState, useEffect, memo, useCallback, useRef } from "react";
|
||||
import { useNavigate } from "react-router";
|
||||
import { logger } from "../../../../Utils/Logger";
|
||||
import Host from "../host";
|
||||
import { StatusLabel } from "../../../../Components/Label";
|
||||
import { jwtDecode } from "jwt-decode";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { networkService } from "../../../../main";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import BarChart from "../../../../Components/Charts/BarChart";
|
||||
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 ActionsMenu from "../actionsMenu";
|
||||
import useUtils from "../../utils";
|
||||
import { TableBodySkeleton } from "./Skeleton";
|
||||
|
||||
/**
|
||||
* 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 (
|
||||
<Box sx={{ flexShrink: 0, ml: "24px" }}>
|
||||
<Button
|
||||
variant="group"
|
||||
onClick={handleFirstPageButtonClick}
|
||||
disabled={page === 0}
|
||||
aria-label="first page"
|
||||
>
|
||||
<LeftArrowDouble />
|
||||
</Button>
|
||||
<Button
|
||||
variant="group"
|
||||
onClick={handleBackButtonClick}
|
||||
disabled={page === 0}
|
||||
aria-label="previous page"
|
||||
>
|
||||
<LeftArrow />
|
||||
</Button>
|
||||
<Button
|
||||
variant="group"
|
||||
onClick={handleNextButtonClick}
|
||||
disabled={page >= Math.ceil(count / rowsPerPage) - 1}
|
||||
aria-label="next page"
|
||||
>
|
||||
<RightArrow />
|
||||
</Button>
|
||||
<Button
|
||||
variant="group"
|
||||
onClick={handleLastPageButtonClick}
|
||||
disabled={page >= Math.ceil(count / rowsPerPage) - 1}
|
||||
aria-label="last page"
|
||||
>
|
||||
<RightArrowDouble />
|
||||
</Button>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
TablePaginationActions.propTypes = {
|
||||
count: PropTypes.number.isRequired,
|
||||
page: PropTypes.number.isRequired,
|
||||
rowsPerPage: PropTypes.number.isRequired,
|
||||
onPageChange: PropTypes.func.isRequired,
|
||||
};
|
||||
import { Pagination } from "../../../Infrastructure/components/TablePagination";
|
||||
|
||||
const MonitorTable = ({ isAdmin, filter, setIsSearching, isSearching }) => {
|
||||
const theme = useTheme();
|
||||
@@ -117,6 +40,7 @@ const MonitorTable = ({ isAdmin, filter, setIsSearching, isSearching }) => {
|
||||
|
||||
const { rowsPerPage } = useSelector((state) => state.ui.monitors);
|
||||
const authState = useSelector((state) => state.auth);
|
||||
|
||||
const [page, setPage] = useState(0);
|
||||
const [monitors, setMonitors] = useState([]);
|
||||
const [monitorCount, setMonitorCount] = useState(0);
|
||||
@@ -195,11 +119,11 @@ const MonitorTable = ({ isAdmin, filter, setIsSearching, isSearching }) => {
|
||||
* 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, monitorCount);
|
||||
return `${start} - ${end}`;
|
||||
};
|
||||
// const getRange = () => {
|
||||
// let start = page * rowsPerPage + 1;
|
||||
// let end = Math.min(page * rowsPerPage + rowsPerPage, monitorCount);
|
||||
// return `${start} - ${end}`;
|
||||
// };
|
||||
|
||||
const handleSort = async (field) => {
|
||||
let order = "";
|
||||
@@ -392,78 +316,13 @@ const MonitorTable = ({ isAdmin, filter, setIsSearching, isSearching }) => {
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
px={theme.spacing(4)}
|
||||
marginTop={8}
|
||||
>
|
||||
<Typography
|
||||
px={theme.spacing(2)}
|
||||
variant="body2"
|
||||
sx={{ opacity: 0.7 }}
|
||||
>
|
||||
Showing {getRange()} of {monitorCount} monitor(s)
|
||||
</Typography>
|
||||
<TablePagination
|
||||
component="div"
|
||||
count={monitorCount}
|
||||
page={page}
|
||||
onPageChange={handleChangePage}
|
||||
rowsPerPage={rowsPerPage}
|
||||
rowsPerPageOptions={[5, 10, 15, 25]}
|
||||
onRowsPerPageChange={handleChangeRowsPerPage}
|
||||
ActionsComponent={TablePaginationActions}
|
||||
labelRowsPerPage="Rows per page"
|
||||
labelDisplayedRows={({ page, count }) =>
|
||||
`Page ${page + 1} of ${Math.max(0, Math.ceil(count / rowsPerPage))}`
|
||||
}
|
||||
slotProps={{
|
||||
select: {
|
||||
MenuProps: {
|
||||
keepMounted: true,
|
||||
disableScrollLock: 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={{
|
||||
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,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Stack>
|
||||
<Pagination
|
||||
page={page}
|
||||
rowsPerPage={rowsPerPage}
|
||||
handleChangePage={handleChangePage}
|
||||
handleChangeRowsPerPage={handleChangeRowsPerPage}
|
||||
monitorCount={monitorCount}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -20,6 +20,8 @@ const Monitors = ({ isAdmin }) => {
|
||||
const authState = useSelector((state) => state.auth);
|
||||
const dispatch = useDispatch({});
|
||||
|
||||
console.log({ monitorState });
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(getUptimeMonitorsByTeamId(authState.authToken));
|
||||
}, [authState.authToken, dispatch]);
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import { useTheme } from "@mui/material";
|
||||
|
||||
const useUtils = () => {
|
||||
const theme = useTheme();
|
||||
const determineState = (monitor) => {
|
||||
if (monitor.isActive === false) return "paused";
|
||||
if (monitor?.status === undefined) return "pending";
|
||||
return monitor?.status == true ? "up" : "down";
|
||||
};
|
||||
|
||||
/* TODO Refactor: from here on shouldn't live in a custom hook, but on theme, or constants */
|
||||
const theme = useTheme();
|
||||
|
||||
const statusColor = {
|
||||
up: theme.palette.success.main,
|
||||
down: theme.palette.error.main,
|
||||
|
||||
Reference in New Issue
Block a user