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, collectVisibleDashboardModelIds, createDashboardPerformanceMarks, createDashboardRefreshDelay, createDashboardTileSnapshotStore, createDashboardTileBackoff, createDashboardRequestAborter, runViewportAwareDashboardHydrationQueue, shouldPauseDashboardRefresh, waitForDashboardHydrationIdle, type DashboardIntersectionObserverFactory, type DashboardTileReference, 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 DashboardTileResponse = | { state: "ready"; tile: DashboardTileReference; item: DashboardModule | ServiceEntry | StatusItem | TelemetryCard; } | { state: "not_found"; tile: DashboardTileReference; message: string; } | { state: "disabled"; tile: DashboardTileReference; message: string; }; type DashboardTilesBatchResponse = { state: "ready"; tiles: DashboardTileResponse[]; }; type DashboardTileHydrationResult = "aborted" | "failed" | "ready"; const dashboardTileHydrationConcurrency = 6; const dashboardFallbackRefreshIntervalMs = 30_000; const dashboardViewportObservationTimeoutMs = 80; 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 lastRefreshIntervalMs = dashboardFallbackRefreshIntervalMs; let hydrationRun = 0; const requestAborter = createDashboardRequestAborter(); const performanceMarks = createDashboardPerformanceMarks(); const refreshDelay = createDashboardRefreshDelay(); const tileBackoff = createDashboardTileBackoff(); const tileSnapshotStore = createDashboardTileSnapshotStore( getTileSnapshotStorage(), ); function refreshPaused() { return shouldPauseDashboardRefresh({ visibilityState: document.visibilityState, online: navigator.onLine, }); } function clearRefreshTimer() { if (refreshTimer !== undefined) { window.clearTimeout(refreshTimer); refreshTimer = undefined; } } function scheduleNextRefresh(baseDelayMs: number) { clearRefreshTimer(); if (cancelled || refreshPaused()) return; refreshTimer = window.setTimeout(() => { refreshTimer = undefined; void loadDashboard(); }, refreshDelay.nextDelayMs(baseDelayMs)); } function pauseRefreshes() { clearRefreshTimer(); requestAborter.abortActiveRequests(); setHydratingItemIds(new Set()); } async function loadDashboard() { if (cancelled || refreshPaused()) return; clearRefreshTimer(); 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; refreshDelay.recordSuccess(); performanceMarks.markShellLoad(); 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()); } const refreshIntervalSeconds = nextDashboard.state === "ready" ? nextDashboard.document.metadata.refreshIntervalSeconds : undefined; if (refreshIntervalSeconds && !refreshPaused()) { lastRefreshIntervalMs = refreshIntervalSeconds * 1000; scheduleNextRefresh(lastRefreshIntervalMs); } } catch (error) { if (!isAbortError(error) && !cancelled) { refreshDelay.recordFailure(); console.error("Dashboard refresh failed", error); scheduleNextRefresh(lastRefreshIntervalMs); } } } function hydrateDashboardTiles( tiles: DashboardTileReference[], run: number, signal: AbortSignal, snapshotContext: DashboardTileSnapshotStoreContext, ) { const modelIds = tiles.map(dashboardTileModelId); void runViewportAwareDashboardHydrationQueue({ batchSize: dashboardTileHydrationConcurrency, collectVisibleModelIds: async () => { await waitForDashboardRenderFrame(signal); return collectVisibleDashboardModelIds({ clearTimeout: window.clearTimeout.bind(window), createObserver: getDashboardIntersectionObserverFactory(), documentTarget: document, modelIds, setTimeout: window.setTimeout.bind(window), signal, timeoutMs: dashboardViewportObservationTimeoutMs, }); }, concurrency: dashboardTileHydrationConcurrency, getModelId: dashboardTileModelId, 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); } }, hydrateBatch: async (batch) => { const results = await hydrateDashboardTileBatch( batch, run, signal, snapshotContext, ); for (const { result, tile } of results) { const key = dashboardTileKey(tile); if (result === "ready") { tileBackoff.recordSuccess(key); } else if (result === "failed") { tileBackoff.recordFailure(key); } } }, items: tiles, onAllItemsSettled: () => performanceMarks.markAllTilesSettled(), onVisibleItemsSettled: () => performanceMarks.markVisibleTilesReady(), signal, waitForIdle: () => waitForDashboardHydrationIdle({ ...getDashboardIdleCallbacks(), clearTimeout: window.clearTimeout.bind(window), setTimeout: window.setTimeout.bind(window), signal, }), }); } async function hydrateDashboardTileBatch( tiles: DashboardTileReference[], run: number, signal: AbortSignal, snapshotContext: DashboardTileSnapshotStoreContext, ): Promise> { try { const response = await fetch("/api/dashboard/tiles", { body: JSON.stringify({ tiles }), headers: { "Content-Type": "application/json" }, method: "POST", signal, }); const batchResponse = (await response.json()) as DashboardTilesBatchResponse; if ( cancelled || signal.aborted || run !== hydrationRun || !response.ok || batchResponse.state !== "ready" ) { throw new Error("Dashboard tile batch hydration failed"); } const responsesByKey = new Map( batchResponse.tiles.map((tileResponse) => [ dashboardTileKey(tileResponse.tile), tileResponse, ]), ); return tiles.map((tile) => { const key = dashboardTileKey(tile); try { return { tile, result: applyDashboardTileHydrationResponse( tile, responsesByKey.get(key), run, signal, snapshotContext, ), }; } finally { finishDashboardTileHydration(key, run, signal); } }); } catch (error) { if (signal.aborted || cancelled || run !== hydrationRun) { return tiles.map((tile) => ({ result: "aborted", tile })); } return Promise.all( tiles.map(async (tile) => ({ tile, result: await hydrateDashboardTile(tile, run, signal, snapshotContext), })), ); } } 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 (!response.ok) { return "failed"; } const result = applyDashboardTileHydrationResponse( tile, tileResponse, run, signal, snapshotContext, ); if (result !== "ready") { return signal.aborted || cancelled || run !== hydrationRun ? "aborted" : "failed"; } return "ready"; } catch (error) { if (!isAbortError(error) && !cancelled) { console.error("Dashboard tile hydration failed", error); return "failed"; } return "aborted"; } finally { finishDashboardTileHydration(key, run, signal); } } function applyDashboardTileHydrationResponse( tile: DashboardTileReference, tileResponse: DashboardTileResponse | undefined, run: number, signal: AbortSignal, snapshotContext: DashboardTileSnapshotStoreContext, ): DashboardTileHydrationResult { if ( cancelled || signal.aborted || run !== hydrationRun || !tileResponse || dashboardTileKey(tileResponse.tile) !== dashboardTileKey(tile) || 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, }); performanceMarks.markFirstTileReady(); return "ready"; } function finishDashboardTileHydration( key: string, run: number, signal: AbortSignal, ) { 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; } } type DashboardIdleWindow = Window & { cancelIdleCallback?: (handle: number) => void; requestIdleCallback?: ( callback: () => void, options?: { timeout?: number }, ) => number; }; function getDashboardIdleCallbacks() { const idleWindow = window as DashboardIdleWindow; return { cancelIdleCallback: idleWindow.cancelIdleCallback?.bind(idleWindow), requestIdleCallback: idleWindow.requestIdleCallback?.bind(idleWindow), }; } function getDashboardIntersectionObserverFactory(): | DashboardIntersectionObserverFactory | undefined { if (typeof window.IntersectionObserver === "undefined") return undefined; return (callback) => new window.IntersectionObserver((entries) => { callback(entries); }); } async function waitForDashboardRenderFrame(signal: AbortSignal): Promise { if (signal.aborted) return; await new Promise((resolve) => { let settled = false; let frame: number | undefined; function finish() { if (settled) return; settled = true; if (frame !== undefined) window.cancelAnimationFrame(frame); signal.removeEventListener("abort", finish); resolve(); } signal.addEventListener("abort", finish, { once: true }); frame = window.requestAnimationFrame(finish); }); } 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 dashboardTileModelId(tile: DashboardTileReference): string { if (tile.kind === "status") return `${tile.stripId}:${tile.id}`; return 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, ), }; }