Files

16 KiB
Raw Permalink Blame History

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 07 ui
nextjs
next-intl
react
admin
module-grants
groups
phase plan provides
15-modul-berechtigungen-gruppen-user-grants 03 ModuleGrantsService/ModuleGrantsController (grant/revoke/getMatrix/getUserAccess), GET /modules/catalog Statusflags
phase plan provides
15-modul-berechtigungen-gruppen-user-grants 06 /admin/groups Admin-UI, alle i18n-Schluessel der gesamten Phase 15 (inkl. adminModules.grants.*/activationDialog.*, admin.users.grants.*, admin.groups.grants.matrixCheckboxLabel)
Freigabe-Matrix Module x Gruppen unter /admin/modules/grants (D-15, PERM-03) -- sticky Kopf/Spalte, Kategorie-Gruppierung, Suchfeld, sofortiges optimistisches Toggle mit Rollback
ActivateModuleDialog mit drei Aktionen (Abbrechen / Spaeter konfigurieren / Sofort freigeben) beim Aktivieren eines Moduls (D-10, D-13)
UserAccessModal im Benutzer-Detail: read-only Gruppenmitgliedschaften + Modul-Zugriffstabelle mit geerbten Gruppen und Direkt-Toggle (D-16, PERM-03)
15-08
tokens tasks commits
12861 3 3
added patterns
aria-label des Grant-Checkboxes beschreibt die vom Klick ausgeloeste Aktion (freigeben/entziehen), nicht den aktuellen Zustand -- granted-Parameter der matrixCheckboxLabel/directCheckboxLabel-ICU-select-Schluessel erhaelt !isGranted, nicht isGranted
UserAccessModal leitet die Gruppenmitgliedschafts-Chipliste ausschliesslich aus der dedupliziertem Vereinigung aller viaGroups-Namen von GET /module-grants/users/:userId ab -- kein zweiter Endpoint fuer die reine Mitgliedschaftsliste, exakt wie der Plan-key_link es vorgibt ('geerbte und direkte Rechte kommen aus einer Antwort')
ActivateModuleDialog aktualisiert den Aktivierungs-Zustand des Aufrufers bereits nach dem erfolgreichen activate-Call, bevor der optionale module-grants-Call laeuft -- das Modul ist ab diesem Zeitpunkt echt aktiv, unabhaengig vom Ausgang des zweiten Calls
created modified
apps/web/src/app/(portal)/admin/modules/grants/page.tsx
apps/web/src/app/(portal)/admin/modules/grants/grants-matrix.test.tsx
apps/web/src/app/(portal)/admin/modules/components/ActivateModuleDialog.tsx
apps/web/src/app/(portal)/admin/users/components/UserAccessModal.tsx
apps/web/src/app/(portal)/admin/users/user-access-modal.test.tsx
apps/web/src/app/(portal)/admin/modules/page.tsx
apps/web/src/app/(portal)/admin/users/page.tsx
Suchfeld der Matrix filtert Modul- und Gruppennamen unabhaengig voneinander (zwei getrennte .filter()-Aufrufe auf dieselbe searchLower-Variable) statt einer kombinierten Sichtbarkeitsregel -- einfachste Interpretation von 'filtert clientseitig sowohl Modul- als auch Gruppennamen', kein Praezedenzfall im Bestandscode fuer eine 2D-Matrix-Suche
granted-Interpolationsparameter der ICU-select-Aria-Label-Schluessel wird als String(!isGranted)/String(!row.direct) uebergeben -- next-intl/TypeScript akzeptiert fuer Message-Params nur string/number/Date, kein boolean; und der Parameterwert beschreibt bewusst die bevorstehende Aktion, nicht den aktuellen Haekchen-Zustand (den vermittelt bereits checked/aria-checked)
ActivateModuleDialog fuehrt beide Aufrufe nicht optimistisch, sondern sequenziell und wartend aus -- da 'Sofort freigeben' zwei echte HTTP-Calls in fester Reihenfolge ist (activate dann grant, laut Plan bewusst kein kombinierter Endpoint), waere ein optimistisches Vorab-Setzen beider Zustaende vor Bestaetigung irrefuehrend; onSuccess wird erst nach dem ersten erfolgreichen Call aufgerufen, weil das Modul ab da unabhaengig vom zweiten Call wirklich aktiv ist
PERM-03
id description requirement verification human_judgment
D1 Freigabe-Matrix Module x Gruppen unter /admin/modules/grants: sticky erste Spalte/Kopfzeile, Kategorie-Gruppierung, Suchfeld, sofortiges optimistisches Toggle mit Rollback + sichtbarer Fehlermeldung bei Fehlschlag, aria-label je Checkbox, Admin-Bypass-Fussnote, leerer Zustand ohne aktive Module PERM-03
kind ref status
unit apps/web/src/app/(portal)/admin/modules/grants/grants-matrix.test.tsx (describe 'AdminModuleGrantsPage': 5 Tests -- befuellte Matrix, leerer Zustand, Rollback bei Fehler, Suchfilter, aria-label je Checkbox) pass
kind ref status
other grep-Nachweise aus Task-1-acceptance_criteria (sticky left-0/top-0, truncate, title=, aria-label, adminNote, grantsLink) + pnpm --filter @tessera/web run build pass
false
id description requirement verification human_judgment
D2 ActivateModuleDialog: drei Aktionen (Abbrechen/Spaeter konfigurieren/Sofort freigeben), Sofort-freigeben deaktiviert mit Hinweistext ohne markierte Standardgruppe (D-13), Aufrufreihenfolge activate->module-grants bei Sofort freigeben PERM-03
kind ref status
unit apps/web/src/app/(portal)/admin/modules/grants/grants-matrix.test.tsx (describe 'ActivateModuleDialog': 3 Tests -- alle drei Buttons, Deaktivierung + Hinweistext ohne Standardgruppe, Aufrufreihenfolge) pass
kind ref status
other grep-Nachweise aus Task-2-acceptance_criteria (max-w-sm, noDefaultGroupHint, disabled, break-words, ActivateModuleDialog in page.tsx) + pnpm --filter @tessera/web run build pass
false
id description requirement verification human_judgment
D3 UserAccessModal im Benutzer-Detail: read-only Gruppenmitgliedschafts-Chipliste (kein zweites Bearbeitungsziel, D-16), Modul-Zugriffstabelle mit geerbten Gruppen-Chips oder Gedankenstrich, Direkt-Checkbox mit optimistischem Toggle + Rollback, Hinweistext ohne aktive Module, aria-label je Checkbox PERM-03
kind ref status
unit apps/web/src/app/(portal)/admin/users/user-access-modal.test.tsx (5 Tests -- Chip-Liste + Leerzustand, Modultabelle mit geerbtem/nicht-geerbtem Modul, Rollback bei Fehler, Hinweistext ohne aktive Module, aria-label je Checkbox) pass
kind ref status
other grep-Nachweise aus Task-3-acceptance_criteria (max-w-2xl, noActiveModules, directCheckboxLabel, module-grants/users, detailsButton) + pnpm --filter @tessera/web run build pass
false
id description verification human_judgment rationale
D4 Vollstaendiges manuelles Durchklicken im Browser aus dem Plan-<verification>-Block: Freigabe in der Matrix setzen/entziehen und mit Testbenutzer gegenpruefen, Modul aktivieren ueber beide Dialogwege, Direkt-Grant neben bestehendem Gruppen-Grant im Benutzer-Detail setzen, Fehlerfall bei gestoppter API, lange Gruppen-/Modulnamen
true Diese Session hatte kein Browser-/Playwright-Tool zur Verfuegung (nur Read/Write/Edit/Bash/Skill) -- identisch zur Vorbedingung in 15-06. Der interaktive Durchlauf aus dem Plan-<verification>-Block konnte nicht ausgefuehrt werden. Abgedeckt ist stattdessen: voller Vitest-Lauf (175/175 gruen, davon 13 neue Tests fuer diesen Plan), 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. 30min 2026-08-04 complete

