Komponentenseite auf ihren tatsaechlichen Zweck schaerfen

Sie war als "Referenz fuer den Einbau" ausgeschildert. Das trifft es nicht:
Wer den Prototyp einbaut, liest Code und Doku und kann sich keine gerenderte
Seite ansehen — die Datei-Pfade und DESIGN-SYSTEM.md tun diese Arbeit
ohnehin. Nuetzlich ist die Seite fuer die Abstimmung mit Menschen: Auf den
Analyse-Seiten sieht man von jedem Baustein immer nur den Zustand, den die
Daten gerade hergeben.

Neu ausgeschildert, und die fehlenden Zustaende ergaenzt:

- Seitenzustaende (laedt / leer / Fehler). In HiveGrid sind alle fuenf
  VMState-Zustaende Pflicht, im Prototyp ist keiner davon je zu sehen, weil
  die Demo-Daten immer liefern. Gestaltet werden muessen sie trotzdem —
  "laedt", "nichts gefunden" und "kaputt" sind drei verschiedene Aussagen.

- Dashboard-Kacheln: Begruessung, WidgetKachel, reservierte Kachel. Das
  Dashboard benutzt nicht ChartCard, sondern einen eigenen Rahmen; bisher war
  der in der Galerie gar nicht vertreten.

Zwei Fehler dabei gefunden und behoben:

