Files
Dashboard/features/dashboard/context/persistenterStore.ts
T
dejanandClaude Opus 5 b8bf987d0e feat(dashboard): Analytics-Dashboard als Gestaltungsvorlage zur Übergabe
UI/UX-Prototyp der Analytics-Dashboards in HiveGrid, fachlich auf einen
SHK-/Handwerksbetrieb zugeschnitten. Ziel ist die Übergabe an die Entwicklung,
nicht der Produktivbetrieb.

Inhalt: sechs Seiten (Übersicht, Finanzen, Aufträge, Kunden, Team, Komponenten),
26 KPIs, neun Chart-Bausteine, Filtersystem mit seitenabhängiger Relevanz und ein
deterministischer Demo-Datensatz (420 Kunden, 1.900 Aufträge, Stichtag 30.07.2026).

Drei Quellen sind zusammengeführt: die fachliche Planung mit dem Branchenpartner
(Januar 2026), die Subframe-Komponentenbibliothek aus dem HiveGrid-Pitch und
IBM Carbon Data Visualization für die Diagrammregeln. Herkunft und bewusste
Abweichungen stehen in docs/HERKUNFT.md.

Dokumentation: README, HANDOVER (was echte Logik braucht), docs/DESIGN-SYSTEM,
KPI-KATALOG, DATENMODELL, NAVIGATION, HERKUNFT, ZIELSYSTEM (Einbauort in
HiveGrid Cloud) und LEITPLANKEN (welche Cloud-Screens als Vorbild taugen).

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

61 lines
1.9 KiB
TypeScript

/**
* Kleiner Store für Werte, die einen Reload überleben sollen.
*
* Warum kein `useState` + `useEffect`: Beim Serverrendering gibt es keinen
* localStorage. Lädt man den gespeicherten Wert nachträglich im Effekt, löst
* das eine zweite Renderrunde aus (und React beanstandet das zu Recht).
* `useSyncExternalStore` ist genau für diesen Fall gebaut: Es rendert
* serverseitig den Standardwert und wechselt nach der Hydration auf den
* gespeicherten — ohne Mismatch und ohne Zusatzrender.
*/
export interface PersistenterStore<T> {
subscribe: (hoerer: () => void) => () => void;
getSnapshot: () => T;
getServerSnapshot: () => T;
setzen: (naechster: T | ((vorher: T) => T)) => void;
}
export function erstellePersistentenStore<T>(
schluessel: string,
standard: T,
/** Gelesene Daten mit dem Standard verschmelzen — fängt alte Formate ab. */
zusammenfuehren: (gespeichert: unknown, standard: T) => T = (g, s) =>
({ ...s, ...(g as object) }) as T
): PersistenterStore<T> {
let wert = standard;
let gelesen = false;
const hoerer = new Set<() => void>();
function lade(): T {
if (gelesen || typeof window === "undefined") return wert;
gelesen = true;
try {
const roh = window.localStorage.getItem(schluessel);
if (roh) wert = zusammenfuehren(JSON.parse(roh), standard);
} catch {
// Defektes JSON oder gesperrter Storage: beim Standard bleiben.
}
return wert;
}
return {
subscribe(neuerHoerer) {
hoerer.add(neuerHoerer);
return () => hoerer.delete(neuerHoerer);
},
getSnapshot: () => lade(),
getServerSnapshot: () => standard,
setzen(naechster) {
lade();
wert = typeof naechster === "function" ? (naechster as (v: T) => T)(wert) : naechster;
try {
window.localStorage.setItem(schluessel, JSON.stringify(wert));
} catch {
// Privater Modus o. Ä. — Persistenz ist optional.
}
hoerer.forEach((h) => h());
},
};
}