Anonymous visitors to a public status page have no persisted Redux state, so ThemedHeatmap and ThemedHistogram tooltips always rendered timestamps in the hardcoded "America/Toronto" default — regardless of the timezone the operator configured. Even on the same domain, ThemedHeatmap and ThemedHistogram had no code path to consume the operator-configured statusPage.timezone returned by the API. - Add SYSTEM_TIMEZONE module constant and a small resolveTimezone() helper to TimeUtils.ts. formatDateWithTz's timezone parameter is now optional and defaults to the system tz, so callers don't need to rebuild the prop -> browser -> UTC chain inline. - Add a `timezone` field to StatusPageThemeContextValue so any themed component on the public page can read the page-wide tz without prop drilling (parallels how `theme` and `mode` are already exposed). - Wire statusPage.timezone into StatusPageThemeProvider from Pages/StatusPage/Status/index.tsx (both the public route and the admin preview). - ThemedHeatmap and ThemedHistogram now read tz from the context instead of useSelector(state.ui.timezone) — that selector was unreliable for anonymous visitors and removing it cuts a Redux subscription on the public page. - Replace the inline `data?.timezone || Intl.DateTimeFormat()...` expression in useStatusPageForm.ts with resolveTimezone(). Closes #3628.
52 lines
1.7 KiB
TypeScript
52 lines
1.7 KiB
TypeScript
import { useMemo } from "react";
|
|
import { statusPageSchema, type StatusPageFormData } from "@/Validation/statusPage";
|
|
import type { StatusPage } from "@/Types/StatusPage";
|
|
import {
|
|
DEFAULT_STATUS_PAGE_THEME,
|
|
DEFAULT_STATUS_PAGE_THEME_MODE,
|
|
} from "@/Types/StatusPage";
|
|
import type { Monitor } from "@/Types/Monitor";
|
|
import { resolveTimezone } from "@/Utils/TimeUtils";
|
|
|
|
interface UseStatusPageFormOptions {
|
|
data?: StatusPage | null;
|
|
monitors?: Monitor[] | null;
|
|
}
|
|
|
|
const generateDefaultUrl = () => Math.floor(Math.random() * 1000000).toString();
|
|
|
|
const transformLogo = (logo: StatusPage["logo"]): StatusPageFormData["logo"] => {
|
|
if (!logo || !logo.data) return null;
|
|
return {
|
|
data: `data:${logo.contentType};base64,${logo.data}`,
|
|
contentType: logo.contentType,
|
|
};
|
|
};
|
|
|
|
export const useStatusPageForm = ({
|
|
data = null,
|
|
monitors = null,
|
|
}: UseStatusPageFormOptions = {}) => {
|
|
return useMemo(() => {
|
|
const defaults: StatusPageFormData = {
|
|
companyName: data?.companyName || "",
|
|
url: data?.url || generateDefaultUrl(),
|
|
timezone: resolveTimezone(data?.timezone),
|
|
type: data?.type || ["uptime"],
|
|
color: data?.color || "#13715B",
|
|
monitors: data?.monitors || [],
|
|
isPublished: data?.isPublished ?? false,
|
|
showCharts: data?.showCharts ?? true,
|
|
showUptimePercentage: data?.showUptimePercentage ?? true,
|
|
showAdminLoginLink: data?.showAdminLoginLink ?? false,
|
|
showInfrastructure: data?.showInfrastructure ?? false,
|
|
customCSS: data?.customCSS || "",
|
|
theme: data?.theme ?? DEFAULT_STATUS_PAGE_THEME,
|
|
themeMode: data?.themeMode ?? DEFAULT_STATUS_PAGE_THEME_MODE,
|
|
logo: transformLogo(data?.logo),
|
|
};
|
|
|
|
return { schema: statusPageSchema, defaults };
|
|
}, [data, monitors]);
|
|
};
|