Skip to content

feat(app): visual design from prototype "RequestFlow A" - #53

Merged
Fluory merged 13 commits into
mainfrom
claude/feat-ui-design-52
Sep 24, 2026
Merged

Fluory merged 13 commits into
mainfrom
claude/feat-ui-design-52

Conversation

@Fluory

@Fluory Fluory commented Sep 23, 2026 •

Copy link
Copy Markdown
Owner

Warum

Fixes #52 · Auftrag des Orchestrators: erst „Mach schön“, dann Umsetzung des Claude-Design-Prototyps „RequestFlow A“ (gebündelte HTML-Datei, Markup + Logik extrahiert). Basis main.

Arbeitsstand

  • Ziel: Die Oberfläche entspricht dem Prototyp „RequestFlow A“ – ohne Änderung an Abläufen, Rechten oder den Texten, auf die sich Tests stützen.
  • Nicht-Ziele: Dark Mode, Komponentenbibliothek, Demo-Schalter des Prototyps (Rolle/Startseite), „Link als eingeladene Person öffnen“, Firmen-/Rollen-Vorschau auf der Registrierungsseite (bräuchte eine neue öffentliche Einladungsabfrage).
  • Erledigt: Palette, Typografie (IBM Plex, selbst gehostet), Kopfzeile mit Firma, Name und Rolle, Startseite mit offener Arbeit, Anfragenliste, zweispaltige Prüfansicht (Angaben, Positionen, Dokumente | Fundstelle mit Korrektur der Positionen und Entscheidung), Benutzer, Einladung mit Kopieren; frischer Review eingearbeitet; verify + beide E2E-Smokes grün; Screenshot-Nachweis.
  • Offen: menschliche Freigabe (neue Abhängigkeit, identity-Modul berührt), dann Review und Merge durch den Orchestrator.
  • Annahmen: Korrektur der Kopffelder bleibt zusätzlich in der Tabellenzeile (die E2E-Specs hängen daran); der Prototyp hat sie nur in der Seitenleiste.
  • Nächster kleinster Schritt: Freigabe/Review durch den Orchestrator.

Was ist passiert (Klartext)

Die Oberfläche sieht jetzt aus wie der Design-Prototyp „RequestFlow A“: warme, ruhige Grautöne, ein Blau als Akzent, die Schrift IBM Plex (liegt auf unserem Server, der Browser lädt nichts von Google). Oben stehen Firma, Name und Rolle. Die Startseite zeigt, wie viele Anfragen auf Prüfung warten oder einen Fehler haben. In der Prüfansicht stehen links die erkannten Angaben, Positionen und Dokumente; rechts bleibt beim Scrollen ein Feld mit dem ausgewählten Wert, seiner Fundstelle im Dokument und den Knöpfen „Freigeben“ und „Ablehnen“ stehen. Unsichere Werte sind mit ⚠ markiert. Nach dem Hochladen führt ein Link direkt zur neuen Anfrage, Einladungslinks lassen sich kopieren. Abläufe, Berechtigungen und alle Texte, auf die sich die automatischen Tests stützen, sind unverändert; beide Browser-Tests laufen ohne Anpassung grün.

Plan-Pflicht (SYSTEM.md §4)

  • Kein Auslöser – keine Modulgrenze, öffentliche API, Migration, Auth/Rechte, kein Zahlungs-/Daten-/Infrapfad, höchstens zwei Module, keine Architekturvarianten, umkehrbar
  • Auslöser zutreffend – Impact Manifest ausgefüllt (Plan vor Code)

Impact Manifest

  • Betroffene Module: app (Seiten, Layout, _components, _server/runtime.ts, Stylesheets) und identity (Actor.name, nur Anzeige).
  • Schnittstellen / Datenänderungen: Actor bekommt das optionale Feld name (aus session.user.name, keine zusätzliche Abfrage) – nie für Autorisierung, nie geloggt. Keine Migration, keine API-Änderung.
  • Neue Abhängigkeiten: @fontsource/ibm-plex-sans und @fontsource/ibm-plex-mono 5.3.0, Lizenz OFL-1.1 – der Prototyp ist auf IBM Plex gebaut; selbst gehostet (nur die lateinischen Schnitte 400/500/600/700 bzw. 400/500), daher keine Drittanfragen aus dem Browser. Bewusst nicht next/font/google (Download beim Build über den Proxy) und kein Google-Fonts-Link (Drittanfrage).
  • Akzeptanzkriterien: feat(app): consistent visual design for the pilot UI #52; Kriterium 2 („keine neue Abhängigkeit, Systemschriften“) durch den Prototyp ersetzt – dokumentiert auf feat(app): consistent visual design for the pilot UI #52.
  • Testplan: Styling bekommt einen Prüfnachweis (Screenshots, Tastatur, Kontrast, Überlaufmessung), kein künstlicher Test; beide Playwright-Smokes unverändert grün; pnpm verify.
  • Verifizierte Fakten: alle Test-IDs, Rollen, Labels, Hidden Inputs und Server Actions unverändert (Review); requestActor() = currentActor(headers()) mit React cache pro Request; Server Actions prüfen weiterhin selbst.
  • Offene Annahmen: –
  • Nicht-Ziele: siehe oben.
  • Risiken und Rollback: Darstellung plus ein Anzeige-Feld; /login und /signup rendern wegen der Kopfzeile im Root-Layout jetzt dynamisch (vorher statisch) – unkritisch; Rollback per Revert.

