import { useEffect, useState } from "react"; import type { DashboardDocument, DashboardModule, ServiceEntry, StatusItem, TelemetryCard, } from "@dimensionlab/dashboard-model"; import type { DashboardRuntimeState } from "$lib/server/dashboard"; import { attachDashboardRefreshLifecycle, createDashboardTileSnapshotStore, createDashboardTileBackoff, createDashboardRequestAborter, runDashboardHydrationQueue, shouldPauseDashboardRefresh, type DashboardTileSnapshotStoreContext, type RestoredDashboardTileSnapshot, } from "$lib/client/dashboard-refresh"; import { dashboardDocumentToUiDashboard } from "$lib/ui-adapter/model-renderer"; import { DashboardFrame, SystemState, ThemeToggle, persistUiTheme, resolveInitialUiTheme, type UiTheme, type UiSeverity, type UiDashboardPreview, } from "@dimensionlab/ui"; type DashboardTileReference = | { kind: "telemetry"; id: string } | { kind: "service"; groupId: string; id: string } | { kind: "module"; id: string } | { kind: "status"; stripId: string; id: string }; type DashboardTileResponse = | { state: "ready"; tile: DashboardTileReference; item: DashboardModule | ServiceEntry | StatusItem | TelemetryCard; } | { state: "not_found"; tile: DashboardTileReference; message: string; } | { state: "disabled"; tile: DashboardTileReference; message: string; }; type DashboardTileHydrationResult = "aborted" | "failed" | "ready"; const dashboardTileHydrationConcurrency = 6; type DashboardTileSnapshotStore = ReturnType; export function AppStateView({ dashboard, onThemeChange, theme, hydratingItemIds, }: { dashboard: DashboardRuntimeState; hydratingItemIds?: ReadonlySet; onThemeChange?: (theme: UiTheme) => void; theme?: UiTheme; }) { const themeToggle = theme && onThemeChange ? ( ) : null; if (dashboard.state === "ready") { const uiDashboard = markHydratingItems( dashboardDocumentToUiDashboard(dashboard.document), hydratingItemIds, ); return ( ); } const detail = `${dashboard.subtitle}: ${dashboard.message}`; const errors = dashboard.state === "invalid" ? dashboard.errors : []; return (
{themeToggle ? (
{themeToggle}
) : null} {errors.length ? (
    {errors.map((error) => (
  • {error}
  • ))}
) : null}
); } export function resolveDocumentMetadata(dashboard: DashboardRuntimeState): { description: string; title: string; } { if (dashboard.state === "ready") { const uiDashboard = dashboardDocumentToUiDashboard(dashboard.document); return { title: uiDashboard.title, description: uiDashboard.subtitle || dashboard.document.metadata.description || uiDashboard.title, }; } return { title: dashboard.title, description: dashboard.subtitle || dashboard.message, }; } export default function App() { const [dashboard, setDashboard] = useState(null); const [hydratingItemIds, setHydratingItemIds] = useState>( () => new Set(), ); const [theme, setTheme] = useState(() => { if (typeof window === "undefined") return "dark"; return resolveInitialUiTheme(getThemeStorage()); }); useEffect(() => { if (!dashboard) return; const metadata = resolveDocumentMetadata(dashboard); document.title = metadata.title; let description = document.querySelector( 'meta[name="description"]', ); if (!description) { description = document.createElement("meta"); description.name = "description"; document.head.append(description); } description.content = metadata.description; }, [dashboard]); useEffect(() => { document.documentElement.dataset.uiTheme = theme; persistUiTheme(theme, getThemeStorage()); }, [theme]); useEffect(() => { let cancelled = false; let refreshTimer: number | undefined; let hydrationRun = 0; const requestAborter = createDashboardRequestAborter(); const tileBackoff = createDashboardTileBackoff(); const tileSnapshotStore = createDashboardTileSnapshotStore( getTileSnapshotStorage(), ); function refreshPaused() { return shouldPauseDashboardRefresh({ visibilityState: document.visibilityState, online: navigator.onLine, }); } function clearRefreshTimer() { if (refreshTimer) { window.clearInterval(refreshTimer); refreshTimer = undefined; } } function pauseRefreshes() { clearRefreshTimer(); requestAborter.abortActiveRequests(); setHydratingItemIds(new Set()); } async function loadDashboard() { if (cancelled || refreshPaused()) return; const shellSignal = requestAborter.beginShellRun(); try { const response = await fetch("/api/dashboard", { signal: shellSignal }); const nextDashboard = (await response.json()) as DashboardRuntimeState; if (cancelled || shellSignal.aborted || refreshPaused()) return; const restored = restoreDashboardTileSnapshots( nextDashboard, nextDashboard.state === "ready" ? tileSnapshotStore.restore({ currentRevisionId: nextDashboard.currentRevisionId, schemaVersion: nextDashboard.schemaVersion, }) : [], ); setDashboard(restored.dashboard); const currentRun = ++hydrationRun; if ( restored.dashboard.state === "ready" && restored.dashboard.liveDatasourceHydration?.enabled !== false ) { const tiles = dashboardHydrationTiles(restored.dashboard.document).filter( (tile) => tileBackoff.canAttempt(dashboardTileKey(tile)), ); const tileSignal = requestAborter.beginTileRun(); setHydratingItemIds( new Set( tiles .map(dashboardTileKey) .filter((key) => !restored.restoredItemIds.has(key)), ), ); hydrateDashboardTiles( tiles, currentRun, tileSignal, { currentRevisionId: restored.dashboard.currentRevisionId, schemaVersion: restored.dashboard.schemaVersion, }, ); } else { setHydratingItemIds(new Set()); } clearRefreshTimer(); const refreshIntervalSeconds = nextDashboard.state === "ready" ? nextDashboard.document.metadata.refreshIntervalSeconds : undefined; if (refreshIntervalSeconds && !refreshPaused()) { refreshTimer = window.setInterval( () => void loadDashboard(), refreshIntervalSeconds * 1000, ); } } catch (error) { if (!isAbortError(error) && !cancelled) { console.error("Dashboard refresh failed", error); } } } function hydrateDashboardTiles( tiles: DashboardTileReference[], run: number, signal: AbortSignal, snapshotContext: DashboardTileSnapshotStoreContext, ) { void runDashboardHydrationQueue({ concurrency: dashboardTileHydrationConcurrency, hydrate: async (tile) => { const key = dashboardTileKey(tile); const result = await hydrateDashboardTile( tile, run, signal, snapshotContext, ); if (result === "ready") { tileBackoff.recordSuccess(key); } else if (result === "failed") { tileBackoff.recordFailure(key); } }, items: tiles, signal, }); } async function hydrateDashboardTile( tile: DashboardTileReference, run: number, signal: AbortSignal, snapshotContext: DashboardTileSnapshotStoreContext, ): Promise { const key = dashboardTileKey(tile); try { const response = await fetch(dashboardTileUrl(tile), { signal }); const tileResponse = (await response.json()) as DashboardTileResponse; if ( cancelled || signal.aborted || run !== hydrationRun || !response.ok || tileResponse.state !== "ready" ) { return signal.aborted || cancelled || run !== hydrationRun ? "aborted" : "failed"; } const readyTileResponse = tileResponse; setDashboard((current) => current?.state === "ready" ? { ...current, document: applyDashboardTile(current.document, readyTileResponse), } : current, ); tileSnapshotStore.saveReadyTile({ ...snapshotContext, response: readyTileResponse, }); return "ready"; } catch (error) { if (!isAbortError(error) && !cancelled) { console.error("Dashboard tile hydration failed", error); return "failed"; } return "aborted"; } finally { if (!cancelled && !signal.aborted && run === hydrationRun) { setHydratingItemIds((current) => { const next = new Set(current); next.delete(key); return next; }); } } } const detachRefreshLifecycle = attachDashboardRefreshLifecycle({ documentTarget: document, windowTarget: window, loadDashboard, pauseRefreshes, refreshPaused: () => cancelled || refreshPaused(), }); void loadDashboard(); return () => { cancelled = true; clearRefreshTimer(); requestAborter.abortActiveRequests(); detachRefreshLifecycle(); }; }, []); return dashboard ? ( ) : null; } function stateSeverity(state: DashboardRuntimeState["state"]): UiSeverity { if (state === "invalid") return "danger"; if (state === "loading") return "loading"; return "stale"; } function stateIcon(state: DashboardRuntimeState["state"]): string { if (state === "invalid") return "mdi:file-alert-outline"; if (state === "loading") return "mdi:progress-clock"; return "mdi:tray"; } function getThemeStorage(): Storage | undefined { try { return window.localStorage; } catch { return undefined; } } function getTileSnapshotStorage(): Storage | undefined { try { return window.localStorage; } catch { return undefined; } } function isAbortError(error: unknown): boolean { return ( typeof error === "object" && error !== null && "name" in error && error.name === "AbortError" ); } export function restoreDashboardTileSnapshots( dashboard: DashboardRuntimeState, snapshots: RestoredDashboardTileSnapshot[], ): { dashboard: DashboardRuntimeState; restoredItemIds: Set; } { if (dashboard.state !== "ready" || snapshots.length === 0) { return { dashboard, restoredItemIds: new Set(), }; } return snapshots.reduce( (current, snapshot) => ({ dashboard: { ...current.dashboard, document: applyDashboardTile( current.dashboard.document, snapshot.response as Extract, ), }, restoredItemIds: new Set([ ...current.restoredItemIds, dashboardTileKey(snapshot.response.tile), ]), }), { dashboard, restoredItemIds: new Set(), }, ); } function markHydratingItems( dashboard: UiDashboardPreview, hydratingItemIds?: ReadonlySet, ): UiDashboardPreview { if (!hydratingItemIds?.size) return dashboard; return { ...dashboard, telemetry: dashboard.telemetry.map((card) => hydratingItemIds.has(`telemetry:${card.id}`) ? { ...card, severity: "loading", detail: "loading live telemetry", } : card, ), serviceGroups: dashboard.serviceGroups.map((group) => ({ ...group, services: group.services.map((service) => hydratingItemIds.has(`service:${group.id}:${service.id}`) ? { ...service, severity: "loading", detail: "loading", } : service, ), })), modules: dashboard.modules.map((module) => hydratingItemIds.has(`module:${module.id}`) ? { ...module, severity: "loading", detail: "loading live data", } : module, ), statusItems: dashboard.statusItems.map((item) => hydratingItemIds.has(`status:${item.id}`) ? { ...item, severity: "loading", value: "loading", } : item, ), }; } export function dashboardHydrationTiles( document: DashboardDocument, ): DashboardTileReference[] { const telemetry = document.telemetry .filter((card) => card.datasource?.type === "external") .map((card): DashboardTileReference => ({ kind: "telemetry", id: card.id })); const services = document.serviceGroups.flatMap((group) => group.services .filter((service) => service.datasource?.type === "external") .map((service): DashboardTileReference => ({ kind: "service", groupId: group.id, id: service.id, })), ); const modules = (document.modules || []) .filter((module) => module.datasource?.type === "external" || module.id === "runtime-health-summary" ) .map((module): DashboardTileReference => ({ kind: "module", id: module.id })); const status = document.statusStrips.flatMap((strip) => strip.items .map((item): DashboardTileReference => ({ kind: "status", stripId: strip.id, id: item.id, })), ); return [...status, ...telemetry, ...modules, ...services]; } function dashboardTileKey(tile: DashboardTileReference): string { if (tile.kind === "status") return `${tile.kind}:${tile.stripId}:${tile.id}`; if (tile.kind === "service") return `${tile.kind}:${tile.groupId}:${tile.id}`; return `${tile.kind}:${tile.id}`; } function dashboardTileUrl(tile: DashboardTileReference): string { const parts = tile.kind === "status" ? ["api", "dashboard", "tile", tile.kind, tile.stripId, tile.id] : tile.kind === "service" ? ["api", "dashboard", "tile", tile.kind, tile.groupId, tile.id] : ["api", "dashboard", "tile", tile.kind, tile.id]; return `/${parts.map(encodeURIComponent).join("/")}`; } function applyDashboardTile( document: DashboardDocument, response: Extract, ): DashboardDocument { if (response.tile.kind === "telemetry") { return { ...document, telemetry: document.telemetry.map((card) => card.id === response.tile.id ? response.item as TelemetryCard : card, ), }; } if (response.tile.kind === "service") { const tile = response.tile; return { ...document, serviceGroups: document.serviceGroups.map((group) => ({ ...group, services: group.id === tile.groupId ? group.services.map((service) => service.id === tile.id ? response.item as ServiceEntry : service, ) : group.services, })), }; } if (response.tile.kind === "module") { return { ...document, modules: (document.modules || []).map((module) => module.id === response.tile.id ? response.item as DashboardModule : module, ), }; } const tile = response.tile; return { ...document, statusStrips: document.statusStrips.map((strip) => strip.id === tile.stripId ? { ...strip, items: strip.items.map((item) => item.id === tile.id ? response.item as StatusItem : item, ), } : strip, ), }; }