docs(15): UI design contract
Tessera CI/CD / Lint & Type Check (push) Successful in 43s
Tessera CI/CD / Tests (push) Successful in 45s
Tessera CI/CD / Build & Publish Images (push) Successful in 7s

This commit is contained in:
2026-08-04 13:58:33 +02:00
parent 37ab7c537d
commit 4f78999238
@@ -193,7 +193,9 @@ Ebenfalls keine neue UI-Komponente: `DashboardService.getWidgets()` filtert serv
## UI Considerations ## UI Considerations
Applicable state considerations resolved: 9 covered, 3 backstop, 0 unresolved Applicable state considerations resolved: 17 covered, 5 backstop, 0 unresolved
> Quelle: `ui-consideration-probe.cjs` über sieben Surfaces (E1 `/admin/groups`, E2 Permission-Matrix, E3 User-Detail-Modal, E4 403-Seite, E5 Marketplace-Karte, E6 Aktivierungs-Dialog, E7 Sidebar + Dashboard-Grid). Der Prosa-Klassifikator ordnete E5 zunächst als `unclassified` ein; nach manueller Einordnung als `list-collection` + `interactive-control` + `static-content` wurden dessen acht Kategorien nachgezogen (Propose-then-confirm, ui-phase §9.5).
| Category | Element(s) | Status | Resolution / Reason | | Category | Element(s) | Status | Resolution / Reason |
|----------|------------|--------|---------------------| |----------|------------|--------|---------------------|
@@ -209,6 +211,15 @@ Applicable state considerations resolved: 9 covered, 3 backstop, 0 unresolved
| zero-one-many | AD-Bindung: Gruppe ohne/mit gebundener AD-Gruppe | ✅ covered | Zwei explizite Zustände dokumentiert (ungebunden zeigt Auswahl-UI, gebunden zeigt Chip + Entfernen-Link) | | zero-one-many | AD-Bindung: Gruppe ohne/mit gebundener AD-Gruppe | ✅ covered | Zwei explizite Zustände dokumentiert (ungebunden zeigt Auswahl-UI, gebunden zeigt Chip + Entfernen-Link) |
| zero-one-many | Standardgruppe: keine/eine markierte Gruppe pro Mandant | ✅ covered | D-13 erzwingt DB-seitig genau 0 oder 1 — UI zeigt Stern-Toggle je Zeile, "Sofort freigeben" wird `disabled`, wenn keine Standardgruppe existiert (Surface Contract 6) | | zero-one-many | Standardgruppe: keine/eine markierte Gruppe pro Mandant | ✅ covered | D-13 erzwingt DB-seitig genau 0 oder 1 — UI zeigt Stern-Toggle je Zeile, "Sofort freigeben" wird `disabled`, wenn keine Standardgruppe existiert (Surface Contract 6) |
| long-text | Modulnamen in der Matrix-ersten-Spalte | ✅ covered | `sticky left-0`-Spalte erhält feste Mindestbreite + `truncate` mit `title`-Attribut, identisch zur Overflow-Behandlung in Zeile darüber | | long-text | Modulnamen in der Matrix-ersten-Spalte | ✅ covered | `sticky left-0`-Spalte erhält feste Mindestbreite + `truncate` mit `title`-Attribut, identisch zur Overflow-Behandlung in Zeile darüber |
| partial | Matrix-Zelle (E2) und „Direkt"-Checkbox im User-Detail (E3), wenn der PATCH nach dem optimistischen Toggle fehlschlägt | ✅ covered | Die Checkbox springt auf den Serverzustand zurück, zusätzlich zur dokumentierten Fehlermeldung. Es darf keinen Zustand geben, in dem die Oberfläche eine Freigabe anzeigt, die in der Datenbank nicht existiert — das bestehende `toggleModule`-Muster in `AdminModulesPage` setzt beim Fehlerfall ebenfalls zurück |
| partial | Gruppenliste (E1): Mitgliederzahl je Zeile | ✅ covered | Kein Teilzustand möglich — die Zahl kommt im selben Response wie die Gruppenzeile; es gibt keinen Nachlade-Pfad, der eine Zeile ohne Zahl rendern könnte |
| partial | AD-Gruppensuche im Create/Rename-Modal (E1), wenn das LDAP nur einen Teil der Treffer liefert oder der Bind fehlschlägt | 🧪 backstop | Kein dokumentiertes Verhalten für Teilergebnisse der Gruppensuche. Verifikation prüft, dass die Ergebnisliste bei fehlgeschlagener oder unvollständiger Suche nicht stillschweigend leer erscheint, sondern den bestehenden Fehler-Div-Pfad aus `admin/ldap/page.tsx` nutzt |
| partial | Sidebar und Dashboard-Grid (E7) | ✅ covered | Kein Teilzustand — beide erhalten vom Server die fertig gefilterte Liste; ein teilweise gefiltertes Ergebnis kann strukturell nicht entstehen |
| empty · loading · error · populated · zero-one-many | Marketplace-Katalog (E5) in seinen Bestandszuständen | ✅ covered | Unverändert gegenüber dem ausgelieferten Marketplace — diese Phase ergänzt ausschließlich das Badge und den nicht-klickbaren Zustand einer einzelnen Karte, sie fasst weder Katalog-Laden, Katalog-Fehler noch Leerzustand an |
| long-text · overflow | Marketplace-Karte (E5) mit langem Modulnamen plus zusätzlichem „Kein Zugriff"-Badge | 🧪 backstop | Die Badge-Reihe (`flex items-center gap-2`) wird durch das zweite Badge breiter; `h3.truncate` deckt den Titel ab, aber nicht die Badge-Reihe. Verifikation prüft, dass Status-Badge und „Kein Zugriff"-Badge bei schmaler Karte umbrechen statt aus der Karte zu laufen |
| partial | Marketplace-Karte (E5): Karte gerendert, bevor der Freigabe-Status bekannt ist | 🧪 backstop | Nicht dokumentiert, ob Aktivierungs- und Freigabestatus im selben Response kommen. Verifikation prüft, dass keine Karte kurzzeitig ohne „Kein Zugriff"-Badge klickbar erscheint und dann nachträglich sperrt — im Zweifel Badge-Status serverseitig mitliefern |
| empty · loading · error | 403-Seite (E4) und Aktivierungs-Dialog (E6) | ✅ covered | Beide sind reine Zustandsanzeigen ohne eigenen Datenabruf: die 403-Seite rendert serverseitig fertig, der Dialog arbeitet auf bereits geladenen Moduldaten. Fehler beim Aktivieren laufen über den bestehenden Fehler-Div-Pfad von `AdminModulesPage` |
| long-text | Modulname im Aktivierungs-Dialog (E6) | 🧪 backstop | Der interpolierte Modulname steht in Titel und Fließtext des `max-w-sm`-Dialogs; kein Truncate dokumentiert. Verifikation prüft, dass ein langer Name den Dialog umbricht statt ihn zu verbreitern |
--- ---