mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-01-25 19:29:39 -06:00
Use new table component, remove old table
This commit is contained in:
@@ -1,71 +0,0 @@
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { Box, Stack } from "@mui/material";
|
||||
import Search from "../../../../Components/Inputs/Search";
|
||||
import MemoizedMonitorTable from "../UptimeTable";
|
||||
import { useState } from "react";
|
||||
import useDebounce from "../../../../Utils/debounce";
|
||||
import PropTypes from "prop-types";
|
||||
import { Heading } from "../../../../Components/Heading";
|
||||
|
||||
const CurrentMonitoring = ({ totalMonitors, monitors, isAdmin, handlePause }) => {
|
||||
const theme = useTheme();
|
||||
const [search, setSearch] = useState("");
|
||||
const [isSearching, setIsSearching] = useState(false);
|
||||
const debouncedFilter = useDebounce(search, 500);
|
||||
const handleSearch = (value) => {
|
||||
setIsSearching(true);
|
||||
setSearch(value);
|
||||
};
|
||||
return (
|
||||
<Box
|
||||
flex={1}
|
||||
py={theme.spacing(8)}
|
||||
>
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
mb={theme.spacing(8)}
|
||||
>
|
||||
<Heading component="h2">Uptime monitors</Heading>
|
||||
|
||||
<Box
|
||||
className="current-monitors-counter"
|
||||
color={theme.palette.text.primary}
|
||||
border={1}
|
||||
borderColor={theme.palette.border.light}
|
||||
backgroundColor={theme.palette.background.accent}
|
||||
>
|
||||
{totalMonitors}
|
||||
</Box>
|
||||
<Box
|
||||
width="25%"
|
||||
minWidth={150}
|
||||
ml="auto"
|
||||
>
|
||||
<Search
|
||||
options={monitors}
|
||||
filteredBy="name"
|
||||
inputValue={search}
|
||||
handleInputChange={handleSearch}
|
||||
/>
|
||||
</Box>
|
||||
</Stack>
|
||||
<MemoizedMonitorTable
|
||||
isAdmin={isAdmin}
|
||||
filter={debouncedFilter}
|
||||
setIsSearching={setIsSearching}
|
||||
isSearching={isSearching}
|
||||
handlePause={handlePause}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
CurrentMonitoring.propTypes = {
|
||||
handlePause: PropTypes.func,
|
||||
totalMonitors: PropTypes.number,
|
||||
monitors: PropTypes.array,
|
||||
isAdmin: PropTypes.bool,
|
||||
};
|
||||
|
||||
export { CurrentMonitoring };
|
||||
@@ -1,47 +0,0 @@
|
||||
import { Skeleton } from "@mui/material";
|
||||
import DataTable from "../../../../../Components/Table";
|
||||
const ROWS_NUMBER = 7;
|
||||
const ROWS_ARRAY = Array.from({ length: ROWS_NUMBER }, (_, i) => i);
|
||||
|
||||
const TableSkeleton = () => {
|
||||
/* TODO Skeleton does not follow light and dark theme */
|
||||
|
||||
const headers = [
|
||||
{
|
||||
id: "name",
|
||||
|
||||
content: "Host",
|
||||
|
||||
render: () => <Skeleton />,
|
||||
},
|
||||
{
|
||||
id: "status",
|
||||
content: "Status",
|
||||
render: () => <Skeleton />,
|
||||
},
|
||||
{
|
||||
id: "responseTime",
|
||||
content: "Response Time",
|
||||
render: () => <Skeleton />,
|
||||
},
|
||||
{
|
||||
id: "type",
|
||||
content: "Type",
|
||||
render: () => <Skeleton />,
|
||||
},
|
||||
{
|
||||
id: "actions",
|
||||
content: "Actions",
|
||||
render: () => <Skeleton />,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<DataTable
|
||||
headers={headers}
|
||||
data={ROWS_ARRAY}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export { TableSkeleton };
|
||||
@@ -1,345 +0,0 @@
|
||||
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 { Box, CircularProgress } from "@mui/material";
|
||||
import ActionsMenu from "../actionsMenu";
|
||||
import Host from "../host";
|
||||
import { StatusLabel } from "../../../../Components/Label";
|
||||
import { TableSkeleton } from "./Skeleton";
|
||||
import BarChart from "../../../../Components/Charts/BarChart";
|
||||
|
||||
import ArrowDownwardRoundedIcon from "@mui/icons-material/ArrowDownwardRounded";
|
||||
import ArrowUpwardRoundedIcon from "@mui/icons-material/ArrowUpwardRounded";
|
||||
|
||||
import { Pagination } from "../../../../Components/Table/TablePagination";
|
||||
import DataTable from "../../../../Components/Table";
|
||||
|
||||
const MonitorTable = ({ isAdmin, filter, setIsSearching, isSearching, handlePause }) => {
|
||||
const theme = useTheme();
|
||||
const navigate = useNavigate();
|
||||
const dispatch = useDispatch();
|
||||
const { determineState } = useUtils();
|
||||
|
||||
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);
|
||||
const [updateTrigger, setUpdateTrigger] = useState(false);
|
||||
const [sort, setSort] = useState({});
|
||||
const [data, setData] = useState([]);
|
||||
const prevFilter = useRef(filter);
|
||||
const headers = [
|
||||
{
|
||||
id: "name",
|
||||
content: (
|
||||
<Box onClick={() => handleSort("name")}>
|
||||
Host
|
||||
<span
|
||||
style={{
|
||||
visibility: sort.field === "name" ? "visible" : "hidden",
|
||||
}}
|
||||
>
|
||||
{sort.order === "asc" ? (
|
||||
<ArrowUpwardRoundedIcon />
|
||||
) : (
|
||||
<ArrowDownwardRoundedIcon />
|
||||
)}
|
||||
</span>
|
||||
</Box>
|
||||
),
|
||||
render: (row) => (
|
||||
<Host
|
||||
key={row._id}
|
||||
url={row.url}
|
||||
title={row.title}
|
||||
percentageColor={row.percentageColor}
|
||||
percentage={row.percentage}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: "status",
|
||||
content: (
|
||||
<Box
|
||||
width="max-content"
|
||||
onClick={() => handleSort("status")}
|
||||
>
|
||||
{" "}
|
||||
Status
|
||||
<span
|
||||
style={{
|
||||
visibility: sort.field === "status" ? "visible" : "hidden",
|
||||
}}
|
||||
>
|
||||
{sort.order === "asc" ? (
|
||||
<ArrowUpwardRoundedIcon />
|
||||
) : (
|
||||
<ArrowDownwardRoundedIcon />
|
||||
)}
|
||||
</span>
|
||||
</Box>
|
||||
),
|
||||
render: (row) => {
|
||||
const status = determineState(row.monitor);
|
||||
return (
|
||||
<StatusLabel
|
||||
status={status}
|
||||
text={status}
|
||||
customStyles={{ textTransform: "capitalize" }}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "responseTime",
|
||||
content: "Response Time",
|
||||
render: (row) => <BarChart checks={row.monitor.checks.slice().reverse()} />,
|
||||
},
|
||||
{
|
||||
id: "type",
|
||||
content: "Type",
|
||||
render: (row) => (
|
||||
<span style={{ textTransform: "uppercase" }}>{row.monitor.type}</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: "actions",
|
||||
content: "Actions",
|
||||
render: (row) => (
|
||||
<ActionsMenu
|
||||
monitor={row.monitor}
|
||||
isAdmin={isAdmin}
|
||||
updateRowCallback={handleRowUpdate}
|
||||
pauseCallback={handlePause}
|
||||
/>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
const handleRowUpdate = () => {
|
||||
setUpdateTrigger((prev) => !prev);
|
||||
};
|
||||
|
||||
const handleChangePage = (event, newPage) => {
|
||||
setPage(newPage);
|
||||
};
|
||||
|
||||
const handleChangeRowsPerPage = (event) => {
|
||||
dispatch(
|
||||
setRowsPerPage({
|
||||
value: parseInt(event.target.value, 10),
|
||||
table: "monitors",
|
||||
})
|
||||
);
|
||||
setPage(0);
|
||||
};
|
||||
|
||||
const fetchPage = useCallback(async () => {
|
||||
try {
|
||||
const { authToken } = authState;
|
||||
const user = jwtDecode(authToken);
|
||||
const res = await networkService.getMonitorsByTeamId({
|
||||
authToken,
|
||||
teamId: user.teamId,
|
||||
limit: 25,
|
||||
types: ["http", "ping", "docker", "port"],
|
||||
status: null,
|
||||
checkOrder: "desc",
|
||||
normalize: true,
|
||||
page: page,
|
||||
rowsPerPage: rowsPerPage,
|
||||
filter: filter,
|
||||
field: sort.field,
|
||||
order: sort.order,
|
||||
});
|
||||
setMonitors(res?.data?.data?.monitors ?? []);
|
||||
setMonitorCount(res?.data?.data?.monitorCount ?? 0);
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
} finally {
|
||||
setIsSearching(false);
|
||||
}
|
||||
}, [authState, page, rowsPerPage, filter, sort, setIsSearching]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchPage();
|
||||
}, [
|
||||
updateTrigger,
|
||||
authState,
|
||||
page,
|
||||
rowsPerPage,
|
||||
filter,
|
||||
sort,
|
||||
setIsSearching,
|
||||
fetchPage,
|
||||
]);
|
||||
|
||||
// Listen for changes in filter, if new value reset the page
|
||||
useEffect(() => {
|
||||
if (prevFilter.current !== filter) {
|
||||
setPage(0);
|
||||
fetchPage();
|
||||
}
|
||||
prevFilter.current = filter;
|
||||
}, [filter, fetchPage]);
|
||||
|
||||
const handleSort = async (field) => {
|
||||
let order = "";
|
||||
if (sort.field !== field) {
|
||||
order = "desc";
|
||||
} else {
|
||||
order = sort.order === "asc" ? "desc" : "asc";
|
||||
}
|
||||
setSort({ field, order });
|
||||
|
||||
const { authToken } = authState;
|
||||
const user = jwtDecode(authToken);
|
||||
|
||||
const res = await networkService.getMonitorsByTeamId({
|
||||
authToken,
|
||||
teamId: user.teamId,
|
||||
limit: 25,
|
||||
types: ["http", "ping"],
|
||||
status: null,
|
||||
checkOrder: "desc",
|
||||
normalize: true,
|
||||
page: page,
|
||||
rowsPerPage: rowsPerPage,
|
||||
filter: null,
|
||||
field: field,
|
||||
order: order,
|
||||
});
|
||||
setMonitors(res?.data?.data?.monitors ?? []);
|
||||
setMonitorCount(res?.data?.data?.monitorCount ?? 0);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const mappedMonitors = monitors.map((monitor) => {
|
||||
let uptimePercentage = "";
|
||||
let percentageColor = theme.palette.percentage.uptimeExcellent;
|
||||
|
||||
// Determine uptime percentage and color based on the monitor's uptimePercentage value
|
||||
if (monitor.uptimePercentage !== undefined) {
|
||||
uptimePercentage =
|
||||
monitor.uptimePercentage === 0
|
||||
? "0"
|
||||
: (monitor.uptimePercentage * 100).toFixed(2);
|
||||
|
||||
percentageColor =
|
||||
monitor.uptimePercentage < 0.25
|
||||
? theme.palette.percentage.uptimePoor
|
||||
: monitor.uptimePercentage < 0.5
|
||||
? theme.palette.percentage.uptimeFair
|
||||
: monitor.uptimePercentage < 0.75
|
||||
? theme.palette.percentage.uptimeGood
|
||||
: theme.palette.percentage.uptimeExcellent;
|
||||
}
|
||||
|
||||
return {
|
||||
id: monitor._id,
|
||||
url: monitor.url,
|
||||
title: monitor.name,
|
||||
percentage: uptimePercentage,
|
||||
percentageColor,
|
||||
monitor: monitor,
|
||||
};
|
||||
});
|
||||
setData(mappedMonitors);
|
||||
}, [monitors, theme]);
|
||||
|
||||
return (
|
||||
<Box position="relative">
|
||||
{isSearching && (
|
||||
<>
|
||||
<Box
|
||||
width="100%"
|
||||
height="100%"
|
||||
position="absolute"
|
||||
sx={{
|
||||
backgroundColor: theme.palette.background.main,
|
||||
opacity: 0.8,
|
||||
zIndex: 100,
|
||||
}}
|
||||
/>
|
||||
<Box
|
||||
height="100%"
|
||||
position="absolute"
|
||||
top="20%"
|
||||
left="50%"
|
||||
sx={{
|
||||
transform: "translateX(-50%)",
|
||||
zIndex: 101,
|
||||
}}
|
||||
>
|
||||
<CircularProgress
|
||||
sx={{
|
||||
color: theme.palette.other.icon,
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
{/*
|
||||
This is the original SX for the row, doesn't match infrastructure table
|
||||
rowSX: {
|
||||
cursor: "pointer",
|
||||
"&:hover": {
|
||||
filter: "brightness(.75)",
|
||||
opacity: 0.75,
|
||||
transition: "filter 0.3s ease, opacity 0.3s ease",
|
||||
},
|
||||
},
|
||||
*/}
|
||||
{monitors.length > 0 ? (
|
||||
<DataTable
|
||||
headers={headers}
|
||||
data={data}
|
||||
config={{
|
||||
rowSX: {
|
||||
cursor: "pointer",
|
||||
"&:hover": {
|
||||
backgroundColor: theme.palette.background.accent,
|
||||
},
|
||||
},
|
||||
onRowClick: (row) => {
|
||||
navigate(`/uptime/${row.id}`);
|
||||
},
|
||||
emptyView: "No monitors found",
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<TableSkeleton />
|
||||
)}
|
||||
<Pagination
|
||||
monitorCount={monitorCount}
|
||||
page={page}
|
||||
rowsPerPage={rowsPerPage}
|
||||
handleChangePage={handleChangePage}
|
||||
handleChangeRowsPerPage={handleChangeRowsPerPage}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
MonitorTable.propTypes = {
|
||||
isAdmin: PropTypes.bool,
|
||||
filter: PropTypes.string,
|
||||
setIsSearching: PropTypes.func,
|
||||
isSearching: PropTypes.bool,
|
||||
setMonitorUpdateTrigger: PropTypes.func,
|
||||
handlePause: PropTypes.func,
|
||||
};
|
||||
|
||||
const MemoizedMonitorTable = memo(MonitorTable);
|
||||
export default MemoizedMonitorTable;
|
||||
@@ -34,7 +34,7 @@ const ActionsMenu = ({ monitor, isAdmin, updateRowCallback, pauseCallback }) =>
|
||||
if (action.meta.requestStatus === "fulfilled") {
|
||||
setIsOpen(false); // close modal
|
||||
dispatch(getUptimeMonitorsByTeamId(authState.authToken));
|
||||
updateCallback();
|
||||
updateRowCallback();
|
||||
createToast({ body: "Monitor deleted successfully." });
|
||||
} else {
|
||||
createToast({ body: "Failed to delete monitor." });
|
||||
@@ -169,6 +169,8 @@ const ActionsMenu = ({ monitor, isAdmin, updateRowCallback, pauseCallback }) =>
|
||||
{isAdmin && (
|
||||
<MenuItem
|
||||
onClick={(e) => {
|
||||
closeMenu(e);
|
||||
|
||||
e.stopPropagation();
|
||||
handlePause(e);
|
||||
}}
|
||||
|
||||
@@ -48,6 +48,7 @@ const Routes = () => {
|
||||
path="/uptime"
|
||||
element={<Monitors />}
|
||||
/>
|
||||
|
||||
<Route
|
||||
path="/uptime/create/:monitorId?"
|
||||
element={<CreateMonitor />}
|
||||
|
||||
Reference in New Issue
Block a user