feat: render dashboard from model

This commit is contained in:
vince 2026-06-18 19:22:55 +02:00
parent 13e3ff867b
commit fede33e8a7
11 changed files with 372 additions and 139 deletions

View file

@ -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";
@ -21,13 +22,30 @@ describe("dashboard runtime loader", () => {
const runtime = loadDashboardRuntime(store);
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);
});
});
async function createTestStore() {

View file

@ -1,31 +1,42 @@
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
| 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 DashboardRuntimeInvalid {
state: "invalid";
title: string;
subtitle: string;
message: string;
errors: string[];
}
export function loadDashboardRuntime(store?: DashboardStore): PlaceholderDashboard {
export function loadDashboardRuntime(
store?: DashboardStore,
): DashboardRuntimeState {
const dashboardStore = store || createDashboardStore();
try {
@ -37,16 +48,42 @@ export function loadDashboardRuntime(store?: DashboardStore): PlaceholderDashboa
const document = active?.document || seeded.document;
const currentRevisionId = active?.currentRevisionId || seeded.id;
if (!document) {
return {
state: "empty",
title: "No Dashboard Model",
subtitle: "No active document",
message: "No validated dashboard document is active yet.",
};
}
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.",
state: "ready",
document,
schemaVersion: document.schemaVersion,
currentRevisionId,
};
} 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 invalidRuntimeState(errors: string[]): DashboardRuntimeInvalid {
return {
state: "invalid",
title: "Invalid Dashboard Model",
subtitle: "Validation failed",
message: "The active dashboard document could not be validated.",
errors,
};
}