Phase 15 Plan 07: Freigabe-Matrix, Aktivierungsdialog und Benutzer-Detail-Grants Summary

Die beiden Oberflächen, über die Freigaben tatsächlich vergeben werden -- eine sticky Freigabe-Matrix Module x Gruppen unter /admin/modules/grants und ein Benutzer-Detail-Modal mit geerbten Gruppen-Rechten plus Direkt-Toggle -- dazu ein dreistufiger Aktivierungsdialog ("Abbrechen"/"Später konfigurieren"/"Sofort freigeben"), der beim Aktivieren eines Moduls nach der Standardgruppen-Freigabe fragt.

Performance

  • Duration: ca. 30 min
  • Started: 2026-08-04T~16:55:00Z (ungefähr -- kein expliziter Start-Timestamp erfasst)
  • Completed: 2026-08-04T17:25:15Z
  • Tasks: 3
  • Files modified: 7

Accomplishments

  • apps/web/src/app/(portal)/admin/modules/grants/page.tsx (neu): lädt einmal GET /module-grants/matrix und rendert Module x Gruppen in einer Tabelle mit sticky left-0-erster Spalte (Modulnamen, truncate + title), sticky top-0-Kopfzeile (Gruppennamen, ebenso truncate + title), Zwischenzeilen je Modulkategorie (bg-muted/50) und einem clientseitigen Suchfeld, das Modul- und Gruppennamen unabhängig voneinander filtert. Jede Zelle ist ein natives Checkbox mit sofortigem optimistischem Toggle (POST/DELETE /module-grants), Inline-Spinner nur in der betroffenen Zelle, Rücksprung auf den Serverzustand plus sichtbare Fehlermeldung bei Fehlschlag (T-15-25) und aria-label aus admin.groups.grants.matrixCheckboxLabel. Fußnote macht D-03 (ADMIN/SUPER_ADMIN umgehen die Matrix) transparent. Leerer Zustand ohne aktive Module verlinkt zurück auf /admin/modules
  • admin/modules/page.tsx: neuer Header-Button "Freigaben-Matrix" (adminModules.grantsLink) verlinkt auf die Unterseite -- kein siebter Sidebar-Eintrag, bleibt Unterseite von Module
  • ActivateModuleDialog.tsx (neu): lädt beim Öffnen GET /groups, sucht die als Standard markierte Gruppe. Drei Aktionen: "Abbrechen" (kein Request), "Später konfigurieren" (nur POST /modules/:id/activate), "Sofort freigeben" (POST .../activate gefolgt von POST /module-grants für die Standardgruppe -- zwei getrennte Aufrufe, kein kombinierter Endpoint). Ohne markierte Standardgruppe (D-13) ist "Sofort freigeben" disabled mit Hinweistext. Titel/Body brechen bei langen Modulnamen um (break-words) statt den max-w-sm-Dialog zu verbreitern
  • admin/modules/page.tsx: toggleModule-Klick verzweigt -- Deaktivierung bleibt der bestehende direkte Pfad, Aktivierung öffnet stattdessen ActivateModuleDialog. Erfolg aktualisiert die Aktivierungs-Map und stößt den Sidebar-Refresh an, identisch zum bisherigen Verhalten
  • UserAccessModal.tsx (neu): lädt einmal GET /module-grants/users/:userId und rendert daraus zwei Abschnitte -- Gruppenmitgliedschaften als read-only Chip-Liste (dedupliziert aus allen viaGroups-Namen, kein Entfernen-Button; Bearbeitung bleibt ausschließlich unter /admin/groups, D-16) und Modul-Zugriff als Tabelle mit den erbenden Gruppen als Chips (oder "–") sowie einer Direkt-Checkbox. Die Direkt-Checkbox verhält sich identisch zur Matrix-Zelle: optimistisches Toggle, Rollback bei Fehler, sichtbare Fehlermeldung, aria-label aus admin.users.grants.directCheckboxLabel. Hinweistext, wenn der Mandant keine aktiven Module hat
  • admin/users/page.tsx: vierter Aktionsbutton "Details" je Zeile öffnet das Modal
  • Beide ICU-select-aria-label-Schlüssel (matrixCheckboxLabel, directCheckboxLabel) bekommen den granted-Parameter als String(!isGranted) -- das Label beschreibt bewusst die vom Klick ausgelöste Aktion ("freigeben"/"entziehen"), nicht den aktuellen Häkchen-Zustand, den checked/aria-checked bereits vermitteln
  • 13 neue Komponententests über zwei Dateien: grants-matrix.test.tsx (5 Matrix-Tests + 3 ActivateModuleDialog-Tests), user-access-modal.test.tsx (5 Tests) -- voller Web-Testlauf 175/175 grün, pnpm --filter @tessera/web run build fehlerfrei

