52 KiB
WOMS „Weißes Design“ P7: Finanzen – Umsetzungsplan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Nach P7 sind alle Finanzseiten ohne Karten, Kästen und Icons gestaltet: Übersicht, Einstellungen, Bankrückkehr und der Reiter „Rechnungen“ am Kunden. Die Dialoge „Buchung zuordnen“ und „Rechnung schreiben“ laufen auf Dialog. Kpi ist gelöscht. Funktion, Rückfragen und Texte bleiben.
Architecture:
- Der Finanzblock in
src/styles/global.css(heute ab/* --- Finanzen --- */, ca. 320 Zeilen) wird Region für Region neu geschrieben, jeweils zusammen mit den Komponenten der Region. Er enthält nur Tokens, keine Farbliterale und kein!important. Task 3 rahmt ihn mit zwei Marken, weil P5 und P6 hinter ihm eigene Blöcke anhängen. - Die Container-Queries (560 Vorschau, 640 Buchungen, 860 Rechnungen) bleiben.
container-typesitzt auf einem neuen Wrapper.fin-bodyunter der Abschnittsüberschrift, Dialoge stehen nie darin. - Neue Datei
src/lib/financeUi.jsmit zwei reinen Helfern: Vorzeichen-Rot und Meldungsklassen.
Tech Stack: React 18, react-router-dom 6, Vite 5, reines CSS. Tests mit node --test, Screenshot-Werkzeug aus P0.
Spec: docs/superpowers/specs/2026-10-04-weisses-design-design.md, Abschnitte „Gestaltungsregeln“, „Bausteine“ (Abschnitt, Knöpfe, Badge, Tabelle, Schlüsselwerte, Formular, Dialog, Meldungen, Laden), „Bewusste Änderungen“ (Esc), „Risiken“ (Scroll-Anker, Vorschlagslisten in Dialogen) und Paket P7. Dazu Anhang …-umbaukarte.md, Abschnitte 3, 4, 5/P7, 7.8. Globale Regeln und Schnittstellen: 2026-10-05-weisses-design-00-uebersicht.md.
Global Constraints
Es gelten die Regeln der Übersicht. Für P7 kommt hinzu:
- Befehle: Jeder Run-Block beginnt mit
source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh &&. Danach gelten$WT,$Wund$TMPDIR. Tests und Build laufen nur über"$W/check.sh", Browser-Läufe nur über"$W/shots.sh".FINist die kommagetrennte Liste aller Ansichten-IDs der Tabelle aus Task 2 Step 2 (Shell-Variable, gesetzt in Task 2 Step 4). - Gate-Läufe:
--gate p7schalteticon-svgfür dieFIN-Ansichten scharf. Die Finanzseite ist erst nach Task 5 ohne Icons, deshalb laufen Task 3 und 4 mit--gate p6(dieselben übrigen Prüfungen). Ab Task 5 gilt--gate p7. - Dateien: nur
src/pages/FinancePage.jsx,FinanceSettingsPage.jsx,BankCallbackPage.jsx, alle 17 Dateien insrc/components/finance/,src/components/ui/Kpi.jsx(löschen),src/components/ui/index.js(Export entfernen),src/lib/financeUi.js(neu),src/styles/global.css(nur der Finanzblock). Allesrc/lib/finance*.js,src/hooks/*,CustomerDetailPage.jsxund alle bestehenden Tests bleiben unberührt. - Texte wörtlich. Erlaubt sind nur
▾/▴statt Chevron-Icons, „+ Position“ (Plus-Icon wird Zeichen), „Entfernen“ in der Positionszeile (heute nur Icon) und „← Zurück zu den Finanzen“. Ladetexte („Speichere …“, „Sende …“, „Rechnungen werden geladen …“) bleiben, nur Spinner und Icons entfallen. - Tonnamen
muted|info|ok|warn|dangerausfinanceFormat.js/financeView.jsbleiben, gemappt wird nur inBadge. - Beträge behalten das Vorzeichen (U+2212). Rot (
--red-fg) gilt genau dann, wenn der angezeigte Text mit „−“ beginnt: Klasse.fin-neg, bei Kennzahlentone="neg". Wo die Lib schon markiert (saldoSicherNegative,saldoErwartetNegative,low.negative,shortfall), gilt diese Markierung weiter. - Container-Falle:
container-type: inline-sizemacht das Element zum Containing Block fürposition: fixed. Es steht nur auf.fin-body,AssignDialogundInvoiceCreateModalsind nie Nachfahren davon. - Hygiene in allen P7-Dateien: keine Icon-Importe, kein
style={{…}}, keinsize="sm", keine Farbliterale, keine Klassenui-btn,form-control,form-group,form-label,overlay-content,overlay-close,spinner,kpi.lockRef/busyRefund allewindow.confirm-Rückfragen bleiben.
Gemeinsame Ersetzungen (Komponenten und CSS):
| Alt | Neu |
|---|---|
Spinner plus Text im .empty |
<div className="loading" role="status">Text wörtlich</div> |
fin-hint / fin-hint-warn / fin-dialog-* |
msg muted / msg warn (role="alert" bleibt) / Fehler im Dialog msg err |
| tonabhängige Meldung | <p {...noticeProps(tone, 'fin-notice')}> |
Button variant="ghost", size="sm" |
Button (Standard), size entfällt |
| Aktion in Zeile, Liste oder Meldung | <button type="button" className="link">, Links <a className="link">, destruktiv Button variant="danger" |
form-control / form-group + form-label |
input/select/textarea / Field bzw. FormField |
| CSS-Farben und -Maße | --text-muted, --text-faint → --text-2; --warn → --orange-fg; Text---danger → --red-fg (Formularfehler bleibt --danger); --border → --line; Flächen und Kartenrahmen entfallen; Gewicht 700/800 → 600 (Tabellentitel 500); Großbuchstaben entfallen; Media-Queries 640/700/720 → 860 |
Die CSS-Schritte nennen nur Abweichungen, Spezifitäts- und Reihenfolgeregeln sowie Maße, die ein Gate-Schritt prüft. Werte, die dort fehlen, bleiben wie im heutigen Finanzblock und folgen der Ersetzungstabelle.
Steuerelemente, die erreichbar bleiben (Namen unverändert):
- Übersicht: „Einstellungen“, „Aktualisieren“, „Aufschlüsselung“, der eine Knopf je Aufgabe (bank-verbinden, bank-aktualisieren, rechnung-oeffnen, zuordnen, kunde-oeffnen, fixkosten-uebernehmen, einstellungen), „Alle N anzeigen“/„Weniger anzeigen“, „Schließen“ (Zuordnen-Hinweis), jedes „Erneut versuchen“, das heute steht. Rechnungen: „Rechnung schreiben“, Filter Offen/Überfällig/Bezahlt/Entwürfe/Alle, Aktionen Zahlungsseite, PDF, Senden, Erinnerung senden, Stornieren, Löschen. Buchungen: „Zuordnen“/„Ändern“. Pager: „Zurück“/„Weiter“.
- Dialoge: „ד, „Abbrechen“, „Schließen“, „Zuordnen“, „Zuordnung lösen“, „Zurück“, „Ja, als bezahlt markieren“, „Als Entwurf speichern“, „Senden“, „+ Position“, „Entfernen“, „Zur Übersicht“.
- Einstellungen und Rest: „Speichern“, „Jetzt abrufen“, „Neu verbinden“/„Bank verbinden“, „Neue Position“, „Bearbeiten“, „Aktivieren“/„Auf inaktiv setzen“, „Löschen“, „Abbrechen“, „Zu den Finanzen“, „Daten erfassen“, „← Zurück zu den Finanzen“.
Entscheidungen (die Spec schweigt; nächste Lösung zu heute):
- Entscheidung:
.fin-stackist ein normaler Block ohnegapund ohne Flex. Der Abstand der Seitenteile istmargin-top: 36pxauf jedem Geschwister nach dem ersten (.fin-stack > * + *). Das ist derselbe Wert wie.page section + sectionaus P1. Beide Regeln setzen dieselbe Eigenschaft am selben Element, also addiert sich nichts. Eingap: 36pxergäbe zwischen zwei Abschnitten 72 px. Als Block lässt.fin-stackden oberen Rand vonh2.section:first-child(8 px, weilCarddie Überschrift als erstes Kind in ein<section>setzt) mit dem Abschnittsabstand verschmelzen. Eine Sonderregel für die Überschrift entfällt. - Entscheidung: Finanztabellen sind keine
tr.row(Zeilen nicht klickbar, Aktionen sind echte Knöpfe und Links). Es gibt kein.hide-sm/.hide-md, schmal stapeln die Container-Queries die Zeilen wie heute. - Entscheidung: Die Aufgaben-Aktion je Zeile und „Alle N anzeigen“ sind
.link(bis zu sieben Ringknöpfe untereinander wären zu unruhig). „Aktualisieren“, allein stehendes „Erneut versuchen“, „Jetzt abrufen“ und „Neue Position“ sind Standardknöpfe. - Entscheidung: Pro Abschnitt höchstens ein schwarzer Knopf (Rechnung schreiben, Speichern, Jetzt abrufen bzw. Bank verbinden, Zuordnen/Senden im Dialog). Die Seitenköpfe haben keinen.
- Entscheidung: Die Aufschlüsselungszeilen („− Steuer-Rücklage …“) bleiben neutral, weil sie Abzüge sind. Rot sind Summen, Salden sowie Posten- und Buchungsbeträge.
- Entscheidung: Der Altersstreifen ist
<ul class="stats fin-ages">mitStatje Gruppe. Er überschreibt das Flex-Layout durch 4 Spalten, ab Container ≤ 860 px 2 Spalten (die Handy-Regel von.statsergäbe 3 + 1). - Entscheidung: Der Dialog-Fuß ist
<div className="fin-foot">(Flex, umbrechend, rechtsbündig, auf dem Handy gleich breite Knöpfe) statt<span className="grow" />. Mit.dialog-foot { flex-wrap: wrap }aus P6 allein stünde der dritte Knopf bei 390 px linksbündig in einer zweiten Zeile.
Review Focus
Aus der Übersicht gehören diese Punkte zu P7:
- Punkt 2, lange Inhalte am Handy: Testdaten
finanz-minusmit Kundenname (60 Zeichen), Verwendungszweck (140 Zeichen ohne Leerzeichen) und Gegenpartei (80 Zeichen).overflow-xist im Gatep7für alle Ansichten scharf. - Punkt 3, Teilausfall:
finanzen-teilausfall(Stripe aus, Abos nicht ladbar, Rechnungen und Buchungen antworten 500). Jeder Block zeigt seine Meldung mit „Erneut versuchen“, die Seite bleibt bedienbar. - Punkt 4, nur Tastatur: Esc, Tab-Falle, gesperrtes „ד bei
busy, „Enter sendet nie“ und „Esc schließt zuerst die Vorschlagsliste“ sind Schritte indlg-zuordnen-*unddlg-rechnung-*. - Punkt 5, viele Einträge: 120 Buchungen, 60 Rechnungen, 50 Zeilen je Seite (
finanzen-pager).
Zusätzlich für P7 (Risiken der Spec): Scroll-Anker finanzen-rechnungen und finanzen-buchungen liegen nicht unter der Mobil-Leiste (finanzen-anker). Die Vorschlagsliste im Kundenfeld wird im scrollenden .dialog-body nicht abgeschnitten (dlg-rechnung-kunde). Negative Beträge sind rot und tragen ihr Minus (finanzen-minus, finanzen-tabellen).
Task 1: Hilfsfunktionen financeUi
Files:
- Create:
src/lib/financeUi.js - Test:
tests/unit/financeUi.test.mjs(neu)
Interfaces:
-
Consumes:
euro,euroWhole,signedEuroaussrc/lib/financeFormat.js(nur im Test). -
Produces:
isNegText(text) → boolean: wahr, wenntextein String ist und mit „−“ (U+2212) beginnt.noticeProps(tone, extra = '') → { className, role }:'ok'→msg ok/status,'warn'→msg warn/alert, sonstmsg muted/status.extrahängt mit Leerzeichen anclassName.
-
Step 1: Paketstart und Ausgangslage
Run: source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && git fetch origin && git merge-base --is-ancestor design-p6 HEAD && git rebase origin/test && git status --short && "$W/check.sh"
Expected: Der Rebase läuft ohne Konflikt, git status ist leer, # fail 0. Ist design-p6 kein Vorfahr von HEAD, stoppt der Agent und meldet das. Testzahl N sowie JS- und CSS-Größe aus der check.sh-Ausgabe werden notiert (Vergleich in Task 9).
Run: source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && git log --oneline origin/test..feature/terminplaner-tp2 -- src/pages/FinancePage.jsx src/pages/FinanceSettingsPage.jsx src/pages/BankCallbackPage.jsx src/components/finance src/components/ui/Kpi.jsx src/components/ui/index.js src/lib/financeUi.js src/styles/global.css
Expected: keine Ausgabe. Gibt es Treffer, stoppt der Agent und fragt den Eigentümer. Er empfiehlt dabei, TP2 zuerst nach test zu bringen.
- Step 2: Failing Test schreiben
import test from 'node:test'
import assert from 'node:assert/strict'
import { loadModule } from './load.mjs'
const { isNegText, noticeProps } = await loadModule('src/lib/financeUi.js')
const { euro, euroWhole, signedEuro } = await loadModule('src/lib/financeFormat.js')
test('isNegText: nur das angezeigte Minus zählt', () => {
assert.equal(isNegText(euro(-449)), true)
assert.equal(isNegText(signedEuro(-449)), true)
assert.equal(isNegText(euroWhole(-123456)), true)
assert.equal(isNegText(euroWhole(-40)), false) // „0 €“: Rundung auf 0 trägt kein Vorzeichen
assert.equal(isNegText(signedEuro(16879)), false)
assert.equal(isNegText(euro(null)), false) // „–“ (U+2013)
for (const v of ['-4,49 €', '', undefined, null, 5]) assert.equal(isNegText(v), false)
})
test('noticeProps: Klasse und Rolle je Ton', () => {
assert.deepEqual(noticeProps('ok'), { className: 'msg ok', role: 'status' })
assert.deepEqual(noticeProps('warn'), { className: 'msg warn', role: 'alert' })
assert.deepEqual(noticeProps('info'), { className: 'msg muted', role: 'status' })
assert.deepEqual(noticeProps(undefined), { className: 'msg muted', role: 'status' })
assert.deepEqual(noticeProps('warn', 'fin-notice'), { className: 'msg warn fin-notice', role: 'alert' })
})
- Step 3: Test laufen lassen
Run: source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && node --test tests/unit/financeUi.test.mjs
Expected: FAIL (Modul financeUi.js fehlt).
- Step 4:
financeUi.jsimplementieren
Zwei benannte Exporte nach der Signatur oben, deutscher Kommentar im Stil der Nachbardateien, keine Abhängigkeit.
- Step 5: Test grün
Run: source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && node --test tests/unit/financeUi.test.mjs && "$W/check.sh" --nur-tests
Expected: PASS, # pass = N + 2, # fail 0, Exit 0.
- Step 6: Commit
git add src/lib/financeUi.js tests/unit/financeUi.test.mjs
git commit -m "Finanzen: Helfer fuer Vorzeichen-Rot und Meldungsklassen" -m "Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>"
Task 2: Werkzeug – Finanzdaten, Ansichten, Gate p7
Files (unter $W/):
- Create:
fixtures/finanz-minus.mjs,fixtures/finanz-teilausfall.mjs - Modify:
views.mjs,gates.mjs, bei Bedarfbackend.mjs(POST …/finanzen/buchungen/:id/zuordnen→{ buchung },POST …/finanzen/bank/rueckkehr→{ ok: true, validUntil: '2027-03-29' })
Interfaces:
-
Consumes:
VIEWS- undGATES-Schema aus der Übersicht (id,path,fixtures,scenario,steps(page, ctx)). Schritte werfen bei Abweichung (node:assert/strict),shots.mjsmeldet das alsstep-failed.ctx.backend.callslistet alle beantworteten Requests. Portal-Antworten unterhttps://portal.test/api/admin/finanzen/…. -
Produces: die Ansichten der Tabelle unten und das Gate
p7(Aufbau nach der Übersicht, Step 3). -
Step 1: Testdaten anlegen
Beide Dateien leiten von standard.mjs ab und ersetzen nur die Finanz-Antworten (gleiche Form).
-
finanz-minus:- Kopf: Kontostand −1.234,56 €,
verfuegbarCents< 0,tiefsterSichernegativ,tiefsterErwartetpositiv (beweist Rot je Betrag). - Vorschau mit 8 Posten und negativen Salden. 9 Aufgaben, darunter je eine mit
rechnung-oeffnenohnehostedUrl,zuordnen,bank-aktualisieren,fixkosten-uebernehmen. - Ein Abo
past_due(gefährdet), alle 4 Altersgruppen mit Werten. - 60 Rechnungen aller Status (eine Abo-Rechnung, ein Kundenname mit 60 Zeichen). 120 Buchungen: Ein- und Ausgänge,
PDNG, Verwendungszweck mit 140 Zeichen ohne Leerzeichen, Gegenpartei mit 80 Zeichen, offene Eingänge mitvorschlaege(Rechnung und Auszahlung). - Ein fester Kunde mit E-Mail, aber ohne Straße, PLZ und Ort.
- Kopf: Kontostand −1.234,56 €,
-
finanz-teilausfall:stand.stripe = { ok: false, fehler: 'Stripe ist gerade nicht erreichbar.' },abos = { fehler: 'Die Abos konnten nicht geladen werden.' }, Kopf ohne Kontostand. -
Step 2: Ansichten anlegen oder anpassen
| ID | Aufbau | Prüfung (Schritt wirft bei Abweichung) |
|---|---|---|
finanzen (vorhanden) |
/finance |
Alle Namen aus „Steuerelemente, die erreichbar bleiben“ (Global Constraints) per getByRole auffindbar. Mindestens ein button.link, alle mit transparentem background-color und border-top-width: 0px (so auch jedes button.link-danger in finanzen-tabellen). |
finanzen-minus |
finanz-minus; Klick „Aufschlüsselung“ |
Text in .stat .v bzw. .fin-low .fin-amount, der mit „−“ beginnt, trägt .v.neg bzw. .fin-neg. Kein .v.neg ohne „−“ (außer in .fin-abo-stats). Kein .fin-breakdown li mit .fin-neg. |
finanzen-tabellen |
finanz-minus |
.fin-forecast-amount, .fin-tx-amount mit „−“ tragen .fin-neg, die anderen nicht. thead von .fin-forecast, .fin-invoices, .fin-tx ist bei 1440 nicht position: absolute, bei 390 absolute. Dreimal .fin-body mit container-type: inline-size, kein .overlay darin. Zwischen #finanzen-rechnungen und #finanzen-buchungen liegen 36 px (± 1 px), nicht 72. Bei 1440 liegen die Oberkanten der beiden Abschnitte in .fin-cols gleich hoch (± 1 px). |
finanzen-pager |
finanz-minus, zu #finanzen-buchungen, „Weiter“ |
Fuß „Seite 2 von 3 · 120 Buchungen“, 50 .fin-tx tbody tr. |
finanzen-anker |
finanz-minus, Klick „Zu den Rechnungen“, 800 ms |
#finanzen-rechnungen hat scroll-margin-top 16px (1440) bzw. 68px (390), seine Oberkante liegt dort (± 1 px). |
finanzen-teilausfall |
finanz-teilausfall, Szenario fehler, /rechnungen und /buchungen → 500 (page.route), Reload |
Mindestens 2 [role=alert] und 2 „Erneut versuchen“, h1 „Finanzen“ sichtbar, „Aktualisieren“ nicht disabled. |
finanzen-fehler |
Szenario fehler, /uebersicht → 500, Reload |
[role=alert] und klickbares „Erneut versuchen“. |
dlg-rechnung (vorhanden) |
Klick „Rechnung schreiben“ | role=dialog mit Name „Rechnung schreiben“, Klasse dialog-wide. Overlay-Rechteck so groß wie das Fenster, kein Vorfahr .fin-body. Fokus im Dialog. |
dlg-rechnung-kunde |
„Mü“ in #fin-invoice-customer |
Feldbreite ≥ 90 % von .dialog-body. [role=listbox] bei 1440 ganz im .dialog, bei 390 ≥ 120 px sichtbar. Esc schließt nur die Liste, das zweite Esc den Dialog. |
dlg-rechnung-enter |
Fokus in Beschreibung, Enter; ctx.backend.calls zählt POST …/rechnungen |
Dialog bleibt, 0 Requests. „Als Entwurf speichern“ ohne Kunde zeigt .msg.err[role=alert] im sichtbaren Teil von .dialog-body. |
dlg-zuordnen-zahlung (vorhanden) |
Klick „Zuordnen“ an erster offener Eingangsbuchung | Vorschläge sind label.check mit Radio. 12 × Tab und 12 × Umschalt+Tab bleiben im .dialog. |
dlg-zuordnen-bestaetigung |
Vorschlag mit Rechnung, „Zuordnen“ | .fin-confirm nennt „in Stripe als bezahlt markiert“, Fuß „Zurück“ und „Ja, als bezahlt markieren“, ctx.backend.calls enthält kein POST …/zuordnen. Esc schließt ohne Request. |
dlg-zuordnen-busy |
wie oben, POST …/zuordnen 1500 ms verzögert, „Ja, …“ |
Währenddessen .dialog-close disabled, Esc lässt den Dialog offen. Danach schließt er. |
finanzen-einstellungen (vorhanden) |
/finance/einstellungen |
h2 „Einstellungen“, „Bankzugang“, „Fixkosten“, Link „← Zurück zu den Finanzen“. |
finanzen-einstellungen-fehler |
Mindestpuffer „abc“, „Speichern“, dann „Neue Position“ | [aria-invalid="true"] hat box-shadow mit rgba(192, 18, 60, .msg.err[role=alert] am Feld. h3 „Neue Position“, Fokus in #fin-fix-name. |
finanzen-bank-callback, -ok |
/finanzen/bank-callback, bzw. mit ?code=x&state=y |
„Zu dieser Seite liegt keine Rückmeldung der Bank vor.“ bzw. .msg.ok mit „Bankzugang erneuert, gültig bis“. |
kunde-invoices (vorhanden), kunde-invoices-adresse |
Kunde ohne Anschrift aus finanz-minus (andere URL als kunde-invoices) |
.fin-address-warn-page mit Rolle alert und „Zur Übersicht“. „Senden“ eines Entwurfs über 250 € ist disabled. |
- Step 3: Gate-Eintrag
Ein Eintrag nach dem Schema der Übersicht. p7 ist p6 plus icon-svg für die FIN-Ansichten und die aller früheren Pakete. Eine ignore-Liste des Vorgängers bleibt unverändert.
p7: {
...GATES.p6,
scoped: {
...GATES.p6.scoped,
'icon-svg': { ...GATES.p6.scoped['icon-svg'], views: [...GATES.p6.scoped['icon-svg'].views, /* IDs aus FIN */] },
},
},
Die absichtlichen 500er der zwei Fehleransichten schreibt der Browser als console.error („Failed to load resource“). Deshalb laufen sie im Szenario fehler, dessen Meldungen console-error ausnimmt. Nimmt checks.mjs nur Meldungen der eigenen Fehlerantworten aus, wird die Ausnahme um Meldungen zu …/api/admin/finanzen/ erweitert.
- Step 4: Vorher-Lauf
Run: source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && FIN=finanzen,finanzen-minus,finanzen-tabellen,finanzen-pager,finanzen-anker,finanzen-teilausfall,finanzen-fehler,finanzen-einstellungen,finanzen-einstellungen-fehler,finanzen-bank-callback,finanzen-bank-callback-ok,dlg-rechnung,dlg-rechnung-kunde,dlg-rechnung-enter,dlg-zuordnen-zahlung,dlg-zuordnen-bestaetigung,dlg-zuordnen-busy,kunde-invoices,kunde-invoices-adresse && "$W/build.sh" "$WT" p7 && "$W/shots.sh" p7 "$W/shots/p7-vorher" --only "$FIN" --gate p7
Expected: Exit 1. icon-svg meldet finanzen, finanzen-einstellungen, dlg-rechnung, dlg-zuordnen-zahlung. Die Schritte schlagen an in finanzen, finanzen-minus, finanzen-tabellen, finanzen-anker (390 px), finanzen-einstellungen-fehler, dlg-rechnung* und dlg-zuordnen-zahlung. finanzen-pager, finanzen-teilausfall, finanzen-fehler, dlg-zuordnen-bestaetigung und dlg-zuordnen-busy sind Wächter für heutiges Verhalten und laufen schon vorher durch.
Task 3: Seite, Statuszeile, Liquiditätskopf, Aufgaben; Kpi löschen
Files:
- Modify:
src/pages/FinancePage.jsx,src/components/finance/StatusLine.jsx,LiquidityHead.jsx,TodoList.jsx,src/components/ui/index.js - Delete:
src/components/ui/Kpi.jsx - Modify:
src/styles/global.css(Marken um den Finanzblock, Regionen „Allgemein“ bisBlock 2: Zu erledigen,.kpi*)
Interfaces:
-
Consumes:
Stat({ label, value, sub, tone }),Card,Badge,EmptyState,PageHeader,Button(P1),isNegText,noticeProps(Task 1). -
Produces: die Klassen
.fin-stack,.fin-top,.fin-block,.fin-body,.fin-table-wrap,.fin-notice,.fin-load-error,.fin-cols,.fin-neg,.fin-flag,.fin-amount,.fin-srfür Task 4 bis 8. -
Step 1: P1-Voraussetzungen prüfen
Run: source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && rg -n "Stat|Dialog" src/components/ui/index.js && rg -n "\.stat \.sub|\.table-foot|\.msg\b|\.loading|\.field-row\b" src/styles/global.css | head
Expected: Stat und Dialog sind exportiert, die Klassen definiert. Fehlt ein Export, wird er in index.js ergänzt.
- Step 2: Finanzblock rahmen, CSS-Region „Allgemein“
Zuerst die Grenzen setzen.
Run: source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && rg -n "^/\* (--- Finanzen|Finanzen: Einstellungen|=====|Kunden-Unterseiten|Projekte \(P5\))" src/styles/global.css
Expected: Der Finanzblock beginnt bei /* --- Finanzen --- */ und endet vor dem nächsten Blockkommentar (P6, KOMPAT …) bzw. am Dateiende. Davor kommt die Marke /* ===== FINANZEN (P7) ===== */, dahinter /* ===== ENDE FINANZEN ===== */.
Run: source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && rg -n -A2 "===== FINANZEN \(P7\)" src/styles/global.css
Expected: die Marke, danach .fin-amount.
.fin-amount und .fin-sr bleiben (Werte gleich). Neu oder geändert:
| Regel | Wert |
|---|---|
.fin-neg, .fin-flag |
color: var(--red-fg), .fin-flag zusätzlich font-weight: 600 |
.fin-stack |
keine eigene Regel (Block, kein gap, siehe Entscheidung oben) |
.fin-stack > * + * |
margin-top: 36px |
.fin-top |
display: flex; flex-direction: column; gap: 20px |
.fin-block |
scroll-margin-top: 16px, bei ≤ 860 px 68px (Mobil-Leiste 52 px + 16 px) |
.fin-body |
container-type: inline-size (einzige Stelle) |
.fin-table-wrap |
overflow-x: auto |
.fin-notice, .fin-page-notice |
margin: 0 0 8px, margin-bottom: 16px |
.fin-load-error |
display: grid; gap: 12px; justify-items: start; padding: 16px 0 |
.fin-cols |
einspaltig, gap: 0 56px (den Zeilenabstand liefert .page section + section); ab 1100 px minmax(0,1.8fr) minmax(0,1fr), .fin-cols-half 2 × minmax(0,1fr) und zusätzlich .fin-stack .fin-cols > section + section { margin-top: 0 }, damit beide Spalten oben bündig stehen (höhere Spezifität als die globale Regel, kein !important). |
Gelöscht: .fin-spin, .fin-hint*, .fin-link*, .fin-stack .kpi-grid. P5 legt den Reset für button.link an. Zeigt die Prüfung in finanzen oder finanzen-tabellen (Task 2) Browser-Rahmen an button.link oder button.link-danger, kommt button.link-danger in dieselbe Regel (background: none; border: 0; padding: 0; font: inherit; cursor: pointer).
-
Step 3: CSS-Blöcke 0, 1, 2
-
Block 0: Die Statuszeile hat keinen Kasten:
.fin-statusFlex,space-between, umbrechend, Abstand8px 16px, 13 px,--text-2..fin-status-itemsFlex, Abstand4px 24px..fin-status-warn--orange-fg, 600. -
Block 1:
.stats.fin-headist ein Rasterrepeat(auto-fit, minmax(220px, 1fr)), Abstand24px 48px,margin: 0. Die Handy-Regel von.stats(3 Spalten) würde die breite dritte Kennzahl abschneiden. Die Doppelklasse schlägt.statsdurch Spezifität..stats.fin-head .sub13 px--text-2..fin-breakdown-totalmitborder-top: 1px solid var(--line), 600..fin-lowsRasterauto auto minmax(0,1fr)..fin-low .fin-amount18/26 px, 600.
-
Block 2:
.fin-todosund.fin-todomit Haarlinienvar(--line)..fin-todoRaster80px minmax(0,1fr) auto, Abstand12px 16px,padding: 12px 0, bei ≤ 860 px einspaltig. Die Streifenklassen.fin-todo-hoch/-mittelund.fin-todos-moreentfallen. -
Step 4:
Kpiund.kpi*löschen
Run: source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && git rm -q src/components/ui/Kpi.jsx. Dann die Zeile export { default as Kpi } aus src/components/ui/index.js und alle .kpi-Regeln samt .fin-head .kpi-value aus global.css entfernen (falls P1 sie nicht schon gelöscht hat).
Expected: Nach Step 5 liefert rg -n "Kpi|kpi" src keinen Treffer.
-
Step 5: Komponenten umbauen
-
FinancePage:- Wurzel
.page. Kopf:PageHeader, „Einstellungen“ alsButton as={Link}ohne Icon. Ladezustandloading. - Ohne Daten:
msg errplusButton variant="primary"„Erneut versuchen“, ohneCardundstyle. - Mit Daten:
.fin-stack>[Teilfehler: msg warn mit .link „Erneut versuchen“],.fin-top(StatusLine,LiquidityHead),TodoList,.fin-cols(ForecastTable,TaxCard),.fin-cols.fin-cols-half(AboCard),InvoiceTable,TransactionTable. assignNoticealsmsg warn fin-page-noticemit.link„Schließen“.AssignDialogbleibt am Ende der.page.
- Wurzel
-
StatusLine: Zeilen als.fin-status-item(Warnungen.fin-status-warn, ohne Icon). „Aktualisieren“/„Aktualisiere …“ ist ein Standardknopf. Hinweis übernoticeProps(hint.limited ? 'info' : 'warn'). -
LiquidityHead:<div className="stats fin-head">mit dreiStat. Kontostand:tone=isNegText(euroWhole(…)) ? 'neg' : ''. Verfügbar:tone=shortfall ? 'neg' : '', Flagge „Rücklagen nicht gedeckt“ als.fin-flag.- Tiefster Stand ohne
tone, Beträge in.fin-lowsbekommen.fin-negbeilow.negative. - „Aufschlüsselung“ ist
<button className="link">mit<span aria-hidden="true">▾/▴</span>, die Zeilen vonbreakdownRowsbleiben neutral.
-
TodoList:Cardmit Badge „n offen“ als Aktion. Zeile.fin-todoohne Zusatzklasse für die Dringlichkeit, Aktion als<button className="link">(„Moment …“ während des Laufs, alle gesperrt), Fehler alsmsg warn. „Alle N anzeigen“ als.linkin<div className="table-foot">. -
Step 6: Hygiene, Bau, Bild
Run: source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && rg -n "react-icons|style=\{|size=\"sm\"|ui-btn|Kpi" src/pages/FinancePage.jsx src/components/finance/{StatusLine,LiquidityHead,TodoList}.jsx; "$W/build.sh" "$WT" p7 && "$W/shots.sh" p7 "$W/shots/p7-t3" --only finanzen,finanzen-minus,finanzen-anker,finanzen-teilausfall,finanzen-fehler --gate p6
Expected: rg ohne Treffer, Gate Exit 0. In finanzen-minus.png: drei Kennzahlen nebeneinander, Kontostand und Verfügbar rot, beim „Tiefsten Stand“ nur der negative Betrag, Aufgaben als Haarlinienzeilen ohne Streifen.
- Step 7: Commit
git add src/styles/global.css src/pages/FinancePage.jsx src/components/finance/StatusLine.jsx src/components/finance/LiquidityHead.jsx src/components/finance/TodoList.jsx src/components/ui/index.js
git commit -m "Finanzen: Seitenkopf, Statuszeile, Kennzahlen und Aufgaben ohne Karten; Kpi entfernt" -m "Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>"
(git rm hat Kpi.jsx schon gestaged.)
Task 4: Vorschau, Steuer, Abos
Files:
- Modify:
src/components/finance/ForecastTable.jsx,TaxCard.jsx,AboCard.jsx,src/styles/global.css(Blöcke 3, 4, 5)
Interfaces:
-
Consumes:
.fin-body,.fin-table-wrap,.fin-neg,.fin-cols(Task 3),Stat,.table,.stats. -
Produces: Signaturen unverändert (
ForecastTable({ vorschau, kontostandCents, stripeDown, abosFehler }),TaxCard({ steuer }),AboCard({ abos })). -
Step 1: CSS Blöcke 3, 4, 5
-
Vorschau:
.fin-forecast-labeloverflow-wrap: anywhere. Randzellen-Padding,th:first-childund Hover-Regeln entfallen.@container (max-width: 560px)bleibt mit denselben Flächen (date kind amount / label / sicher erwartet), Trenner1px solid var(--line),padding: 10px 0. -
Steuer:
.fin-factsRaster, Abstand 16 px..fin-factmit Haarlinie untenvar(--line), der letzte ohne..fin-fact-big22/30, 600..fin-fact-unknown--orange-fg. -
Abos:
.fin-abosund.fin-abomit Haarlinienvar(--line)..fin-aboRasterminmax(0,1fr) auto,padding: 12px 0. -
Gelöscht:
.fin-stats,.fin-stat-*,.fin-todo-card,.fin-forecast-card,.fin-tax-card,.fin-abo-card. -
Step 2: Komponenten umbauen
-
ForecastTable:Card titleohneclassNameundbodyStyle. Darunter.fin-body>.fin-table-wrap><table className="table fin-forecast …">(Rollen,caption.fin-sr,data-labelunverändert). Betragszelle mitfin-negbeiisNegText(row.amountText). Hinweise unter der Tabelle alsmsg warn(role="status") ohne Icon, die Annahme alsmsg muted. -
TaxCard: gleiche Struktur ohne Card-Klasse.v.noteTextsteht als<p className="msg muted fin-tax-note">, Wortlaut unverändert (rechtlich relevant). -
AboCard:<div className="stats fin-abo-stats">(Haken fürfinanzen-minus, ohne eigene Regel) mitStat„Abo-Einnahmen pro Monat“, „zahlende Kunden“ und, nur wenn gesetzt, „gefährdet“ mittone="neg". Liste wie bisher, Fehlerfallmsg warnohne Icon. -
Step 3: Hygiene, Bau, Bild
Run: source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && rg -n "react-icons|style=\{|bodyStyle|Kpi" src/components/finance/{ForecastTable,TaxCard,AboCard}.jsx; "$W/build.sh" "$WT" p7 && "$W/shots.sh" p7 "$W/shots/p7-t4" --only finanzen,finanzen-minus,finanzen-teilausfall --gate p6
Expected: rg ohne Treffer, Exit 0. Bild: bei 390 px Vorschau-Zeilen dreizeilig (Datum/Art/Betrag, Posten, beide Salden), bei 1440 als Tabelle; Steuer als Liste mit Haarlinien.
- Step 4: Commit
git add src/styles/global.css src/components/finance/ForecastTable.jsx src/components/finance/TaxCard.jsx src/components/finance/AboCard.jsx
git commit -m "Finanzen: Vorschau, Steuer und Abos ohne Karten, Container-Query auf Wrapper" -m "Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>"
Task 5: Rechnungen, Buchungen, Blätterleiste, Kundenreiter
Files:
- Modify:
src/components/finance/InvoiceTable.jsx,TransactionTable.jsx,Pager.jsx,AddressWarning.jsx,CustomerInvoices.jsx,src/styles/global.css(Block 6 und 7 ohne Dialoge)
Interfaces:
-
Consumes:
.fin-body,.fin-table-wrap,.fin-notice,.fin-load-error,.table,.seg,.toolbar,.table-foot,.stats,Stat,noticeProps,isNegText. -
Produces: Signaturen unverändert (
InvoiceTable,TransactionTable,Pager({ page, pages, total, noun, onPage }),AddressWarning({ customer, onShowOverview, variant }),CustomerInvoices({ customer, onShowOverview })) undexport const TRANSACTIONS_ANCHOR = 'finanzen-buchungen'. -
Step 1: CSS Block 6 und 7
-
.stats.fin-ages: Liste ohne Zeichen, Rasterrepeat(4, minmax(0,1fr)), Abstand16px 32px,margin: 0 0 16px;.fin-ages .stat .v18/26 px;@container (max-width: 860px)2 Spalten. -
.fin-block .seg { flex-wrap: wrap; max-width: 100% }, bei ≤ 860 px Knopf-Padding0 9px..fin-pager(mit.table-foot):justify-content: center; gap: 16px,.fin-pager-infotabular. -
Tabellen: Abstands- und Randzellenregeln entfallen (
.tableaus P1 gilt). Es bleiben.fin-inv-customerund.fin-tx-counterpartymitoverflow-wrap: anywhere,.fin-inv-number,.fin-inv-created/-dueund.fin-tx-datenowrap,.fin-inv-statusboxRaster 3 px,.fin-actionsFlexgap: 4px 14px,.fin-tx-remittancemit 2 Zeilen Klemme,.fin-tx-assign .badge { white-space: normal; height: auto; padding-block: 2px }(zwei Klassen schlagen.badge),.fin-tx-actionnowrap rechts. -
@container (max-width: 860px)(Rechnungen) und(max-width: 640px)(Buchungen) bleiben mit den Flächennumber status / customer / created due / total remaining / actionsbzw.date amount / what / assign action, Trennervar(--line),padding: 10px 0. -
.fin-address-warnFlex umbrechend4px 12px;-pagemargin: 0 0 16px,-dialogmargin: 0. -
Step 2:
InvoiceTableundPager -
Aufbau:
<section id={…ANCHOR} className="fin-block">>Card title="Rechnungen"mit schwarzem „Rechnung schreiben“ als Aktion >.fin-body(Altersstreifen,.toolbarmit.seg, Meldung, Inhalt,Pager).InvoiceCreateModalbleibt Geschwister derCard. -
Filter:
.segmitaria-pressedund Klasseon. Meldung übernoticeProps(message.tone, 'fin-notice'). Altersstreifen:Stat label={cell.label} value={cell.sumText} sub={…Rechnung(en)}je<li>. -
Zeilenaktionen: „Zahlungsseite“ und „PDF“ als
<a className="link" target="_blank" rel="noopener noreferrer">mit unverändertemaria-label, „Senden“ und „Erinnerung senden“ als.link-Knopf, „Stornieren“ und „Löschen“ alsButton variant="danger". Sperren, Laufbeschriftungen („Sende …“, „Storniere …“, „Lösche …“) undtitleam gesperrten „Senden“ bleiben. -
Tabelle
<table className="table fin-invoices" role="table">, Rollen,caption.fin-sr,data-labelunverändert.Pager:<nav className="table-foot fin-pager">mit „Zurück“/„Weiter“ alsButton variant="quiet". -
Step 3:
TransactionTable,AddressWarning,CustomerInvoices -
TransactionTable:Card title="Buchungen"in<section id=TRANSACTIONS_ANCHOR className="fin-block">, Inhalt in.fin-body. Ladenloading, Fehlerfin-load-errormitmsg warnund Standardknopf, Stripe-Hinweismsg warnohne Icon. Betragfin-amount fin-tx-amountplusfin-negbeiisNegText,fin-tx-outbleibt. „Zuordnen“/„Ändern“ als.link-Knopf mitaria-label. -
AddressWarning:<div className="msg warn fin-address-warn fin-address-warn-{variant}" role="alert">mit<button type="button" className="link">bzw.Link className="link". Der Knopf trägt immertype="button", weil die Variantedialogin einemDialog as="form"steht. -
CustomerInvoices: Sperrfall alsEmptyStateohneCard, „Daten erfassen“ bleibt schwarz. SonstAddressWarningundInvoiceTable. -
Step 4: Hygiene, Bau, Bild
Run: source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && rg -n "react-icons|style=\{|size=\"sm\"|bodyStyle|FaSpinner" src/components/finance/{InvoiceTable,TransactionTable,Pager,AddressWarning,CustomerInvoices}.jsx; "$W/build.sh" "$WT" p7 && "$W/shots.sh" p7 "$W/shots/p7-t5" --only finanzen,finanzen-tabellen,finanzen-pager,finanzen-anker,finanzen-teilausfall,kunde-invoices,kunde-invoices-adresse --gate p7
Expected: rg ohne Treffer, Exit 0 (kein overflow-x bei 390 px trotz langer Namen), alle Schritte laufen durch. Bild: Altersstreifen 4 Spalten (1440) bzw. 2 (390), Filter als graue Schiene, Tabellen mit Haarlinien und Textaktionen.
- Step 5: Commit
git add src/styles/global.css src/components/finance/InvoiceTable.jsx src/components/finance/TransactionTable.jsx src/components/finance/Pager.jsx src/components/finance/AddressWarning.jsx src/components/finance/CustomerInvoices.jsx
git commit -m "Finanzen: Rechnungen und Buchungen als Tabellen ohne Karten, Textaktionen, Blaetterleiste" -m "Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>"
Task 6: Einstellungen, Bankzugang, Fixkosten, Bankrückkehr, FormField
Files:
- Modify:
src/pages/FinanceSettingsPage.jsx,src/pages/BankCallbackPage.jsx,src/components/finance/SettingsCard.jsx,BankAccessCard.jsx,FixedCostsCard.jsx,FormField.jsx,src/styles/global.css(Region „Finanzen: Einstellungen“)
Interfaces:
-
Consumes:
.field,.hint,.field-row,.input/.select/.textareamit Fehlerring bei[aria-invalid="true"](P1),.kv,.msg,noticeProps. -
Produces:
FormField({ id, label, hint, error, children })unverändert. Das Kind bekommt weiterid,aria-invalid,aria-describedby. -
Step 1: CSS Einstellungen
-
.fin-fieldsetRastergap: 16px, ohne Rand und Innenabstand..fin-form-field { min-width: 0 }..fin-input-amountrechts..fin-counter12 px--text-2rechts,margin-top: -8px,.fin-counter-over--danger(Formularfehler), 600..fin-form-actionsFlex,gap: 12px,margin-top: 16px. -
.fin-fixed-formohne Rahmen, nurpadding-bottom: 24px; margin-bottom: 8px; border-bottom: 1px solid var(--line)..fin-form-title14/20, 600..fin-result16 px, 600. -
.fin-fixed-listund.fin-fixedmit Haarlinienvar(--line)..fin-fixedRasterminmax(0,1fr) auto,padding: 12px 0..fin-fixed-offName und Betrag--text-2. -
Gelöscht:
.fin-form-grid,.fin-bank-facts*,.fin-field-error,.fin-form-hint,.fin-form-notice,.fin-form-field .form-control[aria-invalid]. -
Step 2: Komponenten umbauen
-
FormField:.field fin-form-field, Hinweis<div className="hint">, Fehler<div className="msg err" role="alert">. Den Fehlerring liefert alleinaria-invalid. -
SettingsCard: Feldpaare als.field-row, Felderinput fin-input-amount, Textareatextarea, Ladefehlerfin-load-errormitmsg warn. „Speichern“/„Speichere …“ schwarz ohne Icon. Meldung übernoticeProps(notice.tone). -
BankAccessCard:Cardmit Badge als Aktion. Fakten als flaches<dl className="kv">(<dt>/<dd className="num">ohne<div>-Gruppen). Schwarz ist „Jetzt abrufen“ (beicanRefresh), sonst „Bank verbinden“, der andere Knopf ist Standard. „Rufe ab …“ und „Leite weiter …“ bleiben. -
FixedCostsCard: „Neue Position“ im Kopf als Standardknopf ohne Plus. Formular mit<h3 id="fin-fix-form-title" className="fin-form-title">,.field-row, „Speichern“ schwarz, „Abbrechen“ Standard. Zeilenaktionen: „Bearbeiten“ undrow.toggleLabelals.link, „Löschen“ alsButton variant="danger".aria-label,window.confirmund Sperren bleiben. -
Step 3: Seiten
-
FinanceSettingsPage:PageHeadermit „← Zurück zu den Finanzen“ alsButton as={Link}(Standard, Pfeil ist Zeichen). Inhalt.fin-stackmit den drei Abschnitten. -
BankCallbackPage: ohneCardundstyle. Ergebnis<p className="msg ok fin-result">bzw.msg err fin-resultmit unveränderterrole. Ladenloading(„Bankzugang wird eingerichtet …“). „Zu den Finanzen“ bleibt schwarz. -
Step 4: Hygiene, Bau, Bild
Run: source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && rg -n "react-icons|style=\{|size=\"sm\"|form-control|form-group|FaSpinner" src/pages/FinanceSettingsPage.jsx src/pages/BankCallbackPage.jsx src/components/finance/{SettingsCard,BankAccessCard,FixedCostsCard,FormField}.jsx; "$W/build.sh" "$WT" p7 && "$W/shots.sh" p7 "$W/shots/p7-t6" --only finanzen-einstellungen,finanzen-einstellungen-fehler,finanzen-bank-callback,finanzen-bank-callback-ok --gate p7
Expected: rg ohne Treffer, Exit 0, alle Schritte laufen durch. Bild finanzen-einstellungen-fehler: roter Ring und Fehlertext am Feld, keine Kästen.
- Step 5: Commit
git add src/styles/global.css src/pages/FinanceSettingsPage.jsx src/pages/BankCallbackPage.jsx src/components/finance/SettingsCard.jsx src/components/finance/BankAccessCard.jsx src/components/finance/FixedCostsCard.jsx src/components/finance/FormField.jsx
git commit -m "Finanzen: Einstellungen, Bankzugang, Fixkosten und Bankrueckkehr ohne Karten" -m "Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>"
Task 7: AssignDialog auf Dialog
Files:
- Modify:
src/components/finance/AssignDialog.jsx,src/styles/global.css(Dialog-Regionen für die Zuordnung)
Interfaces:
-
Consumes:
Dialog({ title, onClose, busy, wide, footer, children })(P1),Field,.check,.kv,.msg,useScrollIntoView. -
Produces:
AssignDialog({ transaction, invoices, invoicesLoading, invoicesError, invoicesLoaded, onClose, onDone })unverändert.onClose({ refresh })undonDone(buchung)verhalten sich wie bisher. -
Step 1: CSS Zuordnen
-
.fin-foot: Flex,flex: 1, umbrechend,justify-content: flex-end, Abstand 8 px; bei ≤ 860 px.fin-foot .btn { flex: 1 1 auto }. -
.fin-choices: Raster ohne Rand..fin-choice(mit.check): volle Breite,padding: 8px 0, Haarlinie unten. Mit gewähltem Radio ist.fin-choice-label600 (:has(input:checked))..fin-choice-offopacity: .55; cursor: not-allowed..fin-choice-detail13 px--text-2, mit.warn--orange-fg. -
.fin-confirm600 in--orange-fg, kein Kasten..fin-assigned { margin: 0 }..kv .fin-tx-facts-amount { text-align: left; font-weight: 600 }. -
Gelöscht:
.fin-tx-facts*,.fin-confirm:focus, Kastenregeln von.fin-choice. -
Step 2: Komponente umbauen
-
Eigener Esc-Listener,
useDialogFocus,dialogRef, Overlay-Markup und „ד-Knopf entfallen. Es bleibenlockRef,aliveRef,dirtyRef,useScrollIntoView(errorRef, error)undconfirmRef. -
Gerüst
<Dialog title="Buchung zuordnen" onClose={close} busy={disabled} footer={…}>.close()ist parameterlos (das „ד ruft es mit dem Klick-Event), prüftlockRefund ruftonClose({ refresh: dirtyRef.current }). -
Body: Fakten als flaches
<dl className="kv">, Fehlermsg errmitref={errorRef}, Hinweisemsg muted/msg warnmit unveränderten Rollen. Vorschläge und Kategorien überradio(o)als<label className="check fin-choice…">. „Andere offene Rechnung“, „Fixkosten“ und „Notiz (optional)“ sindFieldmitselect/textarea. Beim Bestätigen steht der Text im.fin-confirm(role="group",tabIndex={-1}, Fokus perconfirmRefwie heute). -
Fuß je Zustand: zugeordnet „Schließen“ und, bei
canUnassign, „Zuordnung lösen“ (beide Standard). Bestätigend „Zurück“ (Standard) und „Ja, als bezahlt markieren“ (schwarz, „Markiere …“). Sonst „Abbrechen“ (Standard) und „Zuordnen“ (schwarz, „Ordne zu …“, gesperrt ohne Auswahl). -
Der Zweischritt bleibt: Bei einer Rechnung geht vor der Bestätigung keine Anfrage raus.
-
Step 3: Hygiene, Bau, Prüfung
Run: source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && rg -n "react-icons|useDialogFocus|keydown|overlay|fin-overlay|fin-close|form-control|form-label" src/components/finance/AssignDialog.jsx; "$W/build.sh" "$WT" p7 && "$W/shots.sh" p7 "$W/shots/p7-t7" --only dlg-zuordnen-zahlung,dlg-zuordnen-bestaetigung,dlg-zuordnen-busy --gate p7
Expected: rg ohne Treffer, Exit 0, alle drei Schritte laufen durch. Bild: weißer Dialog, Vorschläge als Radiozeilen mit Haarlinien, bei 390 px Bottom-Sheet mit umbrechendem Fuß.
- Step 4: Commit
git add src/styles/global.css src/components/finance/AssignDialog.jsx
git commit -m "Finanzen: Buchung zuordnen auf Dialog-Baustein (Esc, Sperre, Vorschlaege als Zeilen)" -m "Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>"
Task 8: InvoiceCreateModal auf Dialog
Files:
- Modify:
src/components/finance/InvoiceCreateModal.jsx,src/styles/global.css(Rest der Dialog-Region)
Interfaces:
-
Consumes:
Dialog,Field,AutocompleteInput(P1, rendert.input),AddressWarning,.fin-foot(Task 7). -
Produces:
InvoiceCreateModal({ open, onClose, onCreated, fixedCustomer, onShowOverview })unverändert, samt VertragonCreated(rechnung, info). -
Step 1: CSS Rechnung schreiben
-
.fin-itemsohne Rand..fin-itemRasterminmax(0,1fr) 80px 120px 110px auto, Abstand8px 12px,align-items: end..fin-item .input { width: 100% }(zwei Klassen schlagen.input),.fin-item .fin-amountrechts..fin-item-label12 px--text-2, ab der zweiten Zeile visuell versteckt..fin-items-footFlexspace-between,margin-top: 8px..fin-totalFlex,gap: 16px, 600. -
.fin-dialog-rowRaster180px minmax(0,1fr), Abstand 12 px. -
Bei ≤ 860 px:
.fin-itemRasterminmax(0,.7fr) minmax(0,1fr) minmax(0,1fr) automit Flächendesc desc desc desc / qty price sum remove, Labels sichtbar..fin-dialog-roweinspaltig. -
.autocomplete .input { width: 100% }legt P6 an (rg -n "\.autocomplete \.input" src/styles/global.css). Fehlt sie, kommt sie hierher (Prüfung indlg-rechnung-kunde). -
Alte Hüllenregeln löschen:
.fin-close*,.fin-overlay,.fin-dialogsamt:focus,.fin-dialog-error,.fin-dialog-warn,.fin-dialog-sub,.fin-dialog-actions,.fin-icon-btn*.
Run: source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && rg -n "fin-(overlay|close|icon-btn)" src; rg -n "fin-dialog" src | rg -v "fin-dialog-row"
Expected: Vor Step 2 trifft es noch InvoiceCreateModal.jsx, nach Step 2 höchstens den KOMPAT-Block.
-
Step 2: Komponente umbauen
-
Eigener Esc-Listener,
useDialogFocus,dialogRef, Overlay und „ד-Knopf entfallen. Es bleibenlockRef,aliveRef,errorRefmituseScrollIntoView. -
Gerüst
<Dialog as="form" wide title="Rechnung schreiben" onClose={close} busy={disabled} onSubmit={(e) => e.preventDefault()} footer={…}>. Das Formular hat keinen Submit-Knopf. Jeder Knopf darin trägttype="button"(Buttonsetzt keinen Standard-Typ): „Abbrechen“, „Als Entwurf speichern“, „Senden“, „+ Position“, „Entfernen“ und der Knopf inAddressWarning. Deshalb sendet Enter nie.close()prüftlockRefund ruftonClose(). -
Body: zuerst Fehler
msg errmitref={errorRef}. Kunde alsFieldmithtmlFor="fin-invoice-customer"(fester Kunde oderAutocompleteInput), Hinweisehint/msg warn,AddressWarning variant="dialog"mit dem bisherigenonShowOverview-Handler. -
Positionen als
<fieldset className="fin-items">mit.fin-item-Zeilen (input,fin-amount,aria-labels unverändert). „Entfernen“ ersetzt den Papierkorb als<button type="button" className="link">mitaria-label="Position n entfernen", gesperrt bei nur einer Position. „+ Position“ ist ein Standardknopf. Zahlungsziel und Notiz alsFieldin.fin-dialog-row. -
Fuß
<div className="fin-foot">: „Abbrechen“ und „Als Entwurf speichern“ (Standard), „Senden“ (schwarz,disabledundtitlewie heute). „Speichere …“ und „Sende …“ bleiben. -
Esc: Ein offenes Vorschlagslisten-Popover ruft
preventDefault, dann schließtDialognicht. Ohne Liste schließt Esc überclose. -
Step 3: Hygiene, Bau, Prüfung
Run: source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && rg -n "react-icons|useDialogFocus|keydown|overlay|form-control|form-group|form-label|FaTrash|FaPlus" src/components/finance/InvoiceCreateModal.jsx; "$W/build.sh" "$WT" p7 && "$W/shots.sh" p7 "$W/shots/p7-t8" --only dlg-rechnung,dlg-rechnung-kunde,dlg-rechnung-enter,kunde-invoices-adresse --gate p7
Expected: rg ohne Treffer, Exit 0, alle Schritte laufen durch (Overlay in Fenstergröße, Esc-Reihenfolge, Enter ohne Request, Feldbreite). Bild bei 390 px: Positionszeilen zweizeilig, Fuß mit umbrechenden Knöpfen, Vorschlagsliste sichtbar.
- Step 4: Commit
git add src/styles/global.css src/components/finance/InvoiceCreateModal.jsx
git commit -m "Finanzen: Rechnung schreiben auf Dialog-Baustein (breit, Enter sendet nie, Esc schliesst Liste zuerst)" -m "Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>"
Task 9: Gate P7, Review, Tag
- Step 1: Rebase
Run: source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && git fetch origin && git rebase origin/test && "$W/check.sh"
Expected: Exit 0, # pass = N + 2 und # fail 0. Die Größen von JS und CSS aus dieser Ausgabe gehen in Step 3.
- Step 2: Vollständiger Gate-Lauf, Hygiene und tote Klassen
Run: source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && "$W/build.sh" "$WT" p7 && "$W/shots.sh" p7 "$W/shots/p7" --gate p7
Expected: Exit 0. Alle Prüfungen aus P1 sind für alle Ansichten leer, icon-svg für alle FIN-Ansichten und die der früheren Pakete.
Run:
source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh || exit 1
F="src/components/finance src/pages/FinancePage.jsx src/pages/FinanceSettingsPage.jsx src/pages/BankCallbackPage.jsx"
finblock() { sed -n '/^\/\* ===== FINANZEN (P7)/,/^\/\* ===== ENDE FINANZEN/p' src/styles/global.css; }
rg -n "react-icons|@tabler|style=\{|size=\"sm\"|ui-btn|form-control|form-group|form-label|overlay-content|overlay-close|spinner|Kpi|kpi|#[0-9a-fA-F]{3,8}\b|rgba?\(" $F
finblock | rg -n "#[0-9a-fA-F]{3,8}\b|rgba?\(|!important|var\(--(surface|border|text-muted|text-faint|r-|shadow-[123]|warn|info|ok)\b"
finblock | rg -o "\.fin-[a-z0-9-]+" | sort -u | sed 's/^\.//' | while read c; do rg -q "(^|[^A-Za-z0-9_-])${c}([^A-Za-z0-9_-]|$)" src --glob '!*.css' || echo "TOT $c"; done
finblock | wc -l; finblock | rg -c "container-type"
git diff --name-only design-p6..HEAD
Expected:
-
Die ersten beiden Befehle geben nichts aus.
-
Der dritte nennt nur
fin-address-warn-dialogundfin-address-warn-page(per Template-Präfix gebaut, erlaubt). -
Der Finanzblock hat 250 bis 360 Zeilen,
container-typesteht genau einmal da (.fin-body). -
Der letzte Befehl (
git diff --name-only design-p6..HEAD) nennt nur die Dateien aus „Global Constraints / Dateien“ undtests/unit/financeUi.test.mjs. -
Step 3: Bundle-Größe
Die JS- und CSS-Größe aus der check.sh-Ausgabe von Step 1 wird mit Task 1 Step 1 verglichen. Keiner der beiden Werte ist größer.
- Step 4: Code-Review (sonnet)
Geprüft wird der Diff design-p6..HEAD gegen die Spec-Abschnitte „Gestaltungsregeln“, „Bausteine“ und Paket P7. Prüfaufträge:
-
Verhalten gleich geblieben: Sperren,
window.confirm, Zweischritt inAssignDialog, Rollen unddata-labelder Tabellen. -
busyundclose()sind in beiden Dialogen richtig angeschlossen, kein Dialog sitzt in.fin-body. -
Jede Klasse
fin-*ist definiert oder bewusst entfallen, Rot nur bei Minus, Texte nur wie in „Global Constraints“ geändert. Wichtige Befunde werden behoben, Kleinigkeiten notiert. Nach Korrekturen kommen Step 1 und Step 2 noch einmal, damit die Bilder den Stand zeigen, der getaggt wird. -
Step 5: Tag und Übergabe
Run: source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && "$W/check.sh" && git tag design-p7 && git log --oneline origin/test..design-p7 && git rev-parse design-p7
Der Eigentümer bekommt:
-
die Screenshots aus
shots/p7/1440x900undshots/p7/390x844(Auswahl:finanzen,finanzen-minus,finanzen-teilausfall,dlg-rechnung-kunde,dlg-zuordnen-bestaetigung,finanzen-einstellungen-fehler,kunde-invoices-adresse) -
den Hinweis, dass negative Beträge jetzt rot sind und beide Dialoge neu auch mit Esc schließen
-
die Push-Zeile
git -C /home/webklar/apps/ticket.webklar.com/dev-design push origin 'design-p7^{commit}:refs/heads/test' -
die Rücknahme (nur solange P7 das jüngste Paket ist):
git revert --no-commit design-p6..design-p7, Commit und Push durch den Eigentümer -
den Hinweis für P10: Die KOMPAT-Zeilen zu
.fin-linkund.fin-dialog(760 px) sind tot und fallen mit dem Block weg. -
Step 6: Nach dem Push (erst, wenn der Eigentümer gepusht hat)
-
Im Log von
webklar-ticket-buildsteht „Deployment completed successfully“. -
curl -s https://ticket.webklar.com/ | grep -o 'assets/index-[^"]*\.js'entsprichtdist/index.htmldes Tags. -
Ein lesender Sichttest im Chrome des Eigentümers: Finanzen mit Statuszeile und Rechnungsliste, Scroll von „Zu den Rechnungen“. Keinen Dialog öffnen, der etwas sendet.