467 Stellen. Nicht durch einen anderen Strich ersetzt, sondern durch das
Satzzeichen, das an der jeweiligen Stelle richtig ist: Doppelpunkt, wo eine
Erlaeuterung folgt; Komma, wo der Satz weiterlaeuft; Punkt, wo zwei
Hauptsaetze aneinanderstiessen. Ein Halbgeviertstrich waere nur derselbe
Griff mit kuerzerem Strich gewesen.
Regelbasiert ersetzt und danach nachgeprueft, weil eine Regel allein nicht
reicht. Gefunden und einzeln korrigiert:
- Eingeschobene Saetze mit Strichpaar. Nur der oeffnende Strich passte ins
Muster, der schliessende blieb als "—," stehen. Drei Stellen, jetzt mit
Klammern oder als eigener Satz.
- Doppelte Doppelpunkte, wo beide Striche eines Paars zu Doppelpunkten wurden
("Globale Regeln: Schrift, Groessen, Abstaende: kommen aus HiveGrid").
Sieben Stellen in Doku und Oberflaeche, jetzt in Klammern.
- Pfade, denen ein Komma statt eines Doppelpunkts folgte.
- Ausgerichtete Kommentartabellen, in denen der Strich eine Spalte war.
Nicht angefasst: drei Geviertstriche, die keine Prosa sind, sondern
Tabellenplatzhalter fuer "kein Wert" (Trichter, Auftraege, Kunden). Dort ist
der Strich die Angabe selbst; ihn zu entfernen liesse leere Zellen zurueck.
tsc, lint, build, verify:carbon und verify:layout laufen sauber.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QPeZNwSeU7qG3RRCwaxEZX
450 lines
20 KiB
TypeScript
450 lines
20 KiB
TypeScript
"use client";
|
|
|
|
import React from "react";
|
|
import { PageHeader } from "../components/layout/PageHeader";
|
|
import { PageContainer } from "../components/layout/PageContainer";
|
|
import { KPICard } from "../components/cards/KPICard";
|
|
import { ChartCard } from "../components/cards/ChartCard";
|
|
import { AlertList } from "../components/cards/AlertList";
|
|
import { StatZeile } from "../components/cards/StatZeile";
|
|
import { AreaVerlauf } from "../components/charts/AreaVerlauf";
|
|
import { BalkenVertikal } from "../components/charts/BalkenVertikal";
|
|
import { BalkenHorizontal } from "../components/charts/BalkenHorizontal";
|
|
import { Donut } from "../components/charts/Donut";
|
|
import { Trichter } from "../components/charts/Trichter";
|
|
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 { RESERVIERTE_BEREICHE } from "../view-models/dashboard.vm";
|
|
import { Begruessung } from "../components/dashboard/Begruessung";
|
|
import type { VMState } from "../view-models";
|
|
import {
|
|
KATEGORIE_FARBEN,
|
|
MAX_KATEGORIEN,
|
|
PRIMAER_FARBE,
|
|
STATUS_FARBEN,
|
|
VERGLEICH_FARBE,
|
|
} from "../utils/chartTheme";
|
|
import {
|
|
formatEuro,
|
|
formatEuroAchse,
|
|
formatEuroKompakt,
|
|
formatProzent,
|
|
formatZahl,
|
|
} from "../utils/formatters";
|
|
import { DATENSATZ_INFO } from "../data/seed";
|
|
|
|
/** Beispieldaten, damit die Galerie unabhängig von Filtern immer etwas zeigt. */
|
|
const BEISPIEL_REIHE = Array.from({ length: 12 }, (_, i) => ({
|
|
datum: `2026-${String(i + 1).padStart(2, "0")}`,
|
|
label: ["Jan", "Feb", "Mär", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez"][i],
|
|
value: [148, 162, 171, 158, 166, 149, 152, 161, 189, 205, 198, 176][i] * 1000,
|
|
vergleich: [131, 140, 152, 146, 151, 138, 141, 149, 168, 181, 176, 158][i] * 1000,
|
|
}));
|
|
|
|
const BEISPIEL_KATEGORIEN = [
|
|
{ name: "Heizung", value: 842_000 },
|
|
{ name: "Sanitär", value: 486_000 },
|
|
{ name: "Photovoltaik", value: 421_000 },
|
|
{ name: "Klima & Lüftung", value: 268_000 },
|
|
{ name: "Elektro", value: 194_000 },
|
|
{ name: "Sonstiges", value: 61_000 },
|
|
];
|
|
|
|
export function KomponentenSeite() {
|
|
return (
|
|
<>
|
|
<PageHeader
|
|
titel="Komponenten & Regeln"
|
|
beschreibung="Jeder Baustein einzeln und in allen Zuständen: die Seite zum Draufzeigen in der Abstimmung."
|
|
/>
|
|
|
|
<PageContainer>
|
|
<Abschnitt
|
|
titel="Warum diese Seite existiert"
|
|
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"
|
|
titel="Farbpalette"
|
|
untertitel="IBM Carbon, kategoriale Palette (14 Farben): Reihenfolge nie umsortieren"
|
|
hinweis="Die Reihenfolge ist auf Unterscheidbarkeit optimiert. Farben immer von vorne vergeben, nicht nach Geschmack auswählen."
|
|
>
|
|
<div className="flex flex-col gap-4">
|
|
<div className="flex flex-wrap gap-2">
|
|
{KATEGORIE_FARBEN.map((farbe, index) => (
|
|
<div key={farbe} className="flex w-[104px] flex-col gap-1">
|
|
<span className="h-10 w-full rounded-md" style={{ background: farbe }} />
|
|
<span className="text-[11px] font-medium text-text-neutral-secondary">
|
|
{index + 1}. {farbe}
|
|
</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
<div className="flex flex-wrap gap-4 border-t border-solid border-border-default pt-3">
|
|
{Object.entries(STATUS_FARBEN).map(([name, farbe]) => (
|
|
<span key={name} className="flex items-center gap-2 text-[12px] text-text-neutral-secondary">
|
|
<span className="h-4 w-4 rounded" style={{ background: farbe }} />
|
|
Status „{name}“ · {farbe}
|
|
</span>
|
|
))}
|
|
</div>
|
|
<p className="text-[12px] leading-relaxed text-text-neutral-tertiary">
|
|
Kategorien nutzen die Carbon-Palette, Statusaussagen die Ampelfarben. Beides nie
|
|
mischen: eine grüne Kategorie neben einem grünen Positiv-Status ist nicht mehr lesbar.
|
|
Maximal {MAX_KATEGORIEN} Segmente je Chart, der Rest wird zu „Sonstige“
|
|
zusammengefasst.
|
|
</p>
|
|
</div>
|
|
</ChartCard>
|
|
|
|
<Abschnitt titel="Kennzahlen-Karten" text="features/dashboard/components/cards/KPICard.tsx" />
|
|
<div className="grid grid-cols-2 gap-4 lg:grid-cols-4">
|
|
<KPICard
|
|
label="Mit Trend und Sparkline"
|
|
wert={formatEuro(238_412)}
|
|
zusatz="Zahlungseingang im Zeitraum"
|
|
trend={{ wert: 12.4, richtung: "up" }}
|
|
icon="Euro"
|
|
akzent="positiv"
|
|
verlauf={[42, 48, 45, 52, 58, 55, 63, 71]}
|
|
/>
|
|
<KPICard
|
|
label="Negativer Trend"
|
|
wert={formatProzent(58.2, 0)}
|
|
zusatz="Abschlussquote"
|
|
trend={{ wert: 6.1, richtung: "down" }}
|
|
icon="Target"
|
|
akzent="negativ"
|
|
/>
|
|
<KPICard
|
|
label="Kleiner ist besser"
|
|
wert="34,2 Tage"
|
|
zusatz="Ø Zahlungsdauer"
|
|
trend={{ wert: 8.3, richtung: "down" }}
|
|
icon="Clock"
|
|
akzent="positiv"
|
|
richtungGut="negativ"
|
|
/>
|
|
<KPICard
|
|
label="Ohne Trend"
|
|
wert={formatZahl(1900)}
|
|
zusatz="Vorgänge im Datensatz"
|
|
icon="ClipboardList"
|
|
akzent="neutral"
|
|
/>
|
|
</div>
|
|
<Hinweis text="richtungGut='negativ' dreht die Farblogik um: Bei Zahlungsdauer oder Reaktionszeit ist ein Rückgang gut. Ohne diesen Schalter würde die Karte eine Verbesserung rot einfärben." />
|
|
|
|
<div className="grid grid-cols-1 gap-4 xl:grid-cols-3">
|
|
<ChartCard
|
|
id="komp-area"
|
|
titel="Flächendiagramm mit Vergleich"
|
|
untertitel="components/charts/AreaVerlauf.tsx: für Zeitverläufe"
|
|
className="xl:col-span-2"
|
|
legende={
|
|
<ChartLegende
|
|
eintraege={[
|
|
{ name: "Aktuell", farbe: PRIMAER_FARBE },
|
|
{ name: "Vorjahr", farbe: VERGLEICH_FARBE, gestrichelt: true },
|
|
]}
|
|
/>
|
|
}
|
|
>
|
|
<AreaVerlauf
|
|
daten={BEISPIEL_REIHE}
|
|
formatWert={formatEuroKompakt}
|
|
formatAchse={formatEuroAchse}
|
|
zeigeVergleich
|
|
hoehe={220}
|
|
/>
|
|
</ChartCard>
|
|
|
|
<ChartCard
|
|
id="komp-donut"
|
|
titel="Ringdiagramm"
|
|
untertitel="components/charts/Donut.tsx: für Anteile"
|
|
>
|
|
<Donut daten={BEISPIEL_KATEGORIEN} formatWert={formatEuroKompakt} hoehe={200} />
|
|
</ChartCard>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 gap-4 lg:grid-cols-3">
|
|
<ChartCard
|
|
id="komp-balken"
|
|
titel="Säulendiagramm"
|
|
untertitel="components/charts/BalkenVertikal.tsx: Mengen mit Nulllinie"
|
|
>
|
|
<BalkenVertikal
|
|
daten={BEISPIEL_REIHE.map((p) => ({ label: p.label, value: p.value / 1000 }))}
|
|
formatWert={(w) => formatZahl(w)}
|
|
hoehe={200}
|
|
nurGanzzahlen
|
|
/>
|
|
</ChartCard>
|
|
|
|
<ChartCard
|
|
id="komp-hbalken"
|
|
titel="Balkendiagramm (liegend)"
|
|
untertitel="components/charts/BalkenHorizontal.tsx: Rangfolgen"
|
|
>
|
|
<BalkenHorizontal
|
|
daten={BEISPIEL_KATEGORIEN}
|
|
formatWert={formatEuroKompakt}
|
|
einfarbig={false}
|
|
/>
|
|
</ChartCard>
|
|
|
|
<ChartCard
|
|
id="komp-trichter"
|
|
titel="Trichter"
|
|
untertitel="components/charts/Trichter.tsx: Stufen mit Übergangsquote"
|
|
>
|
|
<Trichter
|
|
stufen={[
|
|
{ name: "Anfrage", value: 1900 },
|
|
{ name: "Vor-Aufmaß", value: 1420 },
|
|
{ name: "Angebot", value: 1180 },
|
|
{ name: "Beauftragt", value: 690 },
|
|
{ name: "Abgeschlossen", value: 612 },
|
|
]}
|
|
formatWert={(w) => formatZahl(w)}
|
|
/>
|
|
</ChartCard>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 gap-4 lg:grid-cols-3">
|
|
<ChartCard
|
|
id="komp-auslastung"
|
|
titel="Auslastungsbalken"
|
|
untertitel="components/charts/AuslastungsBalken.tsx: mit Ampel und 100-%-Marke"
|
|
>
|
|
<AuslastungsBalken
|
|
zeilen={[
|
|
{ name: "Thomas Krause", wert: 104, zusatz: "Meister · Heizung" },
|
|
{ name: "Dennis Meyer", wert: 92, zusatz: "Monteur · Sanitär" },
|
|
{ name: "Maike Schmidt", wert: 78, zusatz: "Monteur · Photovoltaik" },
|
|
{ name: "Kevin Wolf", wert: 54, zusatz: "Auszubildender · Elektro" },
|
|
]}
|
|
/>
|
|
</ChartCard>
|
|
|
|
<ChartCard
|
|
id="komp-alerts"
|
|
titel="Handlungsbedarf"
|
|
untertitel="components/cards/AlertList.tsx: nach Dringlichkeit sortiert"
|
|
>
|
|
<AlertList
|
|
eintraege={[
|
|
{
|
|
id: "a",
|
|
text: "25 überfällige Rechnungen",
|
|
detail: "Zahlungsziel überschritten",
|
|
betrag: 96_194,
|
|
aktion: "Mahnlauf starten",
|
|
art: "kritisch",
|
|
},
|
|
{
|
|
id: "b",
|
|
text: "32 Aufträge über Termin",
|
|
detail: "Ältester seit 99 Tagen fällig",
|
|
betrag: 148_600,
|
|
aktion: "Termine neu planen",
|
|
art: "warnung",
|
|
},
|
|
{
|
|
id: "c",
|
|
text: "23 Förderanträge in Bearbeitung",
|
|
detail: "Bescheid ausstehend",
|
|
betrag: 116_100,
|
|
aktion: "Status prüfen",
|
|
art: "hinweis",
|
|
},
|
|
]}
|
|
/>
|
|
</ChartCard>
|
|
|
|
<ChartCard
|
|
id="komp-leer"
|
|
titel="Leerzustand"
|
|
untertitel="components/charts/ChartLeer.tsx: greift, wenn Filter alles wegschneiden"
|
|
>
|
|
<ChartLeer hoehe={180} />
|
|
</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="[Vorname]"
|
|
datum="Donnerstag, 30. Juli"
|
|
rolle="Geschäftsführung"
|
|
lage="2 Punkte brauchen heute deine 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={RESERVIERTE_BEREICHE[2]} />
|
|
</div>
|
|
|
|
<Abschnitt
|
|
titel="Reservierte Kacheln: was noch ins Dashboard kommt"
|
|
text="features/dashboard/components/dashboard/ReservierteKachel.tsx: das Dashboard zeigt heute nur Auswertungen. Diese drei Bereiche kommen dazu; alle drei stehen im Designer-Brief P11 und haben in HiveGrid bereits ihre Widget-Domäne (tasks, schedule, hr). Sie brauchen kein neues Raster, nur Daten."
|
|
/>
|
|
<div className="grid grid-cols-1 gap-4 lg:grid-cols-3">
|
|
{RESERVIERTE_BEREICHE.map((bereich) => (
|
|
<ReservierteKachel key={bereich.key} bereich={bereich} />
|
|
))}
|
|
</div>
|
|
<Hinweis text="Diese Kacheln stehen hier und nicht auf dem Dashboard. Ein Begrüßungsbildschirm ist kein Ort für Ankündigungen. Wer sich morgens anmeldet, will seine Lage sehen und nicht lesen, was später einmal kommt. Für den Einbau ist die Stelle im Raster als Kommentar in DashboardSeite.tsx markiert; der Zuschnitt steht in docs/SCOPE-ANALYTICS-DASHBOARD.md, Abschnitt 3a." />
|
|
<Hinweis text="Sie zeigen 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"
|
|
untertitel="components/tables/StatusBadge.tsx"
|
|
>
|
|
<div className="flex flex-col gap-4">
|
|
<div className="flex flex-wrap items-center gap-2">
|
|
<span className="w-24 text-[12px] text-text-neutral-tertiary">Auftrag</span>
|
|
<AuftragBadge status="anfrage" />
|
|
<AuftragBadge status="aufmass" />
|
|
<AuftragBadge status="angebot" />
|
|
<AuftragBadge status="beauftragt" />
|
|
<AuftragBadge status="in_arbeit" />
|
|
<AuftragBadge status="abgeschlossen" />
|
|
<AuftragBadge status="verloren" />
|
|
</div>
|
|
<div className="flex flex-wrap items-center gap-2">
|
|
<span className="w-24 text-[12px] text-text-neutral-tertiary">Rechnung</span>
|
|
<RechnungBadge status="entwurf" />
|
|
<RechnungBadge status="gestellt" />
|
|
<RechnungBadge status="bezahlt" />
|
|
<RechnungBadge status="ueberfaellig" />
|
|
</div>
|
|
<StatZeile
|
|
eintraege={[
|
|
{ label: "Aufträge", wert: formatZahl(DATENSATZ_INFO.auftraege), hinweis: "im Demo-Datensatz" },
|
|
{ label: "Rechnungen", wert: formatZahl(DATENSATZ_INFO.rechnungen) },
|
|
{ label: "Kunden", wert: formatZahl(DATENSATZ_INFO.kunden) },
|
|
{ label: "Zeiteinträge", wert: formatZahl(DATENSATZ_INFO.zeiteintraege) },
|
|
]}
|
|
/>
|
|
</div>
|
|
</ChartCard>
|
|
|
|
<ChartCard
|
|
id="komp-regeln"
|
|
titel="Regeln, die beim Einbau erhalten bleiben sollten"
|
|
untertitel="Ausführlich in docs/DESIGN-SYSTEM.md"
|
|
>
|
|
<ul className="flex flex-col gap-2.5">
|
|
{[
|
|
"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: weiße Fläche #ffffff, Text #161616, Schatten statt Rahmen. Seit Carbon v11 ist er 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" />
|
|
{regel}
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</ChartCard>
|
|
</PageContainer>
|
|
</>
|
|
);
|
|
}
|
|
|
|
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>
|
|
{/* 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>
|
|
);
|
|
}
|
|
|
|
function Hinweis({ text }: { text: string }) {
|
|
return (
|
|
<p className="rounded-md border border-solid border-border-default bg-white px-3 py-2 text-[12px] leading-relaxed text-text-neutral-secondary">
|
|
{text}
|
|
</p>
|
|
);
|
|
}
|