monitor select

This commit is contained in:
Alex Holliday committed 2026-01-27 23:05:00 +00:00
1 parent f88e4ee6f4
commit f3ba8c9815
4 files changed
+197 -20

No files matched your search

@@ -4,12 +4,16 @@ import Box from "@mui/material/Box";
import { BaseBox } from "@/Components/v2/design-elements";
import Background from "@/assets/Images/background-grid.svg?react";
import { useTranslation } from "react-i18next";
import type { SxProps } from "@mui/material";
import { useTheme } from "@mui/material/styles";
type StatusBoxProps = React.PropsWithChildren<{ children: React.ReactNode }>;
type StatusBoxProps = React.PropsWithChildren<{
children: React.ReactNode;
sx?: SxProps;
}>;
export const BGBox = ({ children }: StatusBoxProps) => {
export const BGBox = ({ children, sx }: StatusBoxProps) => {
const theme = useTheme();
return (
<BaseBox
@@ -18,6 +22,7 @@ export const BGBox = ({ children }: StatusBoxProps) => {
position: "relative",
flex: 1,
padding: theme.spacing(4),
...sx,
}}
>
<Box
@@ -36,14 +41,16 @@ const StatusBox = ({
label,
n,
color,
sx,
}: {
label: string;
n: number;
color: string | undefined;
sx?: SxProps;
}) => {
const theme = useTheme();
return (
<BGBox>
<BGBox sx={sx}>
<Stack spacing={theme.spacing(4)}>
<Typography
variant={"h2"}
@@ -106,6 +113,9 @@ export const TotalChecksBox = ({ n }: { n: number }) => {
label={t("pages.common.monitors.status.total")}
n={n}
color={theme.palette.primary.light}
sx={{
maxWidth: "300px",
}}
/>
);
};
@@ -117,6 +127,9 @@ export const DownChecksBox = ({ n }: { n: number }) => {
label={t("pages.common.monitors.status.down")}
n={n}
color={theme.palette.error.light}
sx={{
maxWidth: "300px",
}}
/>
);
};
@@ -0,0 +1,132 @@
import {
Table,
Pagination,
ValueLabel,
StatusLabel,
} from "@/Components/v2/design-elements";
import Box from "@mui/material/Box";
import type { Header } from "@/Components/v2/design-elements/Table";
import type { Monitor, MonitorStatus } from "@/Types/Monitor";
import { useTranslation } from "react-i18next";
import { formatDateWithTz } from "@/Utils/TimeUtils";
import { useNavigate } from "react-router";
import type { Check } from "@/Types/Check";
import type { RootState } from "@/Types/state";
import { useSelector } from "react-redux";
export const ChecksTable = ({
monitors,
selectedMonitorId,
page,
setPage,
rowsPerPage,
setRowsPerPage,
}: {
monitors: Monitor[] | null;
selectedMonitorId: string;
page: number;
setPage: (page: number) => void;
rowsPerPage: number;
setRowsPerPage: (rowsPerPage: number) => void;
}) => {
const { t } = useTranslation();
const uiTimezone = useSelector((state: RootState) => state.ui.timezone);
const navigate = useNavigate();
// DATA FETCHING HERE
const getHeaders = (t: Function, uiTimezone: string) => {
const headers: Header<Check>[] = [
{
id: "monitorName",
content: t("common.table.headers.monitor"),
render: (row) => {
return (
monitors?.find((monitor) => monitor.id === row.metadata.monitorId)?.name ||
"N/A"
);
},
},
{
id: "status",
content: "Status",
render: (row) => {
return <StatusLabel status={row.status as MonitorStatus} />;
},
},
{
id: "date",
content: t("checks.table.headers.dateTime"),
render: (row) => {
return formatDateWithTz(
row.createdAt,
"ddd, MMMM D, YYYY, HH:mm A",
uiTimezone
);
},
},
{
id: "statusCode",
content: t("checks.table.headers.statusCode"),
render: (row) => {
const code = row.statusCode;
if (!code) return "N/A";
const value = code < 300 ? "positive" : code < 400 ? "neutral" : "negative";
return (
<ValueLabel
value={value}
text={String(code)}
/>
);
},
},
{
id: "message",
content: t("common.table.headers.message"),
render: (row) => {
return row.message || "N/A";
},
},
];
return headers;
};
const headers = getHeaders(t, uiTimezone);
const handlePageChange = (
_e: React.MouseEvent<HTMLButtonElement> | null,
newPage: number
) => {
setPage(newPage);
};
const handleRowsPerPageChange = (
e: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>
) => {
const value = Number(e.target.value);
setPage(0);
setRowsPerPage(value);
};
return (
<Box>
<Table
headers={headers}
data={checks}
onRowClick={(row) => {
navigate(`/checks/${row.id}`);
}}
emptyViewText={t("checks.table.empty")}
/>
<Pagination
component="div"
count={checks.length}
page={page}
rowsPerPage={rowsPerPage}
onPageChange={handlePageChange}
onRowsPerPageChange={handleRowsPerPageChange}
/>
</Box>
);
};
+39 -4
View File
@@ -1,7 +1,10 @@
import Stack from "@mui/material/Stack";
import { BasePage, TotalChecksBox, DownChecksBox } from "@/Components/v2/design-elements";
import { HeaderTimeRange } from "@/Components/v2/common";
import { Select } from "@/Components/v2/inputs";
import IncidentTable from "./Components/IncidentTable/index.jsx";
import { useTheme } from "@mui/material";
import { MenuItem, useTheme } from "@mui/material";
import { useTranslation } from "react-i18next";
import { useState, useEffect, useMemo } from "react";
import { useParams } from "react-router-dom";
@@ -9,6 +12,12 @@ import { useGet } from "@/Hooks/UseApi";
import type { Monitor } from "@/Types/Monitor";
import type { ChecksSummary } from "@/Types/Check";
interface MonitorLookupEntry {
id: string;
name: string;
type: string;
}
const Checks = () => {
const { t } = useTranslation();
const theme = useTheme();
@@ -16,8 +25,9 @@ const Checks = () => {
// Local state
const [selectedMonitor, setSelectedMonitor] = useState<string>("0");
const [filter, setFilter] = useState<boolean | undefined>(undefined);
const [filter, setFilter] = useState<string | undefined>(undefined);
const [dateRange, setDateRange] = useState<string>("hour");
const [updateTrigger, setUpdateTrigger] = useState<boolean>(false);
// Data fetching with SWR
const monitorsUrl = "/monitors/team";
@@ -35,8 +45,6 @@ const Checks = () => {
error: summaryError,
} = useGet<ChecksSummary>(summaryUrl);
console.log(monitorsResponse, summaryResponse);
return (
<BasePage>
<Stack
@@ -46,6 +54,33 @@ const Checks = () => {
<TotalChecksBox n={summaryResponse?.totalChecks || 0} />
<DownChecksBox n={summaryResponse?.downChecks || 0} />
</Stack>
<Stack
direction={{ xs: "column", md: "row" }}
justifyContent={"space-between"}
alignItems={"center"}
>
<Select
value={selectedMonitor}
onChange={(e: any) => {
setSelectedMonitor(e.target.value);
}}
>
<MenuItem value="0">All monitors</MenuItem>
{monitorsResponse?.map((monitor) => (
<MenuItem
key={monitor.id}
value={monitor.id}
>
{monitor.name}
</MenuItem>
))}
</Select>
<HeaderTimeRange
dateRange={dateRange}
setDateRange={setDateRange}
/>
</Stack>
</BasePage>
);
};
@@ -366,27 +366,24 @@ class MongoChecksRepository implements IChecksRepository {
};
findSummaryByTeamId = async (teamId: string, dateRange: string) => {
const matchStage = {
const baseMatch = {
"metadata.teamId": new mongoose.Types.ObjectId(teamId),
status: false,
...(dateRangeLookup[dateRange] && {
createdAt: {
$gte: dateRangeLookup[dateRange],
},
}),
};
const checks = await CheckModel.aggregate([
{ $match: matchStage },
{
$group: {
_id: null,
totalChecks: { $sum: 1 },
downChecks: { $sum: { $cond: [{ $eq: ["$ack", false] }, 1, 0] } },
},
},
{ $project: { _id: 0 } },
const [totalResult, downResult] = await Promise.all([
CheckModel.countDocuments(baseMatch),
CheckModel.countDocuments({ ...baseMatch, status: false }),
]);
return checks[0] ?? { totalChecks: 0, downChecks: 0 };
return {
totalChecks: totalResult,
downChecks: downResult,
};
};
deleteByMonitorId = async (monitorId: string): Promise<number> => {