fix: refactoring monitorsList and minor fixes based on feedback
This commit is contained in:
1 parent
d3d00353a1
commit
622446c308
4 files changed
+109
-136
No files matched your search
@@ -29,6 +29,7 @@ import type { StatusPageFormData } from "@/Validation/statusPage";
|
||||
import { useGet, usePost, usePut, useDelete } from "@/Hooks/UseApi";
|
||||
import type { Monitor } from "@/Types/Monitor";
|
||||
import type { MonitorDisplayType, StatusPageResponse } from "@/Types/StatusPage";
|
||||
import { getMonitorTypeLabel } from "@/Types/StatusPage";
|
||||
import timezones from "@/Utils/timezones.json";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import axios from "axios";
|
||||
@@ -39,22 +40,6 @@ interface TimezoneOption {
|
||||
name: string;
|
||||
}
|
||||
|
||||
const MONITOR_TYPE_KEYS: Record<string, string> = {
|
||||
http: "pages.common.monitors.monitorTypes.optionHttp",
|
||||
ping: "pages.common.monitors.monitorTypes.optionPing",
|
||||
docker: "pages.common.monitors.monitorTypes.optionDocker",
|
||||
port: "pages.common.monitors.monitorTypes.optionPort",
|
||||
game: "pages.common.monitors.monitorTypes.optionGame",
|
||||
grpc: "pages.common.monitors.monitorTypes.optionGrpc",
|
||||
websocket: "pages.common.monitors.monitorTypes.optionWebSocket",
|
||||
hardware: "pages.common.monitors.monitorTypes.optionHardware",
|
||||
};
|
||||
|
||||
const getMonitorTypeLabel = (type: string, t: (key: string) => string): string => {
|
||||
const i18nMonitorKey = MONITOR_TYPE_KEYS[type];
|
||||
return i18nMonitorKey ? t(i18nMonitorKey) : type;
|
||||
};
|
||||
|
||||
const CreateStatusPage = () => {
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
@@ -111,7 +96,7 @@ const CreateStatusPage = () => {
|
||||
});
|
||||
|
||||
return typesSet.size ? Array.from(typesSet) : ["uptime"];
|
||||
}, [JSON.stringify(watchedMonitorIds), monitors]);
|
||||
}, [watchedMonitorIds, monitors]);
|
||||
|
||||
useEffect(() => {
|
||||
form.setValue("type", computedTypes);
|
||||
|
||||
@@ -12,6 +12,7 @@ import { useSelector } from "react-redux";
|
||||
import { useState } from "react";
|
||||
import type { Monitor } from "@/Types/Monitor";
|
||||
import type { StatusPage } from "@/Types/StatusPage";
|
||||
import { getMonitorTypeLabel } from "@/Types/StatusPage";
|
||||
import type { RootState } from "@/Types/state";
|
||||
import { LAYOUT, SPACING } from "@/Utils/Theme/constants";
|
||||
|
||||
@@ -25,81 +26,98 @@ interface MonitorsListProps {
|
||||
}
|
||||
|
||||
const getMonitorBadgeStyles = (monitorType: string, theme: Theme) => {
|
||||
// for future: specific badge styles can be defined here based on monitor type
|
||||
const badgeConfig: Record<string, { color: string; bg: string }> = {
|
||||
hardware: {
|
||||
color: theme.palette.background.paper,
|
||||
bg: theme.palette.info.light,
|
||||
},
|
||||
http: {
|
||||
color: theme.palette.background.paper,
|
||||
bg: theme.palette.success.light,
|
||||
},
|
||||
ping: {
|
||||
color: theme.palette.background.paper,
|
||||
bg: theme.palette.success.light,
|
||||
},
|
||||
port: {
|
||||
color: theme.palette.background.paper,
|
||||
bg: theme.palette.success.light,
|
||||
},
|
||||
docker: {
|
||||
color: theme.palette.background.paper,
|
||||
bg: theme.palette.success.light,
|
||||
},
|
||||
pagespeed: {
|
||||
color: theme.palette.background.paper,
|
||||
bg: theme.palette.success.light,
|
||||
},
|
||||
game: {
|
||||
color: theme.palette.background.paper,
|
||||
bg: theme.palette.success.light,
|
||||
},
|
||||
};
|
||||
|
||||
const config = badgeConfig[monitorType] ?? badgeConfig.http;
|
||||
const bg =
|
||||
monitorType === "hardware" ? theme.palette.info.light : theme.palette.success.light;
|
||||
return {
|
||||
backgroundColor: config.bg,
|
||||
color: config.color,
|
||||
backgroundColor: bg,
|
||||
color: theme.palette.background.paper,
|
||||
padding: `${theme.spacing(SPACING.SM)} ${theme.spacing(SPACING.LG)}`,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
};
|
||||
};
|
||||
|
||||
// Extracted component for monitor content below the header row
|
||||
const MonitorHeader = ({
|
||||
monitor,
|
||||
showURL,
|
||||
}: {
|
||||
monitor: StatusPageMonitor;
|
||||
showURL: boolean;
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
gap={theme.spacing(LAYOUT.XS)}
|
||||
mb={theme.spacing(LAYOUT.XS)}
|
||||
>
|
||||
<Box sx={{ overflow: "hidden", minWidth: 0, flex: 1 }}>
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
gap={theme.spacing(SPACING.LG)}
|
||||
mb={theme.spacing(SPACING.SM)}
|
||||
>
|
||||
<Typography
|
||||
variant="h6"
|
||||
sx={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}
|
||||
>
|
||||
{monitor.name}
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="caption"
|
||||
sx={getMonitorBadgeStyles(monitor.type ?? "", theme)}
|
||||
>
|
||||
{getMonitorTypeLabel(monitor.type, t)}
|
||||
</Typography>
|
||||
</Stack>
|
||||
{showURL && monitor.url && (
|
||||
<Typography
|
||||
variant="body2"
|
||||
color={theme.palette.text.secondary}
|
||||
sx={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}
|
||||
>
|
||||
{monitor.url}
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
<StatusLabel status={monitor.status} />
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
const MonitorContent = ({
|
||||
monitor,
|
||||
statusPage,
|
||||
chartType,
|
||||
}: {
|
||||
monitor: Monitor & { checks?: Monitor["recentChecks"] };
|
||||
monitor: StatusPageMonitor;
|
||||
statusPage: StatusPage;
|
||||
chartType: string;
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const isInfrastructureMonitor = monitor?.type === "hardware";
|
||||
|
||||
if (isInfrastructureMonitor) {
|
||||
if (statusPage.showInfrastructure === false) {
|
||||
return null;
|
||||
}
|
||||
if (monitor.type === "hardware") {
|
||||
if (statusPage.showInfrastructure === false) return null;
|
||||
return <InfrastructureMetrics monitor={monitor} />;
|
||||
}
|
||||
|
||||
if (statusPage.showCharts === false) {
|
||||
return null;
|
||||
}
|
||||
if (statusPage.showCharts === false) return null;
|
||||
|
||||
const checks = monitor.checks?.slice().reverse() ?? [];
|
||||
return (
|
||||
<Box sx={{ overflow: "hidden", minWidth: 0, flex: 1, mb: theme.spacing(SPACING.LG) }}>
|
||||
{chartType === "histogram" ? (
|
||||
<HistogramResponseTime
|
||||
height={{ xs: 50, md: 100 }}
|
||||
gap={{ xs: theme.spacing(SPACING.SM), md: theme.spacing(LAYOUT.SM) }}
|
||||
checks={monitor?.checks?.slice().reverse() ?? []}
|
||||
checks={checks}
|
||||
/>
|
||||
) : (
|
||||
<HeatmapResponseTime checks={monitor?.checks?.slice().reverse() ?? []} />
|
||||
<HeatmapResponseTime checks={checks} />
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
@@ -108,17 +126,15 @@ const MonitorContent = ({
|
||||
export const MonitorsList = ({ statusPage, monitors }: MonitorsListProps) => {
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const showURL = useSelector((state: RootState) => state.ui?.showURL);
|
||||
|
||||
const [chartType, setChartType] = useState<"histogram" | "heatmap">("histogram");
|
||||
|
||||
return (
|
||||
<Stack gap={theme.spacing(LAYOUT.MD)}>
|
||||
{statusPage.showCharts && (
|
||||
<Stack
|
||||
direction={"row"}
|
||||
alignItems={"center"}
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
gap={theme.spacing(LAYOUT.SM)}
|
||||
>
|
||||
<Typography>{t("pages.statusPages.monitorsList.chartTypeHeatmap")}</Typography>
|
||||
@@ -126,9 +142,7 @@ export const MonitorsList = ({ statusPage, monitors }: MonitorsListProps) => {
|
||||
dualOption
|
||||
value={chartType}
|
||||
checked={chartType === "histogram"}
|
||||
onChange={(e) => {
|
||||
setChartType(e.target.checked ? "histogram" : "heatmap");
|
||||
}}
|
||||
onChange={(e) => setChartType(e.target.checked ? "histogram" : "heatmap")}
|
||||
/>
|
||||
<Typography>
|
||||
{t("pages.statusPages.monitorsList.chartTypeHistogram")}
|
||||
@@ -136,69 +150,22 @@ export const MonitorsList = ({ statusPage, monitors }: MonitorsListProps) => {
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
{monitors?.map((monitor) => {
|
||||
return (
|
||||
<BaseBox
|
||||
key={monitor.id}
|
||||
padding={theme.spacing(LAYOUT.MD)}
|
||||
>
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
gap={theme.spacing(LAYOUT.XS)}
|
||||
mb={theme.spacing(LAYOUT.XS)}
|
||||
>
|
||||
<Box sx={{ overflow: "hidden", minWidth: 0, flex: 1 }}>
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
gap={theme.spacing(SPACING.LG)}
|
||||
mb={theme.spacing(SPACING.SM)}
|
||||
>
|
||||
<Typography
|
||||
variant="h6"
|
||||
sx={{
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
whiteSpace: "nowrap",
|
||||
}}
|
||||
>
|
||||
{monitor.name}
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="caption"
|
||||
sx={getMonitorBadgeStyles(monitor?.type ?? "", theme)}
|
||||
>
|
||||
{t(
|
||||
`pages.common.monitors.monitorTypes.option${monitor?.type.charAt(0).toUpperCase() + monitor?.type.slice(1)}`
|
||||
)}
|
||||
</Typography>
|
||||
</Stack>
|
||||
{showURL && monitor?.url && (
|
||||
<Typography
|
||||
variant="body2"
|
||||
color={theme.palette.text.secondary}
|
||||
sx={{
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
whiteSpace: "nowrap",
|
||||
}}
|
||||
>
|
||||
{monitor.url}
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
<StatusLabel status={monitor?.status} />
|
||||
</Stack>
|
||||
<MonitorContent
|
||||
monitor={monitor}
|
||||
statusPage={statusPage}
|
||||
chartType={chartType}
|
||||
/>
|
||||
</BaseBox>
|
||||
);
|
||||
})}
|
||||
{monitors.map((monitor) => (
|
||||
<BaseBox
|
||||
key={monitor.id}
|
||||
padding={theme.spacing(LAYOUT.MD)}
|
||||
>
|
||||
<MonitorHeader
|
||||
monitor={monitor}
|
||||
showURL={showURL}
|
||||
/>
|
||||
<MonitorContent
|
||||
monitor={monitor}
|
||||
statusPage={statusPage}
|
||||
chartType={chartType}
|
||||
/>
|
||||
</BaseBox>
|
||||
))}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
@@ -1,6 +1,26 @@
|
||||
import type { Monitor } from "@/Types/Monitor";
|
||||
import type { Monitor, MonitorType } from "@/Types/Monitor";
|
||||
export type MonitorDisplayType = "uptime" | "infrastructure";
|
||||
|
||||
export const MONITOR_TYPE_KEYS: Partial<Record<MonitorType, string>> = {
|
||||
http: "pages.common.monitors.monitorTypes.optionHttp",
|
||||
ping: "pages.common.monitors.monitorTypes.optionPing",
|
||||
docker: "pages.common.monitors.monitorTypes.optionDocker",
|
||||
port: "pages.common.monitors.monitorTypes.optionPort",
|
||||
game: "pages.common.monitors.monitorTypes.optionGame",
|
||||
grpc: "pages.common.monitors.monitorTypes.optionGrpc",
|
||||
websocket: "pages.common.monitors.monitorTypes.optionWebSocket",
|
||||
hardware: "pages.common.monitors.monitorTypes.optionHardware",
|
||||
pagespeed: "pages.common.monitors.monitorTypes.optionPagespeed",
|
||||
};
|
||||
|
||||
export const getMonitorTypeLabel = (
|
||||
type: MonitorType,
|
||||
t: (key: string) => string
|
||||
): string => {
|
||||
const key = MONITOR_TYPE_KEYS[type];
|
||||
return key ? t(key) : type;
|
||||
};
|
||||
|
||||
export interface StatusPage {
|
||||
id: string;
|
||||
userId: string;
|
||||
|
||||
@@ -551,7 +551,8 @@
|
||||
"optionWebSocketDescription": "Monitor WebSocket endpoints for connection health and response time.",
|
||||
"optionHttpDescription": "Use HTTP(S) to monitor your website or API endpoint.",
|
||||
"optionPingDescription": "Use ICMP Ping to monitor if a server is online.",
|
||||
"optionPortDescription": "Monitor if a specific port on a server is open."
|
||||
"optionPortDescription": "Monitor if a specific port on a server is open.",
|
||||
"title": "Type"
|
||||
},
|
||||
"thresholds": {
|
||||
"title": "Alert thresholds",
|
||||
|
||||
Reference in new issue
Block a user