diff --git a/src/lib/server/dashboard.test.ts b/src/lib/server/dashboard.test.ts
index 91a5ffb..06c45b8 100644
--- a/src/lib/server/dashboard.test.ts
+++ b/src/lib/server/dashboard.test.ts
@@ -4,6 +4,7 @@ import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, describe, expect, test } from "vitest";
import { dimensionLabDashboardFixture } from "$lib/model/fixtures/dimensionlab";
+import { genericDashboardFixture } from "$lib/model/fixtures/generic";
import { createDashboardStore, type DashboardStore } from "./db/dashboard-store";
import { loadDashboardRuntime } from "./dashboard";
@@ -19,15 +20,43 @@ describe("dashboard runtime loader", () => {
test("seeds and loads the active dashboard from sqlite", async () => {
const store = await createTestStore();
- const runtime = loadDashboardRuntime(store);
+ const runtime = loadDashboardRuntime(store, { seedIfEmpty: true });
- expect(runtime.title).toBe(dimensionLabDashboardFixture.metadata.title);
- expect(runtime.subtitle).toBe(dimensionLabDashboardFixture.metadata.subtitle);
- expect(runtime.status).toBe("building");
+ expect(runtime.state).toBe("ready");
+ if (runtime.state !== "ready") throw new Error("expected ready dashboard");
+ expect(runtime.document.metadata.title).toBe(dimensionLabDashboardFixture.metadata.title);
+ expect(runtime.document.metadata.subtitle).toBe(dimensionLabDashboardFixture.metadata.subtitle);
expect(runtime.schemaVersion).toBe(dimensionLabDashboardFixture.schemaVersion);
expect(runtime.currentRevisionId).toBe(store.getActiveDashboard()?.currentRevisionId);
expect(store.listRevisions()).toHaveLength(1);
});
+
+ test("loads an existing active dashboard without reseeding", async () => {
+ const store = await createTestStore();
+ const seed = store.commitDashboard(genericDashboardFixture, {
+ actor: "test",
+ message: "existing dashboard",
+ });
+
+ const runtime = loadDashboardRuntime(store);
+
+ expect(runtime.state).toBe("ready");
+ if (runtime.state !== "ready") throw new Error("expected ready dashboard");
+ expect(runtime.currentRevisionId).toBe(seed.id);
+ expect(runtime.document.metadata.title).toBe(genericDashboardFixture.metadata.title);
+ expect(store.listRevisions()).toHaveLength(1);
+ });
+
+ test("returns empty state when no dashboard is active and seeding is disabled", async () => {
+ const store = await createTestStore();
+
+ const runtime = loadDashboardRuntime(store);
+
+ expect(runtime.state).toBe("empty");
+ if (runtime.state !== "empty") throw new Error("expected empty dashboard");
+ expect(runtime.title).toBe("No Dashboard Model");
+ expect(store.listRevisions()).toHaveLength(0);
+ });
});
async function createTestStore() {
diff --git a/src/lib/server/dashboard.ts b/src/lib/server/dashboard.ts
index 56380a4..d2bfc39 100644
--- a/src/lib/server/dashboard.ts
+++ b/src/lib/server/dashboard.ts
@@ -1,52 +1,115 @@
import { dimensionLabDashboardFixture } from "$lib/model/fixtures/dimensionlab";
+import type { DashboardDocument } from "$lib/model";
import {
createDashboardStore,
+ DashboardPersistenceValidationError,
type DashboardStore,
} from "$lib/server/db/dashboard-store";
+import { UnsupportedDashboardModelVersionError } from "$lib/server/db/model-migrations";
-export type PlaceholderStatus = "ready" | "building";
+export type DashboardRuntimeState =
+ | DashboardRuntimeEmpty
+ | DashboardRuntimeInvalid
+ | DashboardRuntimeLoading
+ | DashboardRuntimeReady;
-export interface PlaceholderDashboard {
+export interface DashboardRuntimeReady {
+ state: "ready";
+ document: DashboardDocument;
+ schemaVersion: string;
+ currentRevisionId: string;
+}
+
+export interface DashboardRuntimeEmpty {
+ state: "empty";
title: string;
subtitle: string;
- status: PlaceholderStatus;
message: string;
- schemaVersion?: string;
- currentRevisionId?: string;
}
-export function loadPlaceholderDashboard(): PlaceholderDashboard {
- return {
- title: "Dashboard Runtime",
- subtitle: "Runtime scaffold",
- status: "building",
- message:
- "SvelteKit is running. Later issues will replace this placeholder with the validated dashboard model.",
- };
+export interface DashboardRuntimeLoading {
+ state: "loading";
+ title: string;
+ subtitle: string;
+ message: string;
}
-export function loadDashboardRuntime(store?: DashboardStore): PlaceholderDashboard {
+export interface DashboardRuntimeInvalid {
+ state: "invalid";
+ title: string;
+ subtitle: string;
+ message: string;
+ errors: string[];
+}
+
+export interface DashboardRuntimeOptions {
+ seedIfEmpty?: boolean;
+ seedDocument?: DashboardDocument;
+}
+
+export function loadDashboardRuntime(
+ store?: DashboardStore,
+ options: DashboardRuntimeOptions = {},
+): DashboardRuntimeState {
const dashboardStore = store || createDashboardStore();
try {
- const seeded = dashboardStore.seedDashboardIfEmpty(dimensionLabDashboardFixture, {
- actor: "initial-seed",
- message: "load initial dashboard document",
- });
const active = dashboardStore.getActiveDashboard();
- const document = active?.document || seeded.document;
- const currentRevisionId = active?.currentRevisionId || seeded.id;
+ if (active) {
+ return readyRuntimeState(active.document, active.currentRevisionId);
+ }
- return {
- title: document.metadata.title,
- subtitle: document.metadata.subtitle || "",
- status: "building",
- message:
- "Active dashboard document loaded from SQLite. Later issues will replace this placeholder with model-driven rendering.",
- schemaVersion: document.schemaVersion,
- currentRevisionId,
- };
+ if (!options.seedIfEmpty) {
+ return {
+ state: "empty",
+ title: "No Dashboard Model",
+ subtitle: "No active document",
+ message: "No validated dashboard document is active yet.",
+ };
+ }
+
+ const seeded = dashboardStore.seedDashboardIfEmpty(
+ options.seedDocument || dimensionLabDashboardFixture,
+ {
+ actor: "initial-seed",
+ message: "load initial dashboard document",
+ },
+ );
+
+ return readyRuntimeState(seeded.document, seeded.id);
+ } catch (error) {
+ if (error instanceof DashboardPersistenceValidationError) {
+ return invalidRuntimeState(error.failure.errors);
+ }
+
+ if (error instanceof UnsupportedDashboardModelVersionError) {
+ return invalidRuntimeState([error.message]);
+ }
+
+ throw error;
} finally {
if (!store) dashboardStore.close();
}
}
+
+function readyRuntimeState(
+ document: DashboardDocument,
+ currentRevisionId: string,
+): DashboardRuntimeReady {
+ return {
+ state: "ready",
+ document,
+ schemaVersion: document.schemaVersion,
+ currentRevisionId,
+ };
+}
+
+function invalidRuntimeState(errors: string[]): DashboardRuntimeInvalid {
+ return {
+ state: "invalid",
+ title: "Invalid Dashboard Model",
+ subtitle: "Validation failed",
+ message: "The active dashboard document could not be validated.",
+ errors,
+ };
+}
diff --git a/src/lib/ui/components/DashboardFrame.svelte b/src/lib/ui/components/DashboardFrame.svelte
index 0a76f67..4a4fff8 100644
--- a/src/lib/ui/components/DashboardFrame.svelte
+++ b/src/lib/ui/components/DashboardFrame.svelte
@@ -44,7 +44,7 @@
{/each}
-
+
+
+
diff --git a/src/routes/page.test.ts b/src/routes/page.test.ts
index 5c9d0b7..c302884 100644
--- a/src/routes/page.test.ts
+++ b/src/routes/page.test.ts
@@ -1,17 +1,16 @@
import { render } from "svelte/server";
import { describe, expect, test } from "vitest";
+import { genericDashboardFixture } from "$lib/model/fixtures/generic";
import Page from "./+page.svelte";
-describe("home page design-system preview", () => {
- test("renders the runtime dashboard data from the server load", () => {
+describe("home page model renderer", () => {
+ test("renders the active dashboard model from the server load", () => {
const { body } = render(Page, {
props: {
data: {
dashboard: {
- title: "Runtime Surface",
- subtitle: "Loaded from persistence",
- status: "building",
- message: "Runtime model is available.",
+ state: "ready",
+ document: genericDashboardFixture,
schemaVersion: "dashboard.v1",
currentRevisionId: "revision-1234567890",
},
@@ -19,10 +18,63 @@ describe("home page design-system preview", () => {
},
});
- expect(body).toContain("Runtime Surface");
- expect(body).toContain("Loaded from persistence");
- expect(body).toContain("revision");
- expect(body).toContain("primary");
- expect(body).toContain("secondary");
+ expect(body).toContain("Operations Console");
+ expect(body).toContain("Service Uptime");
+ expect(body).toContain("Identity");
+ expect(body).toContain("data-model-id=\"service-uptime\"");
+ expect(body).not.toContain("primary");
+ expect(body).not.toContain("secondary");
+ });
+
+ test("renders invalid model state without crashing", () => {
+ const { body } = render(Page, {
+ props: {
+ data: {
+ dashboard: {
+ state: "invalid",
+ title: "Invalid Dashboard",
+ subtitle: "Validation failed",
+ message: "Dashboard document is invalid.",
+ errors: ["/metadata/title is required"],
+ },
+ },
+ },
+ });
+
+ expect(body).toContain("Invalid Dashboard");
+ expect(body).toContain("Validation failed");
+ expect(body).toContain("Dashboard document is invalid.");
+ });
+
+ test("renders empty and loading model states without crashing", () => {
+ const empty = render(Page, {
+ props: {
+ data: {
+ dashboard: {
+ state: "empty",
+ title: "No Dashboard Model",
+ subtitle: "No active document",
+ message: "No validated dashboard document is active yet.",
+ },
+ },
+ },
+ });
+ const loading = render(Page, {
+ props: {
+ data: {
+ dashboard: {
+ state: "loading",
+ title: "Loading Dashboard",
+ subtitle: "Fetching active model",
+ message: "Waiting for the active dashboard document.",
+ },
+ },
+ },
+ });
+
+ expect(empty.body).toContain("No Dashboard Model");
+ expect(empty.body).toContain("No active document");
+ expect(loading.body).toContain("Loading Dashboard");
+ expect(loading.body).toContain("Fetching active model");
});
});