Renamed filter to dateRange, add incidents table

This commit is contained in:
Alex Holliday committed 2024-08-08 16:13:08 -07:00
1 parent 60996da401
commit d95fcb119d
7 files changed
+285 -113

No files matched your search

@@ -79,7 +79,6 @@ const MonitorDetailsAreaChart = ({ checks }) => {
MonitorDetailsAreaChart.propTypes = {
checks: PropTypes.array,
filter: PropTypes.string,
};
CustomToolTip.propTypes = {
@@ -0,0 +1,125 @@
import PropTypes from "prop-types";
import {
TableContainer,
Table,
TableHead,
TableRow,
TableCell,
TableBody,
TableFooter,
TablePagination,
} from "@mui/material";
import { useState, useEffect } from "react";
import { useSelector } from "react-redux";
import axiosInstance from "../../../Utils/axiosConfig";
import { StatusLabel } from "../../../Components/Label";
const IncidentTable = ({ monitorId, filter }) => {
const { authToken } = useSelector((state) => state.auth);
const [checks, setChecks] = useState([]);
const [checksCount, setChecksCount] = useState(0);
const [paginationController, setPaginationController] = useState({
page: 0,
rowsPerPage: 5,
});
useEffect(() => {
const fetchPage = async () => {
try {
if (monitorId === "0") {
return;
}
const res = await axiosInstance.get(
`/checks/${monitorId}?sortOrder=desc&filter=${filter}&page=${paginationController.page}&rowsPerPage=${paginationController.rowsPerPage}`,
{
headers: {
Authorization: `Bearer ${authToken}`,
},
}
);
setChecks(res.data.data.checks);
setChecksCount(res.data.data.checksCount);
} catch (error) {
console.log(error);
}
};
fetchPage();
}, [
authToken,
monitorId,
filter,
paginationController.page,
paginationController.rowsPerPage,
]);
const handlePageChange = (_, newPage) => {
setPaginationController({
...paginationController,
page: newPage,
});
};
const handleChangeRowsPerPage = (event) => {
setPaginationController({
...paginationController,
rowsPerPage: parseInt(event.target.value, 10),
page: 0,
});
};
return (
<TableContainer>
<Table>
<TableHead>
<TableRow>
<TableCell>Monitor Name</TableCell>
<TableCell>Status</TableCell>
<TableCell>Date & Time</TableCell>
<TableCell>Message</TableCell>
</TableRow>
</TableHead>
<TableBody>
{checks.map((check) => {
const status = check.status === true ? "up" : "down";
return (
<TableRow key={check._id}>
<TableCell>
<StatusLabel
status={status}
text={status}
customStyles={{ textTransform: "capitalize" }}
/>
</TableCell>
<TableCell>
{new Date(check.createdAt).toLocaleString()}
</TableCell>
<TableCell>{check.statusCode}</TableCell>
</TableRow>
);
})}
</TableBody>
<TableFooter>
<TableRow>
<TablePagination
rowsPerPageOptions={[5, 10, 25]}
onPageChange={handlePageChange}
page={paginationController.page}
count={checksCount}
rowsPerPage={paginationController.rowsPerPage}
onRowsPerPageChange={handleChangeRowsPerPage}
/>
</TableRow>
</TableFooter>
</Table>
</TableContainer>
);
};
IncidentTable.propTypes = {
monitorId: PropTypes.string.isRequired,
filter: PropTypes.string.isRequired,
};
export default IncidentTable;
+131 -88
View File
@@ -9,7 +9,7 @@ import { StatusLabel } from "../../Components/Label";
import { useTheme } from "@emotion/react";
import { useNavigate } from "react-router";
import Select from "../../Components/Inputs/Select";
import IncidentTable from "./IncidentTable";
import "./index.css";
/**
@@ -76,99 +76,142 @@ const Incidents = () => {
fetchMonitors();
}, [authState]);
useEffect(() => {}, []);
const handleSelect = (event) => {
setSelectedMonitor(event.target.value);
};
return (
<>
<Stack className="incidents" gap={theme.gap.large}>
{loading ? (
<SkeletonLayout />
) : false ? (
<>
<Stack direction="row" alignItems="center" gap={theme.gap.medium}>
<Typography component="h1">Incident history for: </Typography>
<Select
id="incidents-select-monitor"
placeholder="All servers"
value={selectedMonitor}
onChange={handleSelect}
items={Object.values(monitors)}
/>
{hasAnyIncidents && hasSpecificIncidents && (
<ButtonGroup sx={{ ml: "auto" }}>
<Button
level="secondary"
label="All"
onClick={() => setFilter("all")}
sx={{
backgroundColor:
filter === "all" && theme.palette.otherColors.fillGray,
}}
/>
<Button
level="secondary"
label="Down"
onClick={() => setFilter("down")}
sx={{
backgroundColor:
filter === "down" && theme.palette.otherColors.fillGray,
}}
/>
<Button
level="secondary"
label="Cannot Resolve"
onClick={() => setFilter("resolve")}
sx={{
backgroundColor:
filter === "resolve" &&
theme.palette.otherColors.fillGray,
}}
/>
</ButtonGroup>
)}
</Stack>
{hasAnyIncidents && hasSpecificIncidents ? (
<div>thing</div>
) : (
<Typography alignSelf="center" mt={theme.gap.xxl}>
The monitor you have selected, has no recorded incidents yet.
</Typography>
)}
</>
) : (
<Box>
<Button
level="tertiary"
label="Back"
animate="slideLeft"
img={<WestRoundedIcon />}
onClick={() => navigate(-1)}
sx={{
backgroundColor: theme.palette.otherColors.fillGray,
px: theme.gap.ml,
mb: theme.gap.ml,
"& svg.MuiSvgIcon-root": {
mr: theme.gap.small,
fill: theme.palette.otherColors.slateGray,
},
}}
/>
<Box
padding={theme.gap.xxl}
gap={theme.gap.small}
border={1}
borderRadius={`${theme.shape.borderRadius}px`}
borderColor={theme.palette.otherColors.graishWhite}
backgroundColor={theme.palette.otherColors.white}
>
<Typography textAlign="center">No incidents, yet.</Typography>
</Box>
</Box>
)}
<Stack className="incidents" gap={theme.gap.large}>
<Stack direction="row" alignItems="center" gap={theme.gap.medium}>
<Select
id="incidents-select-monitor"
placeholder="All servers"
value={selectedMonitor}
onChange={handleSelect}
items={Object.values(monitors)}
/>
<ButtonGroup sx={{ ml: "auto" }}>
<Button
level="secondary"
label="All"
onClick={() => setFilter("all")}
sx={{
backgroundColor:
filter === "all" && theme.palette.otherColors.fillGray,
}}
/>
<Button
level="secondary"
label="Down"
onClick={() => setFilter("down")}
sx={{
backgroundColor:
filter === "down" && theme.palette.otherColors.fillGray,
}}
/>
<Button
level="secondary"
label="Cannot Resolve"
onClick={() => setFilter("resolve")}
sx={{
backgroundColor:
filter === "resolve" && theme.palette.otherColors.fillGray,
}}
/>
</ButtonGroup>
</Stack>
</>
<IncidentTable monitorId={selectedMonitor} filter={filter} />
</Stack>
// <>
// <Stack className="incidents" gap={theme.gap.large}>
// {loading ? (
// <SkeletonLayout />
// ) : false ? (
// <>
// <Stack direction="row" alignItems="center" gap={theme.gap.medium}>
// <Typography component="h1">Incident history for: </Typography>
// <Select
// id="incidents-select-monitor"
// placeholder="All servers"
// value={selectedMonitor}
// onChange={handleSelect}
// items={Object.values(monitors)}
// />
// {hasAnyIncidents && hasSpecificIncidents && (
// <ButtonGroup sx={{ ml: "auto" }}>
// <Button
// level="secondary"
// label="All"
// onClick={() => setFilter("all")}
// sx={{
// backgroundColor:
// filter === "all" && theme.palette.otherColors.fillGray,
// }}
// />
// <Button
// level="secondary"
// label="Down"
// onClick={() => setFilter("down")}
// sx={{
// backgroundColor:
// filter === "down" && theme.palette.otherColors.fillGray,
// }}
// />
// <Button
// level="secondary"
// label="Cannot Resolve"
// onClick={() => setFilter("resolve")}
// sx={{
// backgroundColor:
// filter === "resolve" &&
// theme.palette.otherColors.fillGray,
// }}
// />
// </ButtonGroup>
// )}
// </Stack>
// {hasAnyIncidents && hasSpecificIncidents ? (
// <div>thing</div>
// ) : (
// <Typography alignSelf="center" mt={theme.gap.xxl}>
// The monitor you have selected, has no recorded incidents yet.
// </Typography>
// )}
// </>
// ) : (
// <Box>
// <Button
// level="tertiary"
// label="Back"
// animate="slideLeft"
// img={<WestRoundedIcon />}
// onClick={() => navigate(-1)}
// sx={{
// backgroundColor: theme.palette.otherColors.fillGray,
// px: theme.gap.ml,
// mb: theme.gap.ml,
// "& svg.MuiSvgIcon-root": {
// mr: theme.gap.small,
// fill: theme.palette.otherColors.slateGray,
// },
// }}
// />
// <Box
// padding={theme.gap.xxl}
// gap={theme.gap.small}
// border={1}
// borderRadius={`${theme.shape.borderRadius}px`}
// borderColor={theme.palette.otherColors.graishWhite}
// backgroundColor={theme.palette.otherColors.white}
// >
// <Typography textAlign="center">No incidents, yet.</Typography>
// </Box>
// </Box>
// )}
// </Stack>
// </>
);
};
@@ -15,7 +15,7 @@ import { useSelector } from "react-redux";
import axiosInstance from "../../../../Utils/axiosConfig";
import { StatusLabel } from "../../../../Components/Label";
const PaginationTable = ({ monitorId, filter }) => {
const PaginationTable = ({ monitorId, dateRange }) => {
const { authToken } = useSelector((state) => state.auth);
const [checks, setChecks] = useState([]);
const [checksCount, setChecksCount] = useState(0);
@@ -28,7 +28,7 @@ const PaginationTable = ({ monitorId, filter }) => {
const fetchPage = async () => {
try {
const res = await axiosInstance.get(
`/checks/${monitorId}?sortOrder=desc&filter=${filter}&page=${paginationController.page}&rowsPerPage=${paginationController.rowsPerPage}`,
`/checks/${monitorId}?sortOrder=desc&dateRange=${dateRange}&page=${paginationController.page}&rowsPerPage=${paginationController.rowsPerPage}`,
{
headers: {
Authorization: `Bearer ${authToken}`,
@@ -45,7 +45,7 @@ const PaginationTable = ({ monitorId, filter }) => {
}, [
authToken,
monitorId,
filter,
dateRange,
paginationController.page,
paginationController.rowsPerPage,
]);
@@ -115,7 +115,7 @@ const PaginationTable = ({ monitorId, filter }) => {
PaginationTable.propTypes = {
monitorId: PropTypes.string.isRequired,
filter: PropTypes.string.isRequired,
dateRange: PropTypes.string.isRequired,
};
export default PaginationTable;
+13 -14
View File
@@ -113,13 +113,13 @@ const DetailsPage = () => {
const [monitor, setMonitor] = useState({});
const { monitorId } = useParams();
const { authToken } = useSelector((state) => state.auth);
const [filter, setFilter] = useState("day");
const [dateRange, setDateRange] = useState("day");
const navigate = useNavigate();
const fetchMonitor = useCallback(async () => {
try {
const res = await axiosInstance.get(
`/monitors/${monitorId}?sortOrder=asc&filter=${filter}&numToDisplay=50&normalize=true`,
`/monitors/${monitorId}?sortOrder=asc&filter=${dateRange}&numToDisplay=50&normalize=true`,
{
headers: {
Authorization: `Bearer ${authToken}`,
@@ -131,7 +131,7 @@ const DetailsPage = () => {
console.error("Error fetching monitor of id: " + monitorId);
navigate("/not-found");
}
}, [authToken, monitorId, navigate, filter]);
}, [authToken, monitorId, navigate, dateRange]);
useEffect(() => {
fetchMonitor();
@@ -301,44 +301,43 @@ const DetailsPage = () => {
<Button
level="secondary"
label="Day"
onClick={() => setFilter("day")}
onClick={() => setDateRange("day")}
sx={{
backgroundColor:
filter === "day" && theme.palette.otherColors.fillGray,
dateRange === "day" &&
theme.palette.otherColors.fillGray,
}}
/>
<Button
level="secondary"
label="Week"
onClick={() => setFilter("week")}
onClick={() => setDateRange("week")}
sx={{
backgroundColor:
filter === "week" && theme.palette.otherColors.fillGray,
dateRange === "week" &&
theme.palette.otherColors.fillGray,
}}
/>
<Button
level="secondary"
label="Month"
onClick={() => setFilter("month")}
onClick={() => setDateRange("month")}
sx={{
backgroundColor:
filter === "month" &&
dateRange === "month" &&
theme.palette.otherColors.fillGray,
}}
/>
</ButtonGroup>
</Stack>
<Box sx={{ height: "200px" }}>
<MonitorDetailsAreaChart
checks={monitor.checks}
filter={filter}
/>
<MonitorDetailsAreaChart checks={monitor.checks} />
</Box>
</Box>
<Stack gap={theme.gap.ml}>
<Typography component="h2">History</Typography>
{/* TODO New Table */}
<PaginationTable monitorId={monitorId} filter={filter} />
<PaginationTable monitorId={monitorId} dateRange={dateRange} />
{/* <BasicTable data={data} paginated={true} /> */}
</Stack>
</Stack>
+10 -5
View File
@@ -1,11 +1,12 @@
const Check = require("../../../models/Check");
const Monitor = require("../../../models/Monitor");
const mongoose = require("mongoose");
const filterLookup = {
const dateRangeLookup = {
day: new Date(new Date().setDate(new Date().getDate() - 1)),
week: new Date(new Date().setDate(new Date().getDate() - 7)),
month: new Date(new Date().setMonth(new Date().getMonth() - 1)),
};
/**
* Create a check for a monitor
* @async
@@ -35,7 +36,7 @@ const getChecksCount = async (req) => {
const checksQuery = { monitorId: monitorId };
// Filter checks by "day", "week", or "month"
if (filter !== undefined) {
checksQuery.createdAt = { $gte: filterLookup[filter] };
checksQuery.createdAt = { $gte: dateRangeLookup[filter] };
}
const count = await Check.countDocuments(checksQuery);
return count;
@@ -52,8 +53,8 @@ const getChecksCount = async (req) => {
const getChecks = async (req) => {
try {
const { monitorId } = req.params;
let { sortOrder, limit, filter, page, rowsPerPage } = req.query;
let { sortOrder, limit, dateRange, filter, page, rowsPerPage } = req.query;
console.log(filter);
// Default limit to 0 if not provided
if (limit === undefined) limit = 0;
@@ -67,8 +68,12 @@ const getChecks = async (req) => {
// Build query
const checksQuery = { monitorId: monitorId };
// Filter checks by "day", "week", or "month"
if (dateRange !== undefined) {
checksQuery.createdAt = { $gte: dateRangeLookup[dateRange] };
}
// Fitler checks by status
if (filter !== undefined) {
checksQuery.createdAt = { $gte: filterLookup[filter] };
checksQuery.status = false;
}
// Need to skip and limit here
+2 -1
View File
@@ -235,7 +235,8 @@ const getChecksParamValidation = joi.object({
const getChecksQueryValidation = joi.object({
sortOrder: joi.string().valid("asc", "desc"),
limit: joi.number(),
filter: joi.string().valid("day", "week", "month"),
dateRange: joi.string().valid("day", "week", "month"),
filter: joi.string().valid("all", "down", "resolve"),
page: joi.number(),
rowsPerPage: joi.number(),
});