Geändert

  • Stylesheets: src/app/globals.css (Tokens, Basis, Kopfzeile, Flächen, Meldungen, Formulare), src/app/components.css (Status, Tabellen, Liste, Benutzer, Einladung, responsive), neu src/app/review.css (Prüfansicht)
  • src/app/layout.tsx (Schriften, Stylesheets), _components/{app-header,nav-link,auth-form}.tsx, neu _components/copy-button.tsx, _server/runtime.ts (requestActor)
  • Seiten: page.tsx, login, signup, invite, users, requests (+ upload-form.tsx, status-pill.tsx), requests/[id] (+ review-parts.tsx)
  • src/features/identity/{authorize,actor}.ts (Actor.name)
  • package.json, pnpm-lock.yaml (@fontsource/*), CHANGELOG.md

Nachweis (SYSTEM.md §11)

  • verify:changed: typecheck + lint grün
  • verify: grün (lokal, Node 24, PostgreSQL 17 + SeaweedFS): lint, typecheck, Unit 154/154, Integration 114/114, depcruise ohne Verstöße (148 Module), Build, pnpm audit --audit-level high (1 moderate wie auf main)
  • verify:full / E2E-Spec: beide Smokes grün und unverändert (review-smoke, line-items-smoke); KI-Dienst unberührt, Eval-Gate nicht betroffen
  • Manueller Prüfnachweis: Screenshot-Tour (Login, Start, Anfragenliste mit allen Status, Prüfansicht, Fundstelle, Export, Duplikat, Benutzer, Einladen, Handy 390 px) an den Orchestrator geschickt; seitlicher Überlauf bei 390 px auf Liste, Prüfseite und Benutzerseite gemessen: 0 px; Tastatur: alle Aktionen native Links/Buttons/Formulare, Fokusrahmen 3 px, scroll-padding-top gegen Verdecken durch die Kopfzeile, auf dem Handy springt die Auswahl zur Fundstelle; Kontrast nachgerechnet: alle Textpaare ≥ 4.73:1 (niedrigstes --muted auf der Auswahlfarbe), Eingaberahmen 3.41:1 (dunkler als im Prototyp, WCAG 1.4.11); leere Zustände und Fehlerzustände gestaltet
  • Frischer Review: erledigt (0 Blocker · 3 should-fix · 5 nits, alle eingearbeitet oder begründet) – siehe PR-Kommentar; verlangt menschliche Freigabe (identity + Abhängigkeit)

Doku-Entscheidung (genau eine)

  • Keine langlebige Doku betroffen – Begründung: –
  • Doku betroffen und im selben PR aktualisiert:
    • Produktdoku (P0: README): –
    • Technische Doku: –
    • Architekturkarte: –
    • ADR: –
    • CHANGELOG [Unreleased] (sichtbares Feature oder Verhalten – im selben PR, nie „später")

Entferntes oder Umbenanntes: StatusBadge/Source/Dokumentliste aus requests/[id]/page.tsx nach review-parts.tsx verschoben; Spalte „Hinweis“ der Anfragenliste heißt wie im Prototyp „Duplikat“

Dateigrößen und neue Bausteine (SYSTEM.md §7)

Dateien über 500 Zeilen im Diff (Ausnahmen: generierter Code, Lockfiles, Fixtures, Migrationen, Schemas, Ressourcen, Doku, Konfiguration):

  • keine
  • bewusst belassen – Begründung: –
  • im selben PR nach fachlicher Verantwortung geteilt
  • Folge-Issue –

Über 800 Zeilen mit neuer Fachlogik oder über 1000 Zeilen (P1/P2): nicht betroffen

Neue Shared-Komponente, Utility-Datei, Adapter oder fachlicher Service:

  • nein
  • ja – gesucht nach: vorhandenen App-Komponenten (src/app/_components/auth-form.tsx), Status-Darstellung (StatusBadge, status-labels.ts), Sitzungszugriff (currentActor in _server/runtime.ts), Zwischenablage (nichts vorhanden); gefunden: SignOutButton, requestStatusLabel, currentActor – wiederverwendet; requestActor ist nur die gecachte Hülle um currentActor; CopyButton neu (kleinste Client-Komponente)

Subagent-Einsätze

  • Frischer Review (zweimal, read-only): Befunde und Auflösung jeweils als PR-Kommentar.

Risiken / offene Punkte

  • Menschliche Freigabe nötig: neue Abhängigkeit (@fontsource/*) und identity berührt.
  • Auf dem Handy sind in breiten Tabellen zuerst nur die ersten Spalten sichtbar (seitlich wischen).
  • Die Beschriftung des Datei-Knopfs („Choose Files“) kommt vom Browser und folgt dessen Sprache.

🤖 Generated with Claude Code

https://claude.ai/code/session_01DJ5vaKvTYiMvdngT4d3xo1

…ages (#52)

Shared header with navigation, company and role; request and field status as
pills with text; primary/danger actions; tables in scroll containers. Review
page parts moved to review-parts.tsx. Error messages from the login and upload
forms use role=alert instead of role=status. No flow, permission or test change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DJ5vaKvTYiMvdngT4d3xo1
… subject column and duplicate reason (#52)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DJ5vaKvTYiMvdngT4d3xo1
…er falls back to the brand on errors (#52 review)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DJ5vaKvTYiMvdngT4d3xo1
…s in components.css (#52 review)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DJ5vaKvTYiMvdngT4d3xo1
…ompany and role visible on phones, AA field borders, export retry as warning (#52 review)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DJ5vaKvTYiMvdngT4d3xo1

Fluory commented Sep 23, 2026

Copy link
Copy Markdown
Owner Author

Frischer Review (unabhängiger Subagent, read-only, review-pr + Frontend-/Security-Regeln)

Ergebnis: 0 Blocker · 5 should-fix · 3 nits. Bestätigt: Prüfseite Zeile für Zeile gegen main verglichen – alle data-testids, Hidden Inputs, Feldnamen, Bedingungen, Server Actions, aria-labels und Rollen unverändert; „Rolle: Sachbearbeitung" genau einmal gerendert; Admin-Link nur für Admins (Server erzwingt weiterhin); role="alert" nur bei Fehlern, keine neue Nutzer-Enumeration; keine neue Abhängigkeit, keine externen Requests. Kontrast aller Textpaare ≥ 4.5:1 (niedrigstes: --text-muted auf --neutral-bg 5.20).

# Schwere Befund Auflösung
1 should-fix Nachweis fehlt (verify, E2E) pnpm verify grün, beide Smokes grün, Screenshot-Tour – siehe PR-Beschreibung
2 should-fix Firma/Rolle auf dem Handy per display:none versteckt bleibt sichtbar: Konto-Block bekommt unter 640 px eine eigene Zeile
3 should-fix Fixierte Kopfzeile kann den Fokus verdecken (WCAG 2.4.11) html { scroll-padding-top: 4.5rem }
4 should-fix globals.css 824 Zeilen, aber „keine" angekreuzt geteilt: globals.css (Tokens, Basis, Kopfzeile, Flächen, Hinweise, 352 Zeilen) + components.css (Formulare, Tabellen, Status, Prüfansicht, 485 Zeilen)
5 should-fix Layout und Seite lesen die Sitzung doppelt; DB-Ausfall bricht auch die Login-Seite requestActor() = cache(currentActor(headers())) pro Request, genutzt von Kopfzeile und Seiten; Kopfzeile fällt bei Fehlern auf die Marke zurück (Next-Steuerfehler per unstable_rethrow weitergereicht), Fehler wird mit Code geloggt
6 nit „Export wird wiederholt" grün wie ein Erfolg; Upload-Fehler nicht volle Breite Warnhinweis (callout-warn, role="status" bleibt); .upload [role="alert"] ergänzt
7 nit Eingabefeld-Rahmen 1.67:1 (< 3:1, WCAG 1.4.11) neues Token --border-input: #8a939f (3.2:1)
8 nit Kontrast-Kommentare ungenau korrigiert (6.0:1, 6.8:1); „⚠" in der Duplikat-Überschrift bleibt (kein Test hängt daran)

Danach: pnpm verify grün (Unit 154/154, Integration 114/114, depcruise, Build, Audit ohne high), beide E2E-Smokes grün, Überlauf bei 390 px auf Liste, Prüfseite und Benutzerseite jeweils 0 px.


Generated by Claude Code

@Fluory
Fluory marked this pull request as ready for review September 23, 2026 16:53
…primary actions, flat cards, status tags only where attention is needed (#52)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DJ5vaKvTYiMvdngT4d3xo1
… borders on status tags (#52)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DJ5vaKvTYiMvdngT4d3xo1
….1) for the design prototype (#52)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DJ5vaKvTYiMvdngT4d3xo1
…lex, app header with name and role, two-column review with sticky source panel and decision (#52)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DJ5vaKvTYiMvdngT4d3xo1
…a-current on the selected field, password hint via aria-describedby, default selection includes positions, shared session lookup on the invite page (#52 review)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DJ5vaKvTYiMvdngT4d3xo1

Fluory commented Sep 24, 2026

Copy link
Copy Markdown
Owner Author

Frischer Review nach Umsetzung des Prototyps „RequestFlow A“ (unabhängiger Subagent, read-only)

Ergebnis: 0 Blocker · 3 should-fix · 5 nits. Braucht menschliche Freigabe: Der PR berührt src/features/identity (Security-Regel) und bringt eine neue Abhängigkeit.

Bestätigt:

  • Unverändertes Verhalten gegenüber main: alle Test-IDs, Hidden Inputs, Feldnamen, Server Actions und Bedingungen. Beide E2E-Specs halten: genau ein mark, ein role="status" je Schritt, und das Speichern der Seitenleiste kommt als letztes. „Rolle: Sachbearbeitung“ steht genau einmal auf der Seite.
  • Sicherheit: Actor.name dient nur der Anzeige, landet nie in Logs oder Audit und wird nie zur Autorisierung genutzt. Die Zählung auf der Startseite läuft mandantengebunden in withTenant. AppHeader schluckt nur Fehler, die nicht aus Next stammen. Der Upload-Link nutzt die requestId vom Server.
  • Kontrast: alle Textpaare ≥ 4.73:1, Eingaberahmen 3.41:1.
# Schwere Befund Auflösung
1 should-fix Plan-Pflicht falsch: identity (öffentlicher Actor-Typ) ist betroffen Beschreibung: „Auslöser zutreffend“, Impact Manifest mit identity (Feld nur zur Anzeige), Bitte um Freigabe
2 should-fix PR-Beschreibung/#52 veraltet (Schriften, Palette, Dateiliste, Kontrastminimum, Abhängigkeit nur im Commit begründet) Beschreibung neu; Kontrastminimum korrigiert auf 4.73:1; Abhängigkeit mit Version, Lizenz und Grund; #52 per Kommentar aktualisiert (Kriterium 2 ersetzt)
3 should-fix Handy: Klick auf einen Wert lädt neu, die Seitenleiste liegt aber unter den Tabellen – keine sichtbare Änderung Links springen jetzt zu #selected-heading
4 nit Passwort-Hinweis im Label verlängert den Namen des Felds Hinweis per aria-describedby
5 nit Ausgewähltes Kopffeld nur per Hintergrundfarbe markiert aria-current auch am Wert-Link
6 nit Verhaltensabweichungen: Export-Hinweis ohne role="status", „erkannt: Status“ nur in der Seitenleiste, Standardauswahl ignoriert Positionen Standardauswahl berücksichtigt jetzt Positionen (Kommentar präzisiert); „erkannt: Status“ wieder in der Tabelle; Export-Hinweis bleibt bewusst ohne zweite Statusrolle (strikte getByRole("status")-Locator, Inhalt ist statisch)
7 nit Einladungsseite liest die Sitzung doppelt; Import-Reihenfolge Seite nutzt requestActor(), die Server Action prüft weiter selbst; Importe sortiert
8 nit Stylesheet-Kopf behauptet ≥ 3:1 auch für Knopfrahmen präzisiert (Knopfrahmen dekorativ, Text identifiziert)

Danach:

  • pnpm verify grün: Unit 154/154, Integration 114/114, depcruise, Build, Audit ohne high.
  • Beide E2E-Smokes grün, ohne Änderung an den Specs.
  • Überlauf bei 390 px auf Liste, Prüfseite und Benutzerseite: jeweils 0 px.

Generated by Claude Code

@Fluory Fluory changed the title feat(app): consistent visual design for the pilot UI feat(app): visual design from prototype "RequestFlow A" Sep 24, 2026
@Fluory
Fluory merged commit 85c180c into main Sep 24, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(app): consistent visual design for the pilot UI

2 participants