Files

17 KiB

phase, plan, subsystem, tags, requires, provides, affects, actuals, tech-stack, key-files, key-decisions, requirements-completed, coverage, duration, completed, status
phase plan subsystem tags requires provides affects actuals tech-stack key-files key-decisions requirements-completed coverage duration completed status
15-modul-berechtigungen-gruppen-user-grants 06 ui
nextjs
next-intl
react
admin
groups
ldap
phase plan provides
15-modul-berechtigungen-gruppen-user-grants 02 GroupsService/GroupsController — vollständiges CRUD für Gruppen und Mitgliedschaften, GroupsService.getImpact als Zahlenquelle für den Löschdialog (D-17)
/admin/groups Seite: Tabelle (Name/AD-Bindung/Standardgruppe/Mitglieder/Aktionen), Anlege/Umbenennen-Modal mit AD-Radio-Auswahl aus GET /ldap/groups (D-05, D-18), Mitglieder-Modal mit LDAP-Herkunfts-Sperre (D-19), Löschdialog mit konkreten Zahlen (D-17)
Sechster Admin-Navigationseintrag 'Gruppen' mit eigenem Roster-Icon
Sämtliche i18n-Schlüssel der gesamten Phase 15 in de.json/en.json — admin.groups.*, admin.users.grants.*, adminModules.grantsLink/grants.*/activationDialog.*, modules.accessDenied.*, marketplace.statusNoAccess/toastNoAccess, header.admin.groups
15-07
15-08
tokens tasks commits
14500 3 3
added patterns
AD-Bindungs-Auswahl per Radio-Liste (nicht Checkbox) aus derselben GET /ldap/groups-Discovery, die admin/ldap/page.tsx für den Import-Filter nutzt — Markup 1:1 übernommen, nur input type gewechselt
Sichtbare Fehlerbehandlung für Lösch- und AD-Discovery-Fehlschläge (border-destructive/50 bg-destructive/10), bewusster Bruch mit dem sonst projektweiten silent-fail-Muster der Admin-Lösch-Dialoge — dokumentiert als Code-Kommentar an DeleteGroupDialog
ICU-select-Syntax in next-intl-Werten ({granted, select, true {freigeben} other {entziehen}}) für Checkbox-aria-labels, die je nach Zustand zwei Verbformen brauchen, ohne einen zweiten i18n-Schlüssel anzulegen
created modified
apps/web/src/app/(portal)/admin/groups/page.tsx
apps/web/src/app/(portal)/admin/groups/components/GroupFormModal.tsx
apps/web/src/app/(portal)/admin/groups/components/GroupMembersModal.tsx
apps/web/src/app/(portal)/admin/groups/components/DeleteGroupDialog.tsx
apps/web/src/app/(portal)/admin/groups/groups-page.test.tsx
apps/web/src/messages/de.json
apps/web/src/messages/en.json
apps/web/src/components/admin/admin-sidebar.tsx
Task 2/3-Split von page.tsx: Task 2 committet die Seite mit Create/Edit-Modal verdrahtet, aber Mitglieder-/Lösch-State bereits deklariert und an die Aktionsbuttons gehängt, ohne die (noch nicht existierenden) Task-3-Komponenten zu importieren — hält jeden Task-Commit für sich buildbar, statt beide Dateien in einem Commit zu bündeln
Erstellen einer Gruppe MIT sofortiger AD-Bindung läuft zweistufig (POST /groups, dann PATCH /groups/:id {ldapDn}), weil CreateGroupDto ausschliesslich name entgegennimmt (15-02) — keine Schema-/DTO-Änderung nötig, nur Client-seitige Sequenzierung
matrixCheckboxLabel/directCheckboxLabel (Wave-4-Schlüssel für 15-07/15-08) als next-intl-ICU-select mit zusätzlichem granted-Parameter modelliert, um freigeben/entziehen aus einem einzigen Schlüssel zu bedienen, statt zwei separate Schlüssel anzulegen
PERM-01
id description requirement verification human_judgment
D1 /admin/groups Tabelle mit Name/AD-Bindung/Standardgruppe/Mitglieder/Aktionen, leerer Zustand, optimistischer Standardgruppen-Stern-Toggle mit Rollback bei Fehler PERM-01
kind ref status
unit apps/web/src/app/(portal)/admin/groups/groups-page.test.tsx (describe 'AdminGroupsPage': empty state, populated table, star-toggle PATCH + rollback — 4 Tests) pass
kind ref status
other grep-Nachweise aus Task 2 acceptance_criteria (font-weight-Vokabular, sechs Sidebar-Einträge) + pnpm --filter @tessera/web run build pass
false
id description requirement verification human_judgment rationale
D2 GroupFormModal: Anlegen/Umbenennen mit AD-Bindung per Radio-Auswahl aus GET /ldap/groups (D-05/D-18), sichtbarer Fehler-/Leerfall bei der AD-Suche statt stillem Leerbild PERM-01
kind ref status
other grep-Nachweise aus Task 2 acceptance_criteria: type="radio" vorhanden, type="checkbox" abwesend, ldap/groups referenziert, discoverError/noResults sichtbar ausgeführt pass
kind ref status
other pnpm --filter @tessera/web run build (Typecheck inkl. GroupFormModal) + pnpm --filter @tessera/web test (voller Lauf, 162/162 grün) pass
true Kein interaktiver Komponententest öffnet das Modal und klickt einen Radio-Eintrag — die Task-2-acceptance_criteria verlangten explizit nur grep-Nachweise für diese Datei, kein Render-Test. Der volle Klickpfad (Discover -> Radio wählen -> Speichern -> Chip erscheint) und insbesondere das Verhalten gegen ein echtes AD wurden in dieser Session nicht im Browser geprüft (kein Browser-Tool verfügbar).
id description requirement verification human_judgment
D3 GroupMembersModal: Mitglieder-Chips mit Herkunfts-Badge, LDAP-Mitgliedschaften mit deaktiviertem Entfernen-Button (D-19), manuelles Hinzufügen per Benutzersuche PERM-01
kind ref status
unit groups-page.test.tsx ('disables the remove button for LDAP-sourced members and keeps it active for MANUAL members') pass
kind ref status
other grep-Nachweise: disabled/ldapManaged in GroupMembersModal.tsx pass
false
id description requirement verification human_judgment
D4 DeleteGroupDialog: laedt GET /groups/:id/impact, zeigt memberCount/grantCount konkret (D-17), bleibt bei fehlgeschlagenem DELETE offen mit sichtbarer Fehlermeldung (T-15-24, bewusste Abweichung vom silent-fail-Präzedenzfall) PERM-01
kind ref status
unit groups-page.test.tsx ('shows both concrete numbers...', 'stays open and shows a visible error when DELETE fails') pass
kind ref status
other grep-Nachweise: impact-Aufruf + border-destructive/50 bg-destructive/10 in DeleteGroupDialog.tsx pass
false
id description verification human_judgment rationale
D5 Vollständiges manuelles Durchklicken im Browser aus dem Plan-<verification>-Block: Anlegen, Umbenennen, Standardmarkierung setzen/abschalten, AD-Bindung setzen/lösen, Mitglied hinzufügen/entfernen, Löschdialog mit gefüllter Gruppe, sowie AD-Suche/Löschen bei abgeschalteter API
true Diese Session hatte kein Browser-/Playwright-Tool zur Verfügung (nur Read/Write/Edit/Bash/Skill) — der interaktive Durchlauf aus dem Plan-<verification>-Block konnte nicht ausgeführt werden. Abgedeckt ist stattdessen: vollständiger Vitest-Lauf (162/162 grün), fehlerfreier Produktions-Build inkl. Next.js-Typecheck, sowie alle grep-basierten acceptance_criteria aus allen drei Tasks. In WINDOWS.md als unrun-verify vermerkt.
ca. 35min 2026-08-04 complete

