mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-01-26 11:54:11 -06:00
stat boxes
This commit is contained in:
61
client/src/Components/v2/DesignElements/StatBox.tsx
Normal file
61
client/src/Components/v2/DesignElements/StatBox.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import Stack from "@mui/material/Stack";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Box from "@mui/material/Box";
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
import { useMediaQuery } from "@mui/material";
|
||||
import type { PaletteKey } from "@/Utils/Theme/v2/theme";
|
||||
|
||||
type GradientBox = React.PropsWithChildren<{ palette?: PaletteKey }>;
|
||||
|
||||
export const GradientBox: React.FC<GradientBox> = ({ children, palette }) => {
|
||||
const theme = useTheme();
|
||||
const isSmall = useMediaQuery(theme.breakpoints.down("md"));
|
||||
const bg = palette
|
||||
? `linear-gradient(to bottom right, ${theme.palette[palette].main} 30%, ${theme.palette[palette].lowContrast} 70%)`
|
||||
: `linear-gradient(340deg, ${theme.palette.tertiary.main} 10%, ${theme.palette.primary.main} 45%)`;
|
||||
|
||||
return (
|
||||
<Box
|
||||
border={1}
|
||||
sx={{
|
||||
padding: `${theme.spacing(4)} ${theme.spacing(8)}`,
|
||||
width: isSmall
|
||||
? `calc(50% - (1 * ${theme.spacing(8)} / 2))`
|
||||
: `calc(25% - (3 * ${theme.spacing(8)} / 4))`,
|
||||
|
||||
borderStyle: "solid",
|
||||
borderRadius: 4,
|
||||
borderColor: theme.palette.primary.lowContrast,
|
||||
background: bg,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
type StatBoxProps = React.PropsWithChildren<{
|
||||
title: string;
|
||||
subtitle: string;
|
||||
palette?: PaletteKey;
|
||||
}>;
|
||||
|
||||
export const StatBox: React.FC<StatBoxProps> = ({
|
||||
title,
|
||||
subtitle,
|
||||
palette,
|
||||
children,
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const textColor = palette ? theme.palette[palette].contrastText : "inherit";
|
||||
|
||||
return (
|
||||
<GradientBox palette={palette}>
|
||||
<Stack>
|
||||
<Typography color={textColor}>{title}</Typography>
|
||||
<Typography color={textColor}>{subtitle}</Typography>
|
||||
{children}
|
||||
</Stack>
|
||||
</GradientBox>
|
||||
);
|
||||
};
|
||||
@@ -2,3 +2,4 @@ export { SplitBox as HorizontalSplitBox, ConfigBox } from "./SplitBox";
|
||||
export { BasePage } from "./BasePage";
|
||||
export { BGBox, UpStatusBox, DownStatusBox, PausedStatusBox } from "./StatusBox";
|
||||
export { DataTable as Table } from "./Table";
|
||||
export { GradientBox, StatBox } from "./StatBox";
|
||||
|
||||
@@ -1,17 +1,27 @@
|
||||
import { BasePage } from "@/Components/v2/DesignElements";
|
||||
import { HeaderControls } from "@/Components/v2/Monitors/HeaderControls";
|
||||
import Stack from "@mui/material/Stack";
|
||||
import { StatBox } from "@/Components/v2/DesignElements";
|
||||
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
import { useParams } from "react-router";
|
||||
import { useGet, usePatch, type ApiResponse } from "@/Hooks/v2/UseApi";
|
||||
import { useState } from "react";
|
||||
import { getStatusPalette } from "@/Utils/MonitorUtils";
|
||||
import prettyMilliseconds from "pretty-ms";
|
||||
|
||||
const UptimeDetailsPage = () => {
|
||||
const { id } = useParams();
|
||||
const theme = useTheme();
|
||||
|
||||
// Local state
|
||||
const [range, setRange] = useState("30m");
|
||||
|
||||
const { response, loading, error, refetch } = useGet<ApiResponse>(
|
||||
`/monitors/${id}?range=${range}`
|
||||
`/monitors/${id}?embedChecks=true&range=${range}`,
|
||||
|
||||
{},
|
||||
{ refreshInterval: 30000 }
|
||||
);
|
||||
const {
|
||||
patch,
|
||||
@@ -19,11 +29,27 @@ const UptimeDetailsPage = () => {
|
||||
error: postError,
|
||||
} = usePatch<ApiResponse>(`/monitors/${id}/active`);
|
||||
|
||||
const monitor = response?.data || null;
|
||||
const monitor = response?.data?.monitor || null;
|
||||
if (!monitor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const stats = response?.data?.stats || null;
|
||||
|
||||
const streakDuration = stats?.currentStreakStartedAt
|
||||
? Date.now() - stats?.currentStreakStartedAt
|
||||
: 0;
|
||||
|
||||
const lastChecked = stats?.lastCheckTimestamp
|
||||
? Date.now() - stats?.lastCheckTimestamp
|
||||
: -1;
|
||||
|
||||
const checks = response?.data?.checks || null;
|
||||
|
||||
console.log(response);
|
||||
|
||||
const palette = getStatusPalette(monitor.status);
|
||||
|
||||
return (
|
||||
<BasePage>
|
||||
<HeaderControls
|
||||
@@ -32,6 +58,28 @@ const UptimeDetailsPage = () => {
|
||||
isPatching={isPatching}
|
||||
refetch={refetch}
|
||||
/>
|
||||
<Stack
|
||||
direction="row"
|
||||
gap={theme.spacing(8)}
|
||||
>
|
||||
<StatBox
|
||||
palette={palette}
|
||||
title="Active for"
|
||||
subtitle={prettyMilliseconds(streakDuration, { secondsDecimalDigits: 0 })}
|
||||
/>
|
||||
<StatBox
|
||||
title="Last check"
|
||||
subtitle={
|
||||
lastChecked >= 0
|
||||
? `${prettyMilliseconds(lastChecked, { secondsDecimalDigits: 0 })} ago`
|
||||
: "N/A"
|
||||
}
|
||||
/>
|
||||
<StatBox
|
||||
title="Last response time"
|
||||
subtitle={stats?.lastResponseTime ? `${stats?.lastResponseTime} ms` : "N/A"}
|
||||
/>
|
||||
</Stack>
|
||||
</BasePage>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -18,7 +18,7 @@ const UptimeMonitors = () => {
|
||||
const theme = useTheme();
|
||||
const isSmall = useMediaQuery(theme.breakpoints.down("md"));
|
||||
|
||||
const { response, loading } = useGet<ApiResponse>("/monitors?embedChecks=true");
|
||||
const { response, loading } = useGet<ApiResponse>("/monitors?embedChecks=true", {});
|
||||
const monitors = response?.data ?? ([] as IMonitor[]);
|
||||
|
||||
if (monitors.length === 0 && !loading) {
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
import type { MonitorStatus } from "@/Types/Monitor";
|
||||
import type { PaletteKey } from "./Theme/v2/theme";
|
||||
export const getStatusPalette = (status: MonitorStatus): PaletteKey => {
|
||||
const paletteMap: Record<MonitorStatus, PaletteKey> = {
|
||||
up: "success",
|
||||
down: "error",
|
||||
initializing: "warning",
|
||||
};
|
||||
return paletteMap[status];
|
||||
};
|
||||
|
||||
export const getStatusColor = (status: MonitorStatus, theme: any): string => {
|
||||
const statusColors: Record<MonitorStatus, string> = {
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
import { createTheme } from "@mui/material";
|
||||
import { lightPalette, darkPalette, typographyLevels } from "./palette";
|
||||
|
||||
import type { Theme } from "@mui/material/styles";
|
||||
|
||||
export type PaletteKey = {
|
||||
[K in keyof Theme["palette"]]: Theme["palette"][K] extends { main: any } ? K : never;
|
||||
}[keyof Theme["palette"]];
|
||||
|
||||
const fontFamilyPrimary = '"Inter" , sans-serif';
|
||||
const shadow =
|
||||
"0px 4px 24px -4px rgba(16, 24, 40, 0.08), 0px 3px 3px -3px rgba(16, 24, 40, 0.03)";
|
||||
|
||||
Reference in New Issue
Block a user