add status header and chatbot

This commit is contained in:
Alex Holliday
2025-02-20 19:02:24 -08:00
parent e22920ebf9
commit 7ea0e0134c
5 changed files with 209 additions and 50 deletions
@@ -0,0 +1,48 @@
// Components
import { Stack, Typography } from "@mui/material";
import { ColContainer } from "../../../../../Components/StandardContainer";
import SmartToyIcon from "@mui/icons-material/SmartToy";
import Dot from "../../../../../Components/Dot";
// Utils
import { useTheme } from "@emotion/react";
const MESSAGES = [
"I've checked the network status, and we're seeing excellent performance across all regions.",
"The network is stable and functioning optimally. All connections are active and stable.",
"I've reviewed the network status, and everything looks great. No issues detected.",
"The network is up and running smoothly. All connections are active and stable.",
"I've checked the network status, and everything is looking good. No issues detected.",
];
const ChatBot = ({ sx }) => {
const theme = useTheme();
return (
<ColContainer
backgroundColor={theme.palette.chatbot.background}
sx={{ ...sx }}
>
<Stack
direction="row"
alignItems="center"
gap={theme.spacing(4)}
>
<SmartToyIcon sx={{ color: theme.palette.chatbot.textAccent }} />
<Typography color={theme.palette.chatbot.textAccent}>Status Bot</Typography>
<Dot
color={theme.palette.chatbot.textAccent}
style={{ opacity: 0.4 }}
/>
<Typography
variant="body2"
color={theme.palette.chatbot.textAccent}
sx={{ opacity: 0.4 }}
>
Now
</Typography>
</Stack>
<Typography>{MESSAGES[Math.floor(Math.random() * MESSAGES.length)]}</Typography>
</ColContainer>
);
};
export default ChatBot;
@@ -1,7 +1,8 @@
import { Stack, Typography, List, ListItem } from "@mui/material";
import { Stack, Typography } from "@mui/material";
import { useTheme } from "@emotion/react";
import PulseDot from "../../../../../Components/Animated/PulseDot";
import "flag-icons/css/flag-icons.min.css";
import { ColContainer } from "../../../../../Components/StandardContainer";
const BASE_BOX_PADDING_VERTICAL = 16;
const BASE_BOX_PADDING_HORIZONTAL = 8;
@@ -14,21 +15,9 @@ const DeviceTicker = ({ data, width = "100%", connectionStatus }) => {
};
return (
<Stack
direction="column"
gap={theme.spacing(2)}
width={width}
sx={{
padding: `${theme.spacing(BASE_BOX_PADDING_VERTICAL)} ${theme.spacing(BASE_BOX_PADDING_HORIZONTAL)}`,
backgroundColor: theme.palette.background.main,
border: 1,
borderStyle: "solid",
borderColor: theme.palette.primary.lowContrast,
}}
>
<ColContainer>
<Stack
direction="row"
justifyContent={"center"}
gap={theme.spacing(4)}
>
<PulseDot color={statusColor[connectionStatus]} />
@@ -41,30 +30,53 @@ const DeviceTicker = ({ data, width = "100%", connectionStatus }) => {
{connectionStatus === "up" ? "Connected" : "No connection"}
</Typography>
</Stack>
<List>
{data.slice(Math.max(data.length - 5, 0)).map((dataPoint) => {
const countryCode = dataPoint?.countryCode?.toLowerCase() ?? null;
const flag = countryCode ? `fi fi-${countryCode}` : null;
return (
<ListItem key={Math.random()}>
<Stack direction="column">
<Stack
direction="row"
alignItems="center"
gap={theme.spacing(4)}
>
{flag && <span className={flag} />}
<Typography variant="h2">{dataPoint?.city || "Unknown"}</Typography>
</Stack>
<Typography variant="p">{`Response time: ${Math.floor(dataPoint?.responseTime ?? 0)} ms`}</Typography>
<Typography variant="p">{`UPT burned: ${dataPoint.uptBurnt}`}</Typography>
<Typography variant="p">{`${dataPoint?.device?.manufacturer} ${dataPoint?.device?.model}`}</Typography>
</Stack>
</ListItem>
);
})}
</List>
</Stack>
<table>
<thead>
<tr>
<th style={{ textAlign: "left" }}>
<Typography>COUNTRY</Typography>
</th>
<th style={{ textAlign: "left" }}>
<Typography>CITY</Typography>
</th>
<th style={{ textAlign: "right" }}>
<Typography>RESPONSE</Typography>
</th>
<th style={{ textAlign: "right" }}>
<Typography>UPT BURNED</Typography>
</th>
</tr>
</thead>
<tbody>
{data.map((dataPoint) => {
const countryCode = dataPoint?.countryCode?.toLowerCase() ?? null;
const flag = countryCode ? `fi fi-${countryCode}` : null;
const city = dataPoint?.city !== "" ? dataPoint?.city : "Unknown";
return (
<tr key={Math.random()}>
<td style={{ padding: theme.spacing(4) }}>
<Typography>
{flag ? <span className={flag} /> : null}{" "}
{countryCode?.toUpperCase() ?? "N/A"}
</Typography>
</td>
<td>
<Typography>{city}</Typography>
</td>
<td style={{ textAlign: "right" }}>
<Typography>{Math.floor(dataPoint.responseTime)} ms</Typography>
</td>
<td style={{ textAlign: "right" }}>
<Typography color={theme.palette.warning.main}>
+{dataPoint.uptBurnt}
</Typography>
</td>
</tr>
);
})}
</tbody>
</table>
</ColContainer>
);
};
@@ -28,12 +28,17 @@ const DistributedUptimeMap = ({ width = "100%", checks }) => {
useEffect(() => {
if (mapContainer.current && !map.current) {
const initialStyle = buildStyle(initialTheme.current, initialMode.current);
map.current = new maplibregl.Map({
container: mapContainer.current,
style: initialStyle,
center: [0, 20],
zoom: 0.8,
attributionControl: false,
canvasContextAttributes: {
antialias: true,
preserveDrawingBuffer: true,
},
});
}
map.current.on("load", () => {
@@ -87,6 +92,10 @@ const DistributedUptimeMap = ({ width = "100%", checks }) => {
ref={mapContainer}
style={{
width: width,
borderRadius: theme.spacing(4),
borderColor: theme.palette.primary.lowContrast,
borderStyle: "solid",
borderWidth: 1,
}}
/>
);
@@ -1,6 +1,6 @@
// Components
import { Stack } from "@mui/material";
import StatBox from "../../../../../Components/StatBox";
import InfoBox from "../../../../../Components/InfoBox";
import LastUpdate from "../LastUpdate";
import UptLogo from "../../../../../assets/icons/upt_logo.png";
@@ -14,17 +14,21 @@ const StatBoxes = ({ monitor, lastUpdateTrigger }) => {
return (
<Stack
direction="row"
justifyContent="space-between"
gap={theme.spacing(8)}
>
<StatBox
<InfoBox
sx={{ flex: 1 }}
heading="Avg Response Time"
subHeading={`${Math.floor(monitor?.avgResponseTime ?? 0)} ms`}
/>
<StatBox
<InfoBox
sx={{ flex: 1 }}
heading="Checking every"
subHeading={`${(monitor?.interval ?? 0) / 1000} seconds`}
/>
<StatBox
<InfoBox
sx={{ flex: 1 }}
heading={"Last check"}
subHeading={
<LastUpdate
@@ -33,7 +37,8 @@ const StatBoxes = ({ monitor, lastUpdateTrigger }) => {
/>
}
/>
<StatBox
<InfoBox
sx={{ flex: 1 }}
heading="Last server push"
subHeading={
<LastUpdate
@@ -43,13 +48,6 @@ const StatBoxes = ({ monitor, lastUpdateTrigger }) => {
/>
}
/>
<StatBox
heading="UPT Burned"
subHeading={monitor?.totalUptBurnt ?? 0}
img={UptLogo}
alt="Upt Logo"
/>
</Stack>
);
};
@@ -0,0 +1,92 @@
// Components
import { ColContainer } from "../../../../../Components/StandardContainer";
import { Stack, Typography } from "@mui/material";
import PulseDot from "../../../../../Components/Animated/PulseDot";
import LastUpdate from "../LastUpdate";
import ChatBot from "../Chatbot";
import ShareComponent from "../../../../../Components/ShareComponent";
// Utils
import { useTheme } from "@emotion/react";
import PropTypes from "prop-types";
const StatusHeader = ({ monitor, connectionStatus, elementToCapture }) => {
const theme = useTheme();
const COLOR_MAP = {
up: theme.palette.successSecondary.main,
down: theme.palette.error.lowContrast,
};
const MSG_MAP = {
up: "All Systems Operational",
down: "Last Check Failed",
};
const PULSE_COLOR = {
up: theme.palette.success.main,
down: theme.palette.error.main,
};
let bgColor = COLOR_MAP[connectionStatus];
return (
<ColContainer backgroundColor={bgColor}>
<Stack
direction="row"
justifyContent="space-between"
gap={theme.spacing(8)}
>
<Stack
direction="row"
gap={theme.spacing(8)}
>
<PulseDot color={PULSE_COLOR[connectionStatus]} />
<Stack>
<Stack
direction="row"
gap={theme.spacing(8)}
>
<Typography
variant="h1"
color={theme.palette.success.lowContrast}
>
{MSG_MAP[connectionStatus]}
</Typography>
<Typography
variant="body2"
borderRadius={theme.spacing(8)}
padding={theme.spacing(4)}
backgroundColor={theme.palette.successSecondary.lowContrast}
color={theme.palette.success.lowContrast}
>
Uptime: {(monitor.totalUptime * 100).toFixed(2)}%
</Typography>
</Stack>
<Typography
variant="body2"
color={theme.palette.success.lowContrast}
>
Last updated{" "}
<LastUpdate
suffix={"seconds ago"}
lastUpdateTime={monitor.timeSinceLastCheck}
/>
</Typography>
</Stack>
</Stack>
<ShareComponent
elementToCapture={elementToCapture}
fileName={monitor.name}
/>
</Stack>
<ChatBot sx={{ marginTop: theme.spacing(10) }} />
</ColContainer>
);
};
StatusHeader.propTypes = {
monitor: PropTypes.object,
connectionStatus: PropTypes.string,
};
export default StatusHeader;