- Die Regelliste nannte noch den dunklen Tooltip (#393939). Seit Carbon v11
  ist er hell (#ffffff auf #161616); im Code war das laengst umgestellt, auf
  dieser Seite nicht. Eine Referenzseite, die eine ueberholte Regel behauptet,
  ist schlimmer als keine.

- Die Abschnittstexte beginnen mit einem Dateipfad, und ein Pfad ist ein Wort
  ohne Trennstelle. Bei 375 px lief
  "features/dashboard/components/layout/SeitenZustand.tsx" neun Pixel ueber
  den Rand — von verify:layout gefunden.

Die Regel zu den Kartenhoehen aus dem letzten Commit steht jetzt auch hier.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QPeZNwSeU7qG3RRCwaxEZX
This commit is contained in:
dejanandClaude Opus 5 committed 2026-08-31 18:07:50 +02:00
1 parent ffbe70aede
commit 885c7b9609
2 files changed
+107 -6

No files matched your search

@@ -67,8 +67,15 @@ export const NAV_GRUPPEN: NavGruppe[] = [
/**
* Unterer Bereich, durch einen Trenner abgesetzt — wie in der Software.
* Hier steht nur, was zum Prototyp gehört: die Bausteingalerie für die
* Entwicklung. Sie ist kein Produktfeature und wandert beim Einbau nicht mit.
*
* Hier steht nur, was zum Prototyp gehört: die Bausteingalerie. Sie ist für
* die **Abstimmung** da, nicht für die Umsetzung — sie zeigt jeden Baustein in
* allen Zuständen, auch in denen, die die Demo-Daten nie erzeugen (negativer
* Trend, leer, Fehler, „kleiner ist besser"). Wer den Prototyp einbaut,
* braucht sie nicht: Dafür sind die Datei-Pfade und `docs/DESIGN-SYSTEM.md`
* maßgeblich.
*
* Kein Produktfeature, wandert beim Einbau nicht mit.
*/
export const NAV_SEKUNDAER: NavEintrag[] = [
{
+98 -4
View File
@@ -16,6 +16,11 @@ import { AuslastungsBalken } from "../components/charts/AuslastungsBalken";
import { ChartLeer } from "../components/charts/ChartLeer";
import { ChartLegende } from "../components/charts/ChartLegende";
import { AuftragBadge, RechnungBadge } from "../components/tables/StatusBadge";
import { SeitenZustand } from "../components/layout/SeitenZustand";
import { WidgetKachel } from "../components/dashboard/WidgetKachel";
import { ReservierteKachel } from "../components/dashboard/ReservierteKachel";
import { Begruessung } from "../components/dashboard/Begruessung";
import type { VMState } from "../view-models";
import {
KATEGORIE_FARBEN,
MAX_KATEGORIEN,
@@ -54,14 +59,15 @@ export function KomponentenSeite() {
<>
<PageHeader
titel="Komponenten & Regeln"
beschreibung="Alle Bausteine dieses Prototyps an einem Ort — als Referenz für den Einbau in die Software."
beschreibung="Jeder Baustein einzeln und in allen Zuständen — die Seite zum Draufzeigen in der Abstimmung."
/>
<PageContainer>
<Abschnitt
titel="Warum diese Seite existiert"
text="Der Prototyp ist eine Gestaltungsvorlage, kein Produkt. Diese Seite zeigt jeden Baustein isoliert, damit beim Einbau klar ist, welche Komponente wofür gedacht ist und welche Regeln dahinterstehen. Datei-Pfade stehen jeweils darunter."
text="Auf den Analyse-Seiten sieht man von jedem Baustein immer nur den einen Zustand, den die Daten gerade hergeben — die Kennzahlenkarte mit steigendem Trend, das Diagramm mit Werten darin. Die anderen Zustände existieren trotzdem und müssen mitgestaltet werden. Diese Seite zeigt sie alle nebeneinander, damit man in der Abstimmung darauf zeigen kann, ohne erst einen Filter zu suchen, der den passenden Fall herstellt."
/>
<Hinweis text="Diese Seite ist kein Produktfeature. Sie gehört weder zu Dashboard noch zu Analytics, steht in der Seitenleiste unter dem Trenner und wandert beim Einbau nicht mit. Für die Umsetzung selbst sind die Datei-Pfade unter jedem Abschnitt und docs/DESIGN-SYSTEM.md maßgeblich — beides steht im Repo und braucht diese Seite nicht." />
<ChartCard
id="komp-farben"
@@ -272,6 +278,87 @@ export function KomponentenSeite() {
</ChartCard>
</div>
<Abschnitt
titel="Seitenzustände"
text="features/dashboard/components/layout/SeitenZustand.tsx — die vier Zustände außer „fertig“. Sie sind in HiveGrid Pflicht (VMState: idle · loading · ready · empty · error) und im laufenden Prototyp nie zu sehen, weil die Demo-Daten immer liefern. Gestaltet werden müssen sie trotzdem: „lädt“, „nichts gefunden“ und „kaputt“ sind drei verschiedene Aussagen und dürfen nicht gleich aussehen."
/>
<div className="grid grid-cols-1 gap-4 xl:grid-cols-3">
{([
["Lädt", { status: "loading" } as VMState<unknown>],
["Leer — Filter schneiden alles weg", { status: "empty" } as VMState<unknown>],
[
"Fehler",
{
status: "error",
error: { messageKey: "Zeitüberschreitung beim Laden der Kennzahlen" },
} as VMState<unknown>,
],
] as const).map(([titel, vm]) => (
<div key={titel} className="flex flex-col gap-2">
<span className="text-[12px] font-medium text-text-neutral-tertiary">{titel}</span>
{/* -mx-4: SeitenZustand bringt seinen eigenen Seitenrand mit, weil
er im Normalfall die ganze Seite füllt. Hier steht er in einer
Rasterspalte und braucht ihn nicht.
Ohne zusätzlichen Rahmen: Die Komponente hat bereits eine
gestrichelte Kante. Ein Kasten darum wäre ein zweiter Rahmen
und zeigte den Baustein anders, als er in der Anwendung
aussieht. */}
<div className="-mx-4 sm:-mx-6">
<SeitenZustand vm={vm} />
</div>
</div>
))}
</div>
<Abschnitt
titel="Dashboard-Kacheln"
text="features/dashboard/components/dashboard/ — das Dashboard benutzt nicht ChartCard, sondern einen eigenen Kachelrahmen. Gleiche Kante, gleiche Abstände, aber ohne das ⋯-Menü von Analytics; dafür mit Domänen-Icon, eigenem Zeitraum und Absprung. Der Grund steht in docs/SCOPE-ANALYTICS-DASHBOARD.md: In Analytics gilt ein Filter für alle Karten, im Dashboard trägt jede Kachel ihren eigenen."
/>
{/* Wie bei den Seitenzuständen: Die Begrüßung trägt ihren eigenen
Seitenrand, weil sie im Dashboard direkt auf der Seite steht. In der
Galerie liegt sie schon im Innenabstand des Containers und wäre
sonst doppelt eingerückt. */}
<div className="-mx-4 sm:-mx-6">
<Begruessung
anrede="Guten Tag"
name="Dejan"
datum="Donnerstag, 30. Juli"
rolle="Geschäftsführung"
lage="2 Punkte brauchen heute Ihre Aufmerksamkeit."
kritisch
/>
</div>
<div className="grid grid-cols-1 gap-4 lg:grid-cols-3">
<WidgetKachel
titel="Umsatz nach Gewerk"
untertitel="Zahlungseingang der letzten 30 Tage"
hinweis="Der Zeitraum steht im Kopf, weil es auf dem Dashboard keinen seitenweiten Filter gibt."
domaene="finance"
zeitraum="30_tage"
absprung="/analytics/finanzen"
className="lg:col-span-2"
>
<BalkenHorizontal
daten={BEISPIEL_KATEGORIEN.slice(0, 5)}
formatWert={formatEuroKompakt}
einfarbig={false}
/>
</WidgetKachel>
<ReservierteKachel
bereich={{
key: "hr",
domaene: "hr",
titel: "HR-Anträge",
beschreibung: "Offene Urlaubs- und Zeitkorrekturanträge.",
icon: "UserCheck",
quelle: "HR-Hub",
}}
/>
</div>
<Hinweis text="Die reservierte Kachel zeigt bewusst keine Zahl. „2 neue Anträge“ aus erfundenen Daten liest sich in einer Abnahme wie eine fertige Funktion — und irgendwann fragt jemand, warum die Zahl nie hochzählt. Die gestrichelte Kante sagt dasselbe ehrlicher: Hier kommt etwas hin, die Fläche ist dafür da." />
<ChartCard
id="komp-badges"
titel="Status-Kennzeichnung"
@@ -315,12 +402,13 @@ export function KomponentenSeite() {
{[
"Nur horizontale Gridlines in #e0e0e0. Vertikale Linien lenken vom Verlauf ab.",
"Achsenbeschriftungen 12 px in #525252, keine Tick-Marks, Achsenlinie #8d8d8d.",
"Ein Tooltip für alle Charts: dunkle Fläche #393939, weißer Text, kein Doppelpunkt.",
"Ein Tooltip für alle Charts: weiße Fläche #ffffff, Text #161616, Schatten statt Rahmen — seit Carbon v11 hell, nicht mehr dunkel.",
"Maximal fünf Kategorien je Chart, alles darüber wird zu „Sonstige“ zusammengefasst.",
"Balkenabstand mindestens halbe Balkenbreite; Balken nie breiter als 48 px.",
"Zahlen rechtsbündig und in Tabellenziffern, Text linksbündig.",
"Karten: 16 px Innenabstand, 16 px Abstand zwischen Karten — innen nie größer als außen.",
"Jede Karte braucht einen Leerzustand. Filter können jedes Chart leer laufen lassen.",
"Karten einer Zeile nur strecken, wenn sie dieselbe Bauart haben. Ein Diagramm mit fester Höhe neben einer Balkenliste bleibt oben bündig — sonst wird aus einer Karte mit einem Balken eine 427 px hohe Fläche.",
].map((regel) => (
<li key={regel} className="flex gap-2.5 text-[13px] leading-relaxed text-text-neutral-secondary">
<span className="mt-1.5 h-1.5 w-1.5 flex-none rounded-full bg-neutral-400" />
@@ -338,7 +426,13 @@ function Abschnitt({ titel, text }: { titel: string; text: string }) {
return (
<div className="flex flex-col gap-1 pt-2">
<h2 className="text-[15px] font-semibold text-text-neutral-primary">{titel}</h2>
<p className="max-w-3xl text-[13px] leading-relaxed text-text-neutral-tertiary">{text}</p>
{/* break-words: Diese Absätze beginnen mit einem Dateipfad, und ein Pfad
ist ein einziges Wort ohne Trennstelle. Bei 375 px lief
"features/dashboard/components/layout/SeitenZustand.tsx" neun Pixel
über den Rand. */}
<p className="max-w-3xl break-words text-[13px] leading-relaxed text-text-neutral-tertiary">
{text}
</p>
</div>
);
}