diff --git a/client/src/Components/v2/design-elements/BasePage.tsx b/client/src/Components/v2/design-elements/BasePage.tsx index de20fc005..e5091091e 100644 --- a/client/src/Components/v2/design-elements/BasePage.tsx +++ b/client/src/Components/v2/design-elements/BasePage.tsx @@ -91,7 +91,7 @@ export const BasePage = ({ spacing={theme.spacing(10)} {...props} > - + {children} ); diff --git a/client/src/Components/v2/design-elements/Breadcrumb.tsx b/client/src/Components/v2/design-elements/Breadcrumb.tsx index e0a310c9f..8ecfd6ec6 100644 --- a/client/src/Components/v2/design-elements/Breadcrumb.tsx +++ b/client/src/Components/v2/design-elements/Breadcrumb.tsx @@ -11,13 +11,64 @@ const isId = (segment: string): boolean => { const actionSegments = ["create", "configure"]; -export const Breadcrumb = () => { +export const Breadcrumb = ({ + breadcrumbOverride, +}: { + breadcrumbOverride?: string[] | undefined; +}) => { const { t } = useTranslation(); const theme = useTheme(); const location = useLocation(); + // If override is an empty array, hide entirely + if (breadcrumbOverride !== undefined && breadcrumbOverride.length === 0) { + return null; + } + + // If override has items, render them directly + if (breadcrumbOverride !== undefined && breadcrumbOverride.length > 0) { + return ( + + } + sx={{ + fontSize: "14px", + marginBottom: theme.spacing(6), + "& .MuiBreadcrumbs-separator": { + color: theme.palette.text.secondary, + }, + }} + > + {breadcrumbOverride.map((item, index) => { + const isLast = index === breadcrumbOverride.length - 1; + return ( + + {item} + + ); + })} + + ); + } + + // Default behavior: use location pathname const segments = location.pathname.split("/").filter((x) => x); + if (segments.length === 0) { + return null; + } + // Build simplified breadcrumb: "uptime" or "uptime / details" or "uptime / create" const basePage = segments[0] || t("common.breadcrumbs.home"); diff --git a/client/src/Components/v2/inputs/Switch.tsx b/client/src/Components/v2/inputs/Switch.tsx index b8ae8c1ec..05be73a24 100644 --- a/client/src/Components/v2/inputs/Switch.tsx +++ b/client/src/Components/v2/inputs/Switch.tsx @@ -3,8 +3,12 @@ import Switch from "@mui/material/Switch"; import type { SwitchProps } from "@mui/material/Switch"; import { useTheme } from "@mui/material/styles"; -export const SwitchComponent = forwardRef( - function SwitchComponent({ sx, ...props }, ref) { +interface SwitchComponentProps extends SwitchProps { + dualOption?: boolean; +} + +export const SwitchComponent = forwardRef( + function SwitchComponent({ sx, dualOption = false, ...props }, ref) { const theme = useTheme(); const additionalSx = Array.isArray(sx) ? sx : sx ? [sx] : []; @@ -28,6 +32,12 @@ export const SwitchComponent = forwardRef( }, }, }, + ...(dualOption && { + "& .MuiSwitch-track": { + backgroundColor: theme.palette.primary.main, + opacity: 1, + }, + }), }, ...additionalSx, ]} diff --git a/client/src/Pages/StatusPage/Status/Components/HeaderStatusPageControls.tsx b/client/src/Pages/StatusPage/Status/Components/HeaderStatusPageControls.tsx index 87349d703..382bf08e4 100644 --- a/client/src/Pages/StatusPage/Status/Components/HeaderStatusPageControls.tsx +++ b/client/src/Pages/StatusPage/Status/Components/HeaderStatusPageControls.tsx @@ -72,7 +72,7 @@ export const HeaderStatusPageControls = ({ > )} - {isAdmin && ( + {isAdmin && !isPublic && ( { - return Monitors List; +interface MonitorsListProps { + statusPage: StatusPage; + monitors: StatusPageMonitor[]; +} + +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 ( + + {statusPage.showCharts !== false && ( + + {t("pages.statusPages.monitorsList.chartTypeHeatmap")} + { + setChartType(e.target.checked ? "histogram" : "heatmap"); + }} + /> + + {t("pages.statusPages.monitorsList.chartTypeHistogram")} + + + )} + + {monitors?.map((monitor) => { + const status = determineState(monitor); + return ( + + + + {monitor.name} + {showURL && ( + + {monitor.url} + + )} + + + + {statusPage.showCharts !== false && ( + + {chartType === "histogram" ? ( + + ) : ( + + )} + + )} + + ); + })} + + ); }; diff --git a/client/src/Pages/StatusPage/Status/index.tsx b/client/src/Pages/StatusPage/Status/index.tsx index 411234c15..41373b19a 100644 --- a/client/src/Pages/StatusPage/Status/index.tsx +++ b/client/src/Pages/StatusPage/Status/index.tsx @@ -3,6 +3,7 @@ import { StatusBar } from "@/Pages/StatusPage/Status/Components/StatusBar"; import { MonitorsList } from "@/Pages/StatusPage/Status/Components/MonitorsList"; import Typography from "@mui/material/Typography"; +import { useTheme } from "@mui/material"; import { useTranslation } from "react-i18next"; import { useIsAdmin } from "@/Hooks/useIsAdmin"; import { useLocation, useParams } from "react-router-dom"; @@ -11,6 +12,7 @@ import type { StatusPageResponse } from "@/Types/StatusPage"; import { HeaderStatusPageControls } from "./Components/HeaderStatusPageControls"; const StatusPageView = () => { + const theme = useTheme(); const { t } = useTranslation(); const { url } = useParams(); const isAdmin = useIsAdmin(); @@ -24,18 +26,21 @@ const StatusPageView = () => { const statusPage = data?.statusPage; const monitors = data?.monitors ?? []; - if (!statusPage || !monitors) { - return null; - } + if (!statusPage) return null; - if (monitors.length === 0) { - return "poo"; + let sx: React.CSSProperties = {}; + if (isPublic) { + sx.paddingTop = theme.spacing(20); + sx.paddingLeft = "20vw"; + sx.paddingRight = "20vw"; } return ( { {t("statusPageStatusServiceStatus")} - {JSON.stringify(data, null, 2)} ); }; diff --git a/client/src/Utils/MonitorUtils.ts b/client/src/Utils/MonitorUtils.ts index ed4e2894e..6a30b3fc4 100644 --- a/client/src/Utils/MonitorUtils.ts +++ b/client/src/Utils/MonitorUtils.ts @@ -2,6 +2,13 @@ import type { Monitor, MonitorStatus, MonitorType } from "@/Types/Monitor"; import type { PaletteKey } from "@/Utils/Theme/v2Theme"; import type { ValueType } from "@/Components/v2/design-elements/StatusLabel"; +export const determineState = (monitor: Monitor) => { + if (typeof monitor === "undefined") return "pending"; + if (monitor?.isActive === false) return "paused"; + if (monitor?.status === undefined) return "pending"; + return monitor?.status == true ? "up" : "down"; +}; + export const getMonitorPath = (type: MonitorType): string => { const pathMap: Record = { http: "uptime", diff --git a/client/src/locales/en.json b/client/src/locales/en.json index 3c78c3acf..affb70a73 100644 --- a/client/src/locales/en.json +++ b/client/src/locales/en.json @@ -479,6 +479,10 @@ "allDown": "All systems down", "degraded": "Degraded performance", "unknown": "Unknown status" + }, + "monitorsList": { + "chartTypeHeatmap": "Heatmap", + "chartTypeHistogram": "Histogram" } } },
{JSON.stringify(data, null, 2)}