Task Commits

Jeder Task wurde atomar committet:

  1. Task 1: Freigabe-Matrix Module mal Gruppen - 6b2a6f1 (feat)
  2. Task 2: Aktivierungsdialog mit drei Aktionen in /admin/modules - 3cd6d99 (feat)
  3. Task 3: Benutzer-Detail mit geerbten Rechten und Direkt-Freigaben - 050d070 (feat)

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

Files Created/Modified

  • apps/web/src/app/(portal)/admin/modules/grants/page.tsx - Freigabe-Matrix (neu)
  • apps/web/src/app/(portal)/admin/modules/grants/grants-matrix.test.tsx - 8 Tests (Matrix + ActivateModuleDialog)
  • apps/web/src/app/(portal)/admin/modules/page.tsx - Header-Button "Freigaben-Matrix", toggleModule-Verzweigung, ActivateModuleDialog-Einbindung
  • apps/web/src/app/(portal)/admin/modules/components/ActivateModuleDialog.tsx - Aktivierungsdialog mit drei Aktionen (neu)
  • apps/web/src/app/(portal)/admin/users/page.tsx - vierter Aktionsbutton "Details", UserAccessModal-Einbindung
  • apps/web/src/app/(portal)/admin/users/components/UserAccessModal.tsx - Benutzer-Detail-Zugriff (neu)
  • apps/web/src/app/(portal)/admin/users/user-access-modal.test.tsx - 5 Tests

