/** * Zustandsanzeigen: Laden, Fehler, Leere, Hinweise. * * `ErrorState` zeigt immer die Vorgangsnummer. `NotMeasured` ist die Antwort * auf einen Grundsatz, der sich durch die ganze Anlage zieht: Unbekannt ist * nicht null. Eine Kennzahl ohne Datengrundlage erscheint als Satz mit * Begruendung, nie als "0" — eine Null liest sich wie "geprueft und in * Ordnung", und genau das waere falsch. */ import { AlertTriangle, Info, Loader2, TriangleAlert } from 'lucide-react'; import type { ReactNode } from 'react'; import { cn } from '@/lib/utils'; import { Button } from './Button'; export function LoadingState({ label = 'Wird geladen …' }: { readonly label?: string }) { return (
{label}
); } export function ErrorState({ title = 'Die Anfrage ist fehlgeschlagen', message, requestId, onRetry, className, }: { readonly title?: string | undefined; readonly message?: string | undefined; readonly requestId?: string | undefined; readonly onRetry?: (() => void) | undefined; readonly className?: string | undefined; }) { return (

{title}

{message ?

{message}

: null} {requestId ? (

Vorgangsnummer: {requestId}

) : null} {onRetry ? ( ) : null}
); } export function EmptyState({ title, description, action, icon, }: { readonly title: ReactNode; readonly description?: ReactNode | undefined; readonly action?: ReactNode | undefined; readonly icon?: ReactNode | undefined; }) { return (
{icon ?
{icon}
: null}

{title}

{description ? (

{description}

) : null} {action ?
{action}
: null}
); } /** * Hinweis fuer eine Kennzahl ohne Datengrundlage. * * Der Grund gehoert dazu. "—" allein sieht nach einem Fehler der Oberflaeche * aus; "nicht gemessen, weil kein Wiederherstellungstest lief" ist eine * Handlungsanweisung. */ export function NotMeasured({ reason }: { readonly reason: ReactNode }) { return ( {reason} ); } /** Auffaelliger Hinweiskasten. */ export function Callout({ tone = 'info', title, children, className, role = 'note', }: { readonly tone?: 'info' | 'warning' | 'critical' | 'healthy' | undefined; readonly title?: ReactNode | undefined; readonly children?: ReactNode | undefined; readonly className?: string | undefined; /** * ARIA-Rolle. * * Standard ist `note` — die meisten Hinweise erklaeren etwas und stehen schon * beim Oeffnen der Maske da. Ein Hinweis, der **als Folge einer Handlung** * erscheint, gehoert dagegen auf `alert`: Ein Screenreader sagt ihn dann an, * statt ihn im Seiteninhalt untergehen zu lassen. */ readonly role?: 'note' | 'alert' | 'status' | undefined; }) { const toneClasses = { info: 'border-info/30 bg-info-bg text-info', warning: 'border-warning/30 bg-warning-bg text-warning', critical: 'border-critical/30 bg-critical-bg text-critical', healthy: 'border-healthy/30 bg-healthy-bg text-healthy', } as const; const IconComponent = tone === 'info' ? Info : tone === 'healthy' ? Info : AlertTriangle; return (
{title ?

{title}

: null} {children ? (
{children}
) : null}
); } /** Beschriftetes Wertepaar fuer Detailseiten. */ export function DetailItem({ label, children, className, }: { readonly label: ReactNode; readonly children: ReactNode; readonly className?: string | undefined; }) { return (
{label}
{children}
); }