- 11.1 MainLayout health indicator: warning MessageBar when fail2ban offline - 11.2 formatDate utility + TimezoneProvider + GET /api/setup/timezone - 11.3 Responsive sidebar: auto-collapse <640px, media query listener - 11.4 PageFeedback (PageLoading/PageError/PageEmpty), BanTable updated - 11.5 prefers-reduced-motion: disable sidebar transition - 11.6 WorldMap ARIA: role/tabIndex/aria-label/onKeyDown for countries - 11.7 Health transition logging (fail2ban_came_online/went_offline) - 11.8 Global handlers: Fail2BanConnectionError/ProtocolError -> 502 - 11.9 379 tests pass, 82% coverage, ruff+mypy+tsc+eslint clean - Timezone endpoint: setup_service.get_timezone, 5 new tests
96 lines
2.5 KiB
TypeScript
96 lines
2.5 KiB
TypeScript
/**
|
|
* TimezoneProvider.
|
|
*
|
|
* Fetches the IANA timezone configured during the BanGUI setup wizard and
|
|
* makes it available throughout the component tree via React Context.
|
|
*
|
|
* The timezone is fetched once at mount. On error or before the initial
|
|
* fetch resolves, the value defaults to ``"UTC"`` so date-formatting callers
|
|
* always receive a safe fallback.
|
|
*/
|
|
|
|
import {
|
|
createContext,
|
|
useCallback,
|
|
useContext,
|
|
useEffect,
|
|
useMemo,
|
|
useState,
|
|
} from "react";
|
|
import { fetchTimezone } from "../api/setup";
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Context definition
|
|
// ---------------------------------------------------------------------------
|
|
|
|
interface TimezoneContextValue {
|
|
/** IANA timezone string, e.g. ``"Europe/Berlin"`` or ``"UTC"``. */
|
|
timezone: string;
|
|
}
|
|
|
|
const TimezoneContext = createContext<TimezoneContextValue>({ timezone: "UTC" });
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Provider
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export interface TimezoneProviderProps {
|
|
children: React.ReactNode;
|
|
}
|
|
|
|
/**
|
|
* Wrap the application (or authenticated shell) with this provider to make the
|
|
* configured timezone available via {@link useTimezone}.
|
|
*
|
|
* @example
|
|
* ```tsx
|
|
* <TimezoneProvider>
|
|
* <App />
|
|
* </TimezoneProvider>
|
|
* ```
|
|
*/
|
|
export function TimezoneProvider({
|
|
children,
|
|
}: TimezoneProviderProps): React.JSX.Element {
|
|
const [timezone, setTimezone] = useState<string>("UTC");
|
|
|
|
const load = useCallback((): void => {
|
|
fetchTimezone()
|
|
.then((resp) => { setTimezone(resp.timezone); })
|
|
.catch(() => {
|
|
// Silently fall back to UTC; the backend may not be reachable yet.
|
|
});
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
load();
|
|
}, [load]);
|
|
|
|
const value = useMemo<TimezoneContextValue>(() => ({ timezone }), [timezone]);
|
|
|
|
return (
|
|
<TimezoneContext.Provider value={value}>{children}</TimezoneContext.Provider>
|
|
);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Hook
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/**
|
|
* Return the IANA timezone string configured during setup.
|
|
*
|
|
* Must be used inside a {@link TimezoneProvider}.
|
|
*
|
|
* @returns The configured timezone, e.g. ``"Europe/Berlin"``.
|
|
*
|
|
* @example
|
|
* ```tsx
|
|
* const { timezone } = useTimezone();
|
|
* const label = formatDate(item.created_at, timezone);
|
|
* ```
|
|
*/
|
|
export function useTimezone(): string {
|
|
return useContext(TimezoneContext).timezone;
|
|
}
|