Decisions Made

  • Suchfeld der Matrix filtert Modul- und Gruppennamen unabhängig voneinander (zwei getrennte .filter()-Aufrufe) statt einer kombinierten Sichtbarkeitsregel -- einfachste, direkte Interpretation des Plan-Texts, kein Präzedenzfall im Bestandscode für eine 2D-Matrix-Suche
  • granted-Interpolationsparameter der ICU-select-Aria-Label-Schlüssel wird als String(!isGranted) übergeben statt als boolean -- next-intl/TypeScript-Typen für Message-Params akzeptieren nur string | number | Date; inhaltlich beschreibt der Parameter bewusst die bevorstehende Aktion, nicht den aktuellen Zustand
  • ActivateModuleDialog führt "Sofort freigeben" nicht optimistisch aus, sondern wartet auf beide HTTP-Antworten in fester Reihenfolge; onSuccess wird bereits nach dem ersten erfolgreichen Call aufgerufen, weil das Modul ab da unabhängig vom zweiten Call wirklich aktiv ist -- vermeidet einen Zustand, in dem die Oberfläche eine Aktivierung anzeigt, die der Server (noch) nicht bestätigt hat

Deviations from Plan

None - plan executed exactly as written. Die im Plan als "Rule 2"-Kandidat denkbare Frage, ob ein fehlgeschlagener module-grants-Call nach erfolgreichem activate-Call den Dialog erneut öffnen oder einen zweiten Retry-Pfad anbieten sollte, wurde bewusst NICHT als zusätzliche Funktionalität ergänzt: der Plan-Text sagt explizit nur "im Fehlerfall wird zurückgesetzt und die Meldung im bestehenden Fehler-Div angezeigt" -- das leistet die aktuelle Implementierung (Modul bleibt aktiv, Fehlermeldung erscheint, Admin kann die Freigabe danach über die Matrix nachholen).

Issues Encountered

  • Diese Session lief ohne Browser-/Playwright-Tool (nur Read/Write/Edit/Bash/Skill verfügbar), identisch zur Vorbedingung in Plan 15-06. Der im Plan unter <verification> geforderte manuelle Durchklick-Test (Freigabe in der Matrix setzen/entziehen und mit Testbenutzer gegenprüfen, Modul über beide Dialogwege aktivieren, Direkt-Grant neben bestehendem Gruppen-Grant setzen, Fehlerfall bei gestoppter API, lange Gruppen-/Modulnamen) konnte nicht ausgeführt werden. Abgedeckt ist stattdessen: voller Vitest-Lauf (175/175 grün, davon 13 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 D4 (human_judgment: true) und in .planning/WINDOWS.md als unrun-verify vermerkt
  • Die im Auftrag genannte local_db_note (lokale Gruppe "Alle Benutzer" mit isDefault = false) wurde in dieser Session NICHT geändert -- ohne Browser-Tool gab es keinen manuellen Durchlauf, der eine markierte Standardgruppe benötigt hätte. Die lokale Dev-DB ist gegenüber dem Sessionstart unverändert
  • Beim ersten Testlauf schlug die aria-label-Erwartung fehl, weil granted zunächst als isGranted (aktueller Zustand) statt als !isGranted (bevorstehende Aktion) übergeben wurde -- korrigiert vor dem ersten Commit, siehe "Decisions Made"
  • Der Next.js-Produktionsbuild lehnte den boolean-Wert für den granted-Interpolationsparameter mit einem TypeScript-Fehler ab (next-intl-Message-Params erwarten string | number | Date) -- behoben durch explizite String(...)-Konvertierung an beiden Aufrufstellen (Matrix-Zelle, Direkt-Checkbox)

User Setup Required

None - keine externe Service-Konfiguration nötig.

Next Phase Readiness

  • Alle drei Oberflächen aus PERM-03/D-15/D-16 sind vollständig nutzbar: Gruppenfreigaben über die Matrix, Einzelfreigaben im Benutzer-Detail, Aktivierungsdialog mit Standardgruppen-Rückfrage
  • Plan 15-08 (laut Frontmatter affects) kann auf diesem Plan aufbauen -- ModuleGrantsService/ModuleGrantsController aus 15-03 sind die einzige Schreibseite, die alle drei Oberflächen dieses Plans konsumieren
  • Empfehlung vor /gsd-ship dieser Phase: einmaliger manueller Durchklick-Test im Browser für 15-06 UND 15-07 zusammen (beide Sessions liefen ohne Browser-Tool), siehe WINDOWS.md
  • Kein neuer Blocker aus diesem Plan

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

Self-Check: PASSED

Alle acht in dieser SUMMARY genannten Dateien (fünf Quelldateien, zwei Testdateien plus diese SUMMARY.md) existieren auf der Platte, alle drei Task-Commit-Hashes (6b2a6f1, 3cd6d99, 050d070) sind im Git-Log auffindbar.