refactor(web): move website into turbo app workspace

This commit is contained in:
vince 2026-06-20 05:41:36 +02:00
parent 2664804e91
commit b4e626a868
66 changed files with 318 additions and 298 deletions

183
apps/web/src/App.tsx Normal file
View file

@ -0,0 +1,183 @@
import { useEffect, useState } from "react";
import type { DashboardRuntimeState } from "$lib/server/dashboard";
import { dashboardDocumentToUiDashboard } from "$lib/ui-adapter/model-renderer";
import {
DashboardFrame,
SystemState,
ThemeToggle,
persistUiTheme,
resolveInitialUiTheme,
type UiTheme,
type UiSeverity,
} from "@dimensionlab/ui";
const loadingDashboardState: DashboardRuntimeState = {
state: "loading",
title: "Loading Dashboard",
subtitle: "Fetching active model",
message: "Waiting for the active dashboard document.",
};
export function AppStateView({
dashboard,
onThemeChange,
theme,
}: {
dashboard: DashboardRuntimeState;
onThemeChange?: (theme: UiTheme) => void;
theme?: UiTheme;
}) {
const themeToggle =
theme && onThemeChange ? (
<ThemeToggle theme={theme} onThemeChange={onThemeChange} />
) : null;
if (dashboard.state === "ready") {
return (
<DashboardFrame
dashboard={dashboardDocumentToUiDashboard(dashboard.document)}
actions={themeToggle}
/>
);
}
const detail = `${dashboard.subtitle}: ${dashboard.message}`;
const errors = dashboard.state === "invalid" ? dashboard.errors : [];
return (
<main className="state-shell" data-dashboard-state={dashboard.state}>
{themeToggle ? (
<div className="state-shell__actions">{themeToggle}</div>
) : null}
<SystemState
title={dashboard.title}
detail={detail}
severity={stateSeverity(dashboard.state)}
icon={stateIcon(dashboard.state)}
/>
{errors.length ? (
<ul aria-label="Validation errors">
{errors.map((error) => (
<li key={error}>{error}</li>
))}
</ul>
) : null}
</main>
);
}
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<DashboardRuntimeState>(loadingDashboardState);
const [theme, setTheme] = useState<UiTheme>(() => {
if (typeof window === "undefined") return "dark";
return resolveInitialUiTheme(getThemeStorage());
});
useEffect(() => {
const metadata = resolveDocumentMetadata(dashboard);
document.title = metadata.title;
let description = document.querySelector<HTMLMetaElement>(
'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;
async function loadDashboard() {
const response = await fetch("/api/dashboard");
const nextDashboard = (await response.json()) as DashboardRuntimeState;
if (cancelled) return;
setDashboard(nextDashboard);
if (refreshTimer) {
window.clearInterval(refreshTimer);
refreshTimer = undefined;
}
const refreshIntervalSeconds =
nextDashboard.state === "ready"
? nextDashboard.document.metadata.refreshIntervalSeconds
: undefined;
if (refreshIntervalSeconds) {
refreshTimer = window.setInterval(
() => void loadDashboard(),
refreshIntervalSeconds * 1000,
);
}
}
void loadDashboard();
return () => {
cancelled = true;
if (refreshTimer) window.clearInterval(refreshTimer);
};
}, []);
return (
<AppStateView
dashboard={dashboard}
theme={theme}
onThemeChange={setTheme}
/>
);
}
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;
}
}