feat: render dashboard from model #20

Merged
vince merged 3 commits from codex/issue-7-model-renderer into main 2026-06-18 19:33:29 +02:00
3 changed files with 44 additions and 15 deletions
Showing only changes of commit 35855f018f - Show all commits

View file

@ -20,7 +20,7 @@ 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.state).toBe("ready");
if (runtime.state !== "ready") throw new Error("expected ready dashboard");
@ -46,6 +46,17 @@ describe("dashboard runtime loader", () => {
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() {

View file

@ -42,21 +42,24 @@ export interface DashboardRuntimeInvalid {
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);
}
if (!document) {
if (!options.seedIfEmpty) {
return {
state: "empty",
title: "No Dashboard Model",
@ -65,12 +68,15 @@ export function loadDashboardRuntime(
};
}
return {
state: "ready",
document,
schemaVersion: document.schemaVersion,
currentRevisionId,
};
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);
@ -86,6 +92,18 @@ export function loadDashboardRuntime(
}
}
function readyRuntimeState(
document: DashboardDocument,
currentRevisionId: string,
): DashboardRuntimeReady {
return {
state: "ready",
document,
schemaVersion: document.schemaVersion,
currentRevisionId,
};
}
function invalidRuntimeState(errors: string[]): DashboardRuntimeInvalid {
return {
state: "invalid",

View file

@ -2,6 +2,6 @@ import { loadDashboardRuntime } from "$lib/server/dashboard";
export function load() {
return {
dashboard: loadDashboardRuntime(),
dashboard: loadDashboardRuntime(undefined, { seedIfEmpty: true }),
};
}