refactor(status-page): tighten the refined-theme conversion

Apply review feedback:
- Type the styles factory's return as a named RefinedStyles interface so the consumer no longer needs `as Function` / `as any` casts on factory entries
- Lift toneColor / toneSoft to shared/overallStatus alongside the existing OverallTone type — the next three themes will need the same mapping
- Reuse heatLevel() in the legacy classPrefix branch of ThemedInfrastructure so both API paths share one threshold table
- Memoize the styles map in RefinedStatusPage so MUI's sx processor sees referential equality across renders that don't actually change tokens
- Make the dual-API gate in ThemedHeatmap / ThemedHistogram require ALL relevant sx props together so a partial migration can't silently fall back to the legacy class path
This commit is contained in:
gorkem-bwl committed 2026-04-27 18:30:04 -04:00
1 parent 163f5ec1ec
commit b95d8886b9
6 files changed
+92 -68

No files matched your search

@@ -1,4 +1,4 @@
import { useState } from "react";
import { useMemo, useState } from "react";
import Box from "@mui/material/Box";
import Stack from "@mui/material/Stack";
import { useTranslation } from "react-i18next";
@@ -27,7 +27,7 @@ interface Props {
export const RefinedStatusPage = ({ statusPage, monitors }: Props) => {
const { t } = useTranslation();
const { tokens, mode } = useStatusPageTheme();
const styles = refinedStyles(tokens, mode === "dark");
const styles = useMemo(() => refinedStyles(tokens, mode === "dark"), [tokens, mode]);
const [chartMode, setChartMode] = useState<"heatmap" | "histogram">("heatmap");
const overall = resolveOverallStatus(monitors, t);
@@ -62,7 +62,7 @@ export const RefinedStatusPage = ({ statusPage, monitors }: Props) => {
</Box>
<Box sx={styles.hero}>
<Box sx={(styles.statusDot as Function)(overall.tone)} />
<Box sx={styles.statusDot(overall.tone)} />
<Box sx={styles.statusCopy}>
<Box
component="h1"
@@ -79,7 +79,7 @@ export const RefinedStatusPage = ({ statusPage, monitors }: Props) => {
})}
</Box>
</Box>
<Box sx={(styles.heroIcon as Function)(overall.tone)}>{overall.icon}</Box>
<Box sx={styles.heroIcon(overall.tone)}>{overall.icon}</Box>
</Box>
{statusPage.showCharts && (
@@ -94,7 +94,7 @@ export const RefinedStatusPage = ({ statusPage, monitors }: Props) => {
role="radio"
aria-checked={chartMode === "heatmap"}
onClick={() => setChartMode("heatmap")}
sx={(styles.chartSwitchButton as Function)(chartMode === "heatmap")}
sx={styles.chartSwitchButton(chartMode === "heatmap")}
>
{t("pages.statusPages.monitorsList.chartTypeHeatmap")}
</Box>
@@ -104,7 +104,7 @@ export const RefinedStatusPage = ({ statusPage, monitors }: Props) => {
role="radio"
aria-checked={chartMode === "histogram"}
onClick={() => setChartMode("histogram")}
sx={(styles.chartSwitchButton as Function)(chartMode === "histogram")}
sx={styles.chartSwitchButton(chartMode === "histogram")}
>
{t("pages.statusPages.monitorsList.chartTypeHistogram")}
</Box>
@@ -151,7 +151,7 @@ export const RefinedStatusPage = ({ statusPage, monitors }: Props) => {
</Box>
<Box
component="span"
sx={(styles.badge as Function)(badgeTone)}
sx={styles.badge(badgeTone)}
>
{t(statusBadgeKey[monitor.status])}
</Box>
@@ -167,10 +167,7 @@ export const RefinedStatusPage = ({ statusPage, monitors }: Props) => {
gaugeLabelSx: styles.gaugeLabel,
gaugeValueSx: styles.gaugeValue,
gaugeBarSx: styles.gaugeBar,
gaugeFillSx: styles.gaugeFill as (
level: "ok" | "warm" | "hot",
width: number
) => any,
gaugeFillSx: styles.gaugeFill,
gaugeSubSx: styles.gaugeSub,
}}
/>
@@ -180,13 +177,13 @@ export const RefinedStatusPage = ({ statusPage, monitors }: Props) => {
<ThemedHeatmap
checks={monitor.recentChecks ?? []}
containerSx={styles.heatmap}
cellSx={styles.heatmapCell as (kind: any) => any}
cellSx={styles.heatmapCell}
/>
) : (
<ThemedHistogram
checks={monitor.recentChecks ?? []}
containerSx={styles.histogram}
barSx={styles.bar as (kind: any, h: number) => any}
barSx={styles.bar}
statsSx={styles.chartStats}
/>
))}
@@ -1,10 +1,53 @@
import type { SxProps, Theme } from "@mui/material/styles";
import type { StatusPageThemeTokens } from "../tokens";
import { type OverallTone, toneColor, toneSoft } from "../shared/overallStatus";
export type RefinedTone = "up" | "warn" | "down";
export type RefinedHeatCell = "fast" | "med" | "slow" | "down" | "empty";
export type RefinedBarKind = "up" | "down" | "empty";
export type RefinedGaugeFill = "ok" | "warm" | "hot";
export interface RefinedStyles {
page: SxProps<Theme>;
top: SxProps<Theme>;
brand: SxProps<Theme>;
logoMono: SxProps<Theme>;
logoImg: SxProps<Theme>;
company: SxProps<Theme>;
hero: SxProps<Theme>;
statusDot: (tone: OverallTone) => SxProps<Theme>;
statusCopy: SxProps<Theme>;
heroTitle: SxProps<Theme>;
heroSub: SxProps<Theme>;
heroIcon: (tone: OverallTone) => SxProps<Theme>;
chartSwitchWrap: SxProps<Theme>;
chartSwitch: SxProps<Theme>;
chartSwitchButton: (active: boolean) => SxProps<Theme>;
monitorList: SxProps<Theme>;
card: SxProps<Theme>;
cardRow: SxProps<Theme>;
cardLeft: SxProps<Theme>;
monitorName: SxProps<Theme>;
monitorMeta: SxProps<Theme>;
pill: SxProps<Theme>;
pillHardware: SxProps<Theme>;
monitorUrl: SxProps<Theme>;
badge: (tone: OverallTone) => SxProps<Theme>;
heatmap: SxProps<Theme>;
heatmapCell: (kind: RefinedHeatCell) => SxProps<Theme>;
histogram: SxProps<Theme>;
bar: (kind: RefinedBarKind, heightPct: number) => SxProps<Theme>;
chartStats: SxProps<Theme>;
infra: SxProps<Theme>;
infraEmpty: SxProps<Theme>;
gauge: SxProps<Theme>;
gaugeLabel: SxProps<Theme>;
gaugeValue: SxProps<Theme>;
gaugeBar: SxProps<Theme>;
gaugeFill: (level: RefinedGaugeFill, widthPct: number) => SxProps<Theme>;
gaugeSub: SxProps<Theme>;
footer: SxProps<Theme>;
}
const sansFontStack =
'-apple-system, BlinkMacSystemFont, "Inter", "Segoe UI", Roboto, sans-serif';
@@ -12,13 +55,10 @@ const cardShadow = "0 1px 2px rgba(10, 16, 32, 0.06), 0 6px 18px rgba(10, 16, 32
const cardShadowHover =
"0 2px 4px rgba(16, 24, 40, 0.06), 0 10px 24px rgba(16, 24, 40, 0.06)";
const toneColor = (tone: RefinedTone, t: StatusPageThemeTokens) =>
tone === "down" ? t.down : tone === "warn" ? t.warn : t.up;
const toneSoft = (tone: RefinedTone, t: StatusPageThemeTokens) =>
tone === "down" ? t.downSoft : tone === "warn" ? t.warnSoft : t.upSoft;
export const refinedStyles = (tokens: StatusPageThemeTokens, isDark: boolean) => {
export const refinedStyles = (
tokens: StatusPageThemeTokens,
isDark: boolean
): RefinedStyles => {
const heatCellBg: Record<RefinedHeatCell, string> = {
fast: tokens.up,
med: `color-mix(in srgb, ${tokens.up} 60%, #ffffff 40%)`,
@@ -39,7 +79,7 @@ export const refinedStyles = (tokens: StatusPageThemeTokens, isDark: boolean) =>
hot: tokens.down,
};
const sx: Record<string, any> = {
return {
page: {
flex: "1 0 auto",
maxWidth: 960,
@@ -78,11 +118,7 @@ export const refinedStyles = (tokens: StatusPageThemeTokens, isDark: boolean) =>
fontWeight: 700,
fontSize: 13,
},
logoImg: {
maxHeight: 32,
maxWidth: 120,
objectFit: "contain",
},
logoImg: { maxHeight: 32, maxWidth: 120, objectFit: "contain" },
company: { fontSize: 14 },
hero: {
@@ -96,7 +132,7 @@ export const refinedStyles = (tokens: StatusPageThemeTokens, isDark: boolean) =>
boxShadow: cardShadow,
mb: "20px",
},
statusDot: (tone: RefinedTone) => ({
statusDot: (tone) => ({
width: 10,
height: 10,
borderRadius: "50%",
@@ -114,17 +150,13 @@ export const refinedStyles = (tokens: StatusPageThemeTokens, isDark: boolean) =>
color: tokens.text,
},
heroSub: { m: 0, color: tokens.textMuted, fontSize: 13 },
heroIcon: (tone: RefinedTone) => ({
heroIcon: (tone) => ({
color: toneColor(tone, tokens),
display: "flex",
alignItems: "center",
}),
chartSwitchWrap: {
display: "flex",
justifyContent: "flex-end",
mb: "12px",
},
chartSwitchWrap: { display: "flex", justifyContent: "flex-end", mb: "12px" },
chartSwitch: {
display: "inline-flex",
border: `1px solid ${tokens.border}`,
@@ -133,7 +165,7 @@ export const refinedStyles = (tokens: StatusPageThemeTokens, isDark: boolean) =>
p: "3px",
gap: "2px",
},
chartSwitchButton: (active: boolean) => ({
chartSwitchButton: (active) => ({
border: 0,
background: active ? tokens.upSoft : "transparent",
fontFamily: "inherit",
@@ -199,29 +231,19 @@ export const refinedStyles = (tokens: StatusPageThemeTokens, isDark: boolean) =>
borderRadius: "999px",
fontWeight: 600,
},
pillHardware: isDark
? {
fontSize: 10,
textTransform: "uppercase",
letterSpacing: "0.08em",
padding: "2px 8px",
borderRadius: "999px",
fontWeight: 600,
color: "#60a5fa",
border: "1px solid rgba(96, 165, 250, 0.3)",
background: "rgba(96, 165, 250, 0.1)",
}
: {
fontSize: 10,
textTransform: "uppercase",
letterSpacing: "0.08em",
padding: "2px 8px",
borderRadius: "999px",
fontWeight: 600,
color: "#1d4ed8",
border: "1px solid rgba(29, 78, 216, 0.3)",
background: "rgba(29, 78, 216, 0.06)",
},
pillHardware: {
fontSize: 10,
textTransform: "uppercase",
letterSpacing: "0.08em",
padding: "2px 8px",
borderRadius: "999px",
fontWeight: 600,
color: isDark ? "#60a5fa" : "#1d4ed8",
border: `1px solid ${
isDark ? "rgba(96, 165, 250, 0.3)" : "rgba(29, 78, 216, 0.3)"
}`,
background: isDark ? "rgba(96, 165, 250, 0.1)" : "rgba(29, 78, 216, 0.06)",
},
monitorUrl: {
fontSize: 12,
color: tokens.textMuted,
@@ -232,7 +254,7 @@ export const refinedStyles = (tokens: StatusPageThemeTokens, isDark: boolean) =>
maxWidth: 280,
},
badge: (tone: RefinedTone) => ({
badge: (tone) => ({
fontSize: 11,
fontWeight: 600,
padding: "4px 10px",
@@ -249,7 +271,7 @@ export const refinedStyles = (tokens: StatusPageThemeTokens, isDark: boolean) =>
gap: "3px",
height: 42,
},
heatmapCell: (kind: RefinedHeatCell) => ({
heatmapCell: (kind) => ({
borderRadius: "2px",
background: heatCellBg[kind],
opacity: kind === "empty" ? 0.4 : 1,
@@ -265,7 +287,7 @@ export const refinedStyles = (tokens: StatusPageThemeTokens, isDark: boolean) =>
alignItems: "flex-end",
height: 42,
},
bar: (kind: RefinedBarKind, heightPct: number) => ({
bar: (kind, heightPct) => ({
background: barBg[kind],
borderRadius: "2px",
minHeight: 3,
@@ -318,7 +340,7 @@ export const refinedStyles = (tokens: StatusPageThemeTokens, isDark: boolean) =>
overflow: "hidden",
mt: "8px",
},
gaugeFill: (level: RefinedGaugeFill, widthPct: number) => ({
gaugeFill: (level, widthPct) => ({
display: "block",
height: "100%",
background: gaugeFillBg[level],
@@ -347,6 +369,4 @@ export const refinedStyles = (tokens: StatusPageThemeTokens, isDark: boolean) =>
},
},
};
return sx;
};
@@ -30,7 +30,7 @@ const classify = (check: CheckSnapshot): Exclude<HeatCellKind, "empty"> => {
export const ThemedHeatmap = ({ checks, containerSx, cellSx, classPrefix }: Props) => {
const { t } = useTranslation();
const useSxApi = Boolean(cellSx);
const useSxApi = Boolean(containerSx && cellSx);
const source = checks.slice(0, CELLS).reverse();
const padded: (CheckSnapshot | null)[] = [
@@ -36,7 +36,7 @@ export const ThemedHistogram = ({
statsGap = 1,
}: Props) => {
const { t } = useTranslation();
const useSxApi = Boolean(barSx);
const useSxApi = Boolean(containerSx && barSx && statsSx);
const { padded, max, avg, peak } = useMemo(() => {
const source = checks.slice(0, CELLS).reverse();
@@ -118,8 +118,8 @@ export const ThemedInfrastructure = ({ monitor, sxApi, classPrefix }: Props) =>
return (
<Box className={`${classPrefix}-infra`}>
{gauges.map((g) => {
const heatClass =
g.value > 85 ? `${classPrefix}-hot` : g.value > 70 ? `${classPrefix}-warm` : "";
const level = heatLevel(g.value);
const heatClass = level === "ok" ? "" : `${classPrefix}-${level}`;
return (
<Box
key={g.key}
@@ -9,9 +9,16 @@ import {
Wrench,
} from "lucide-react";
import type { Monitor, MonitorStatus } from "@/Types/Monitor";
import type { StatusPageThemeTokens } from "../tokens";
export type OverallTone = "up" | "warn" | "down";
export const toneColor = (tone: OverallTone, t: StatusPageThemeTokens): string =>
tone === "down" ? t.down : tone === "warn" ? t.warn : t.up;
export const toneSoft = (tone: OverallTone, t: StatusPageThemeTokens): string =>
tone === "down" ? t.downSoft : tone === "warn" ? t.warnSoft : t.upSoft;
export interface OverallStatus {
tone: OverallTone;
message: string;