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
136 lines
5.4 KiB
TypeScript
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>
|
|
);
|
|
}
|