diff --git a/apps/web/src/pages/dashboard/DashboardPage.tsx b/apps/web/src/pages/dashboard/DashboardPage.tsx index aa4cf77..ae15cae 100644 --- a/apps/web/src/pages/dashboard/DashboardPage.tsx +++ b/apps/web/src/pages/dashboard/DashboardPage.tsx @@ -1,5 +1,6 @@ import { useCallback, useMemo } from "react"; import { useAuth } from "../../shared/auth"; +import { useTheme } from "../../shared/hooks"; import { useAisTargetPolling } from "../../features/aisPolling/useAisTargetPolling"; import type { DerivedLegacyVessel } from "../../features/legacyDashboard/model/types"; import { LEGACY_ALARM_KINDS } from "../../features/legacyDashboard/model/types"; @@ -58,6 +59,7 @@ function useLegacyIndex(data: LegacyVesselDataset | null) { export function DashboardPage() { const { user, logout } = useAuth(); + const { theme, toggleTheme } = useTheme(); const uid = user?.id ?? null; // ── Data fetching ── @@ -259,6 +261,8 @@ export function DashboardPage() { onLogoClick={onLogoClick} userName={user?.name} onLogout={logout} + theme={theme} + onToggleTheme={toggleTheme} /> (() => { + const t = readTheme(); + applyTheme(t); + return t; + }); + + useEffect(() => { + applyTheme(theme); + }, [theme]); + + const setTheme = useCallback((t: Theme) => { + setThemeState(t); + try { + localStorage.setItem(STORAGE_KEY, t); + } catch { + // quota exceeded or unavailable + } + }, []); + + const toggleTheme = useCallback(() => { + setTheme(theme === 'dark' ? 'light' : 'dark'); + }, [theme, setTheme]); + + return { theme, setTheme, toggleTheme } as const; +} diff --git a/apps/web/src/widgets/topbar/Topbar.tsx b/apps/web/src/widgets/topbar/Topbar.tsx index f100743..833f1ad 100644 --- a/apps/web/src/widgets/topbar/Topbar.tsx +++ b/apps/web/src/widgets/topbar/Topbar.tsx @@ -11,9 +11,11 @@ type Props = { onLogoClick?: () => void; userName?: string; onLogout?: () => void; + theme?: "dark" | "light"; + onToggleTheme?: () => void; }; -export function Topbar({ total, fishing, transit, pairLinks, alarms, pollingStatus, lastFetchMinutes, clock, adminMode, onLogoClick, userName, onLogout }: Props) { +export function Topbar({ total, fishing, transit, pairLinks, alarms, pollingStatus, lastFetchMinutes, clock, adminMode, onLogoClick, userName, onLogout, theme, onToggleTheme }: Props) { const statusColor = pollingStatus === "ready" ? "#22C55E" : pollingStatus === "loading" ? "#F59E0B" : pollingStatus === "error" ? "#EF4444" : "var(--muted)"; return ( @@ -55,6 +57,15 @@ export function Topbar({ total, fishing, transit, pairLinks, alarms, pollingStat
{clock}
+ {onToggleTheme && ( + + )} {userName && (
{userName}