Files
Dashboard/features/dashboard/components/cards/AlertList.tsx
T
dejanandClaude Opus 5 6a0b36ddcc Geviertstriche aus Text, Kommentaren und Doku entfernen
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
2026-08-31 20:51:19 +02:00

136 lines
5.4 KiB
TypeScript

"use client";
import React from "react";
import {
FeatherAlertTriangle,
FeatherArrowRight,
FeatherCheckCircle,
FeatherInfo,
} from "@subframe/core";
import { STATUS_FARBEN } from "../../utils/chartTheme";
import { formatEuro } from "../../utils/formatters";
import { useToast } from "../../context/ToastContext";
import type { AlertEintrag } from "../../data/calculations";
/**
* Handlungsbedarf-Liste (COMP-08).
*
* Jede Zeile nennt drei Dinge: was los ist, um wie viel Geld es geht und was
* zu tun ist. Ohne den Betrag lassen sich die Zeilen nicht gegeneinander
* abwägen, ohne die Handlung ist die Liste ein Bericht statt einer Arbeitsliste.
* Der Absprung in die jeweilige Arbeitsliste ist im Prototyp eine Attrappe.
*/
export function AlertList({
eintraege,
leerText = "Keine offenen Punkte",
/** Zwei Spalten, wenn die Karte über die volle Seitenbreite läuft. */
spalten = 1,
}: {
eintraege: AlertEintrag[];
leerText?: string;
spalten?: 1 | 2;
}) {
const { zeigeToast } = useToast();
const rang = { kritisch: 0, warnung: 1, hinweis: 2 } as const;
const sortiert = [...eintraege].sort((a, b) => rang[a.art] - rang[b.art]);
if (!sortiert.length) {
return (
<div className="flex h-full min-h-[180px] flex-col items-center justify-center gap-2 text-center">
<FeatherCheckCircle className="text-[22px]" style={{ color: STATUS_FARBEN.positiv }} />
<span className="text-[13px] text-text-neutral-tertiary">{leerText}</span>
</div>
);
}
return (
// Container-Query statt Viewport: dieselbe Liste steht mal über die volle
// Seitenbreite und mal in einer Drittelkarte. Der Viewport weiß davon nichts.
<ul
className={`@container ${
spalten === 2 ? "grid grid-cols-1 gap-x-8 xl:grid-cols-2" : "flex flex-col"
}`}
>
{sortiert.map((eintrag) => {
const farbe =
eintrag.art === "kritisch"
? STATUS_FARBEN.negativ
: eintrag.art === "warnung"
? STATUS_FARBEN.warnung
: STATUS_FARBEN.info;
return (
/**
* Der Trennstrich sitzt oben, nicht unten.
*
* Unten liegend braucht er eine Ausnahme für die letzte Zeile, und
* „die letzte Zeile" gibt es bei zwei Spalten zweimal, einmal je
* Spalte, an einer Stelle, die von der Anzahl der Einträge abhängt.
* Die rechte Spalte behielt dadurch einen Strich, unter dem nichts
* mehr kam.
*
* Oben liegend ist die Ausnahme dagegen die *erste* Zeile jeder
* Spalte, und die steht bei zeilenweisem Fluss immer an derselben
* Stelle: die ersten beiden Kinder. Das gilt unabhängig davon, wie
* viele Einträge die Liste hat.
*/
<li
key={eintrag.id}
className={`border-t border-solid border-border-default first:border-t-0 ${
spalten === 2 ? "xl:[&:nth-child(-n+2)]:border-t-0" : ""
}`}
>
<button
onClick={() =>
zeigeToast(eintrag.aktion, `${eintrag.text}: Absprung folgt in der Umsetzung`)
}
/* -mx-2 und die zusätzliche Breite: Der Untergrund lief vorher
genau bis zum Pfeil und schnitt dort hart ab, bei zwei Spalten
besonders sichtbar, weil daneben 32 px Spaltenabstand ungefärbt
blieben. Jetzt greift er acht Pixel über den Inhalt hinaus und
hat runde Ecken; er liest sich als Fläche unter der Zeile statt
als Kasten um sie herum.
Der Trennstrich sitzt dafür an der Zeile und nicht mehr am
Knopf: Er soll die Zeilen trennen und nicht mitwandern. */
className="group -mx-2 flex w-[calc(100%+1rem)] items-center gap-3 rounded-md px-2 py-3 text-left transition-colors duration-150 hover:bg-neutral-50"
>
<span className="flex-none" style={{ color: farbe }}>
{eintrag.art === "hinweis" ? (
<FeatherInfo className="text-[16px]" />
) : (
<FeatherAlertTriangle className="text-[16px]" />
)}
</span>
<span className="flex min-w-0 grow flex-col">
<span className="min-w-0 break-words text-[13px] font-medium leading-snug text-text-neutral-primary">
{eintrag.text}
</span>
<span className="min-w-0 break-words text-[12px] leading-snug text-text-neutral-tertiary">
{eintrag.detail}
</span>
</span>
{eintrag.betrag !== undefined ? (
<span className="flex-none whitespace-nowrap text-[14px] font-semibold tabular-nums text-text-neutral-primary">
{formatEuro(eintrag.betrag)}
</span>
) : null}
{/* Schmale Karte: nur der Pfeil, die Handlung steht im Tooltip. */}
<span
title={eintrag.aktion}
className="flex flex-none items-center justify-end gap-1 whitespace-nowrap text-[12px] font-medium text-text-neutral-tertiary group-hover:text-text-neutral-primary @md:w-[150px]"
>
<span className="hidden @md:inline">{eintrag.aktion}</span>
<FeatherArrowRight className="text-[13px]" />
</span>
</button>
</li>
);
})}
</ul>
);
}