Phase 15 Plan 06: Gruppenverwaltung im Admin-UI Summary

/admin/groups mit Tabelle, Anlege/Umbenennen-Dialog samt AD-Radio-Bindung, Mitglieder-Dialog mit LDAP-Herkunftssperre und Löschdialog mit konkreten Zahlen — plus sämtliche i18n-Schlüssel der gesamten Phase 15 in de.json/en.json (auch die erst von 15-07/15-08 benötigten).

Performance

  • Duration: ca. 35 min
  • Started: 2026-08-04T16:30:00Z (ungefähr — kein expliziter Start-Timestamp erfasst)
  • Completed: 2026-08-04T17:05:00Z
  • Tasks: 3
  • Files modified: 8

Accomplishments

  • de.json/en.json tragen deckungsgleich (762 Schlüssel) alle Übersetzungen der gesamten Phase 15 — nicht nur für /admin/groups, sondern auch die von den Wave-4-Plänen 15-07/15-08 benötigten Namensräume (admin.users.grants.*, adminModules.grantsLink/grants.*/activationDialog.*, modules.accessDenied.*, marketplace.statusNoAccess/toastNoAccess), damit diese Pläne parallel laufen können, ohne die Übersetzungsdateien anzufassen
  • AdminSidebar hat einen sechsten Eintrag "Gruppen" → /admin/groups mit einem eigenen Roster-/Listen-Icon (Lucide list-Glyphe), optisch klar vom Personen-Icon des Benutzer-Eintrags unterschieden
  • /admin/groups zeigt eine Tabelle mit Name, AD-Bindungs-Badge, Standardgruppen-Stern-Toggle (optimistisch mit Rollback bei Fehler, volle Refetch bei Erfolg wegen der serverseitigen Exklusivität aus D-13), Mitgliederzahl und den drei Aktionen Bearbeiten/Mitglieder/Löschen
  • GroupFormModal bindet eine Gruppe an eine AD-Gruppe ausschliesslich über eine Radio-Auswahl aus der bestehenden GET /ldap/groups-Discovery (D-05, D-18) — kein Freitextfeld für einen DN, damit T-15-23 (getippter DN im späteren LDAP-Filter) strukturell ausgeschlossen bleibt. Fehlschlag oder Leerergebnis der AD-Suche erscheinen sichtbar (ldapBind.discoverError/.noResults) statt einer stillen Leerliste
  • GroupMembersModal zeigt Mitglieder als Chips mit Herkunfts-Badge; bei Herkunft LDAP ist der Entfernen-Button deaktiviert mit Tooltip "Wird über AD-Sync verwaltet" (D-19) — nur der Sync darf diese Mitgliedschaft entfernen
  • DeleteGroupDialog lädt GET /groups/:id/impact und interpoliert memberCount/grantCount konkret in den Warntext (D-17); bricht bewusst mit dem projektweiten silent-fail-Präzedenzfall der übrigen Lösch-Dialoge, weil ein unbemerkter Fehlschlag hier einen Admin im Glauben liesse, eine Gruppe samt Freigaben sei weg, während sie weiterhin Zugriff gewährt (T-15-24) — der Dialog bleibt bei einem Fehlschlag offen und zeigt die Meldung im bestehenden error-Div-Muster
  • 7 neue Komponententests in groups-page.test.tsx: leerer Zustand, gefüllte Tabelle, Stern-Toggle (Erfolg + Rollback bei Fehler), deaktivierter vs. aktiver Entfernen-Button je nach Mitgliedschafts-Herkunft, Löschtext mit beiden Zahlen, sichtbare Fehlermeldung bei fehlgeschlagenem Löschen — voller Web-Testlauf 162/162 grün, pnpm --filter @tessera/web run build fehlerfrei

