Files
dejanandClaude Opus 5 99be627dad refactor(dashboard): Seiten von der Datenquelle entkoppeln
Die Seiten holten ihre Zahlen selbst aus einem globalen Store. HiveGrid Cloud
verlangt reine Screens nach dem Muster <XPage vm={} actions={} /> — jede Seite
hätte beim Einbau auseinandergenommen werden müssen.

Neue Schichtung:

  view-models/<seite>.vm.ts   baue<Seite>VM(daten, filter) → VMState<…>
                              reine Funktion, kein React, kein Store
  pages/<Seite>.tsx           ({ vm, optionen }) → JSX      wird portiert
  routes/<Seite>Route.tsx     Store + Builder + Seite       wird ersetzt

view-models/index.ts spiegelt VMState wortgleich aus @hivegrid/view-models;
beim Einbau ist es ein Import-Wechsel. Der Loader ruft denselben Builder mit
echten Daten, die Seiten bleiben unberührt.

Dabei abgefallen: die fünf Seitenzustände (idle · loading · ready · empty ·
error) über SeitenZustand — bisher lief der Prototyp nur im Zustand "Daten da",
in der Cloud sind alle fünf DoD-Pflicht. Die Filterleiste bekommt ihre
Auswahllisten jetzt als Prop (in der Software die Facetten aus dem Loader)
statt sie selbst aus dem Datensatz zu ziehen.

Store- und Rohdatenzugriff gibt es damit ausschließlich in routes/. Alle sechs
Seiten rendern pixelgleich; typecheck, lint und build sind grün.

Nicht geändert: Next.js bleibt. Im gesamten Projekt gibt es vier
Next-spezifische Importe, alle in AppShell und app/layout — beide Dateien
entfallen beim Einbau ohnehin, weil die Cloud Shell und Root-Layout mitbringt.
Ein Framework-Wechsel wäre Aufwand ohne Ertrag.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QPeZNwSeU7qG3RRCwaxEZX
2026-08-29 17:39:41 +02:00

64 lines
2.8 KiB
TypeScript

"use client";
import React from "react";
import { FeatherAlertTriangle, FeatherInbox } from "@subframe/core";
import { STATUS_FARBEN } from "../../utils/chartTheme";
import { PageContainer } from "./PageContainer";
import type { VMState } from "../../view-models";
/**
* Nicht-fertige Zustände einer Seite: idle · loading · empty · error.
*
* Der Prototyp lief bisher nur im Zustand „Daten da". Die Cloud verlangt alle
* fünf (`VMState`), und die Zustände sind dort Pflicht in der Definition of
* Done. Sie hier zu haben kostet wenig und erspart beim Einbau, sie nachträglich
* in jede Seite einzuweben.
*
* Gestaltung bewusst zurückhaltend: Das sind Übergänge, keine Inhalte.
*/
export function SeitenZustand({ vm }: { vm: VMState<unknown> }) {
if (vm.status === "ready") return null;
return (
<PageContainer>
<div className="flex min-h-[320px] flex-col items-center justify-center gap-3 rounded-lg border border-dashed border-border-default bg-white px-6 py-12 text-center">
{vm.status === "loading" || vm.status === "idle" ? (
<>
<span className="flex flex-col items-center gap-3" aria-live="polite">
<span className="h-6 w-6 animate-spin rounded-full border-2 border-neutral-200 border-t-neutral-500" />
<span className="text-[13px] text-text-neutral-tertiary">Kennzahlen werden berechnet …</span>
</span>
{/* Skelett statt Sprung: die Karten stehen später an derselben Stelle. */}
<div className="mt-4 grid w-full max-w-3xl grid-cols-2 gap-4 lg:grid-cols-4">
{[0, 1, 2, 3].map((i) => (
<span key={i} className="h-[104px] animate-pulse rounded-lg bg-neutral-100" />
))}
</div>
</>
) : vm.status === "empty" ? (
<>
<FeatherInbox className="text-[24px] text-text-neutral-tertiary" />
<span className="text-[15px] font-semibold text-text-neutral-primary">
Keine Daten für diese Auswahl
</span>
<span className="max-w-[380px] text-[13px] text-text-neutral-tertiary">
Die gesetzten Filter schneiden alles weg. Zeitraum erweitern oder einzelne
Einschränkungen in der Filterleiste entfernen.
</span>
</>
) : (
<>
<FeatherAlertTriangle className="text-[24px]" style={{ color: STATUS_FARBEN.negativ }} />
<span className="text-[15px] font-semibold text-text-neutral-primary">
Kennzahlen konnten nicht geladen werden
</span>
<span className="max-w-[380px] text-[13px] text-text-neutral-tertiary">
{vm.error?.messageKey ?? "Unbekannter Fehler"}
</span>
</>
)}
</div>
</PageContainer>
);
}