Task Commits

Jeder Task wurde atomar committet:

  1. Task 1: Alle i18n-Schlüssel der Phase und der sechste Admin-Navigationseintrag - 90dc398 (feat)
  2. Task 2: Gruppenübersicht mit Anlege-, Umbenennen- und AD-Bindungs-Dialog - c3ba1f7 (feat)
  3. Task 3: Mitglieder-Dialog und Löschdialog mit konkreten Zahlen - 4985e43 (feat)

Plan metadata: siehe Commit dieser SUMMARY.md (docs: complete plan)

Files Created/Modified

  • apps/web/src/app/(portal)/admin/groups/page.tsx - Gruppenübersicht, State/Fetch-Logik, Standardgruppen-Stern-Toggle, hängt alle drei Dialoge ein
  • apps/web/src/app/(portal)/admin/groups/components/GroupFormModal.tsx - Anlegen/Umbenennen inkl. AD-Radio-Bindung
  • apps/web/src/app/(portal)/admin/groups/components/GroupMembersModal.tsx - Mitglieder-Chips + manuelles Hinzufügen per Benutzersuche
  • apps/web/src/app/(portal)/admin/groups/components/DeleteGroupDialog.tsx - Löschdialog mit impact-Zahlen und sichtbarem Fehlerfall
  • apps/web/src/app/(portal)/admin/groups/groups-page.test.tsx - 7 Komponententests
  • apps/web/src/messages/de.json, en.json - alle i18n-Schlüssel der gesamten Phase 15
  • apps/web/src/components/admin/admin-sidebar.tsx - sechster Navigationseintrag

Decisions Made

  • Task 2/3-Split von page.tsx: Task 2 committet die Seite mit Create/Edit-Modal verdrahtet und Mitglieder-/Lösch-State bereits deklariert, aber ohne Import der (in Task 2 noch nicht existierenden) Task-3-Komponenten — jeder Task-Commit bleibt für sich buildbar
  • Gruppen-Erstellung mit sofortiger AD-Bindung läuft zweistufig (POST /groups, dann PATCH /groups/:id {ldapDn}), weil CreateGroupDto aus 15-02 ausschliesslich name entgegennimmt — keine Schema-/DTO-Änderung, reine Client-Sequenzierung
  • matrixCheckboxLabel/directCheckboxLabel (Wave-4-Schlüssel für 15-07/15-08) als next-intl-ICU-select mit einem zusätzlichen granted-Parameter modelliert ({module} für Gruppe {group} {granted, select, true {freigeben} other {entziehen}}), damit ein einziger Schlüssel beide Aria-Label-Varianten (freigeben/entziehen) bedient, statt zwei separate Schlüssel für denselben Text-Baustein anzulegen
  • DeleteGroupDialog behandelt einen fehlgeschlagenen impact-Abruf (nicht nur den Lösch-Aufruf selbst) sichtbar und deaktiviert den Löschen-Button, statt mit memberCount: 0/grantCount: 0 eine falsche Zahl zu zeigen — D-17 verlangt konkrete, korrekte Zahlen, ein Fallback auf 0 wäre irreführend

Deviations from Plan

Auto-fixed Issues

1. [Rule 2 - fehlende Funktionalität] DeleteGroupDialog behandelt einen fehlgeschlagenen impact-Abruf sichtbar

  • Found during: Task 3, beim Implementieren von DeleteGroupDialog
  • Issue: Der Plan beschreibt nur den Fehlerfall des eigentlichen DELETE-Aufrufs explizit; ein fehlschlagender GET /groups/:id/impact-Aufruf hätte sonst entweder eine undefined-Anzeige oder (schlimmer) eine falsche 0/0-Zahl gezeigt — beides würde D-17 verletzen, das konkrete, korrekte Zahlen verlangt
  • Fix: impactError-State zeigt bei fehlgeschlagenem Impact-Abruf den Fehler-Div statt des Warntexts und deaktiviert den Löschen-Button, bis die Zahlen tatsächlich geladen sind
  • Files modified: apps/web/src/app/(portal)/admin/groups/components/DeleteGroupDialog.tsx
  • Commit: 4985e43

2. [Rule 2 - fehlende Funktionalität] ICU-select für matrixCheckboxLabel/directCheckboxLabel statt reiner Freigeben-Formulierung

  • Found during: Task 1, beim Anlegen der Wave-4-Schlüssel admin.groups.grants.matrixCheckboxLabel und admin.users.grants.directCheckboxLabel
  • Issue: Der Copywriting Contract des UI-SPEC beschreibt für dieselbe Checkbox zwei Textvarianten ("… freigeben" bzw. "… entziehen" je nach Zustand), der Plan listet aber nur je einen Schlüssel mit den Parametern Modul/Gruppe bzw. Modul/Benutzer — ohne einen dritten Parameter hätte der Schlüssel nur eine der beiden Zustandsvarianten abdecken können
  • Fix: Beide Schlüssel nutzen next-intl-kompatible ICU-select-Syntax mit einem zusätzlichen granted-Boolean-Parameter, der zwischen den beiden Verbformen wählt — bleibt bei genau einem Schlüssel pro aria-label, wie im Plan vorgegeben
  • Files modified: apps/web/src/messages/de.json, apps/web/src/messages/en.json
  • Commit: 90dc398

Total deviations: 2 auto-fixed (beide Rule 2 — fehlende Funktionalität) Impact on plan: Beide Ergänzungen waren für Korrektheit (D-17) bzw. Vollständigkeit der Wave-4-Schlüssel notwendig. Kein Scope Creep — beide bleiben innerhalb der im Plan beschriebenen Dateien und Verantwortlichkeiten.

Issues Encountered

  • Diese Session lief ohne Browser-/Playwright-Tool (nur Read/Write/Edit/Bash/Skill verfügbar). Der im Plan unter <verification> geforderte manuelle Durchklick-Test im Browser (Anlegen, Umbenennen, Standardmarkierung, AD-Bindung setzen/lösen, Mitglied hinzufügen/entfernen, Löschdialog mit gefüllter Gruppe, sowie AD-Suche/Löschen bei abgeschalteter API) konnte nicht ausgeführt werden. Abgedeckt ist stattdessen: voller Vitest-Lauf (162/162 grün, davon 7 neue Tests für diesen Plan), fehlerfreier Produktions-Build inkl. Next.js-Typecheck, und alle grep-basierten acceptance_criteria aus allen drei Tasks. Als coverage-Eintrag D5 (human_judgment: true) und in .planning/WINDOWS.md als unrun-verify vermerkt — ein menschlicher Durchklick-Test vor dem Ship dieser Phase wird empfohlen.
  • Die lokale Dev-DB hat nur einen Mandanten mit einer Gruppe ("Alle Benutzer", laut lokaler Notiz aktuell isDefault = false). Dieser Plan hat die DB in dieser Session nicht angefasst — kein Stern-Toggle wurde live gegen die lokale API ausgeführt (siehe Issue oben), der DB-Zustand ist unverändert gegenüber dem Sessionstart.

User Setup Required

None - keine externe Service-Konfiguration nötig.

Next Phase Readiness

  • de.json/en.json sind vollständig für die gesamte Phase 15 vorbereitet — 15-07 und 15-08 können parallel laufen, ohne die Übersetzungsdateien anzufassen
  • /admin/groups ist vollständig nutzbar (PERM-01): Gruppen anlegen/umbenennen/löschen, Mitglieder manuell zuweisen/entfernen, AD-Bindung setzen/lösen, Standardgruppe markieren
  • Empfehlung vor /gsd-ship dieser Phase: einmaliger manueller Durchklick-Test im Browser (siehe Issues Encountered/D5), da diese Session ohne Browser-Tool lief
  • Kein neuer Blocker aus diesem Plan; 15-04 (AD-Gruppen-Mitgliedschafts-Sync) blieb wie in den Files-to-read vermerkt an einem offenen Human-Verify-Checkpoint stehen (nicht Teil dieses Plans) — dessen 15-04-SUMMARY.md existiert entsprechend noch nicht

Phase: 15-modul-berechtigungen-gruppen-user-grants Completed: 2026-08-04

Self-Check: PASSED

Alle neun in dieser SUMMARY genannten Dateien existieren auf der Platte, alle drei Task-Commit-Hashes (90dc398, c3ba1f7, 4985e43) sind im Git-Log auffindbar.