Files
tessera-ctl/.planning/phases/15-modul-berechtigungen-gruppen-user-grants/15-07-SUMMARY.md
T

174 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
phase: 15-modul-berechtigungen-gruppen-user-grants
plan: 07
subsystem: ui
tags: [nextjs, next-intl, react, admin, module-grants, groups]
# Dependency graph
requires:
- phase: 15-modul-berechtigungen-gruppen-user-grants
plan: 03
provides: ModuleGrantsService/ModuleGrantsController (grant/revoke/getMatrix/getUserAccess), GET /modules/catalog Statusflags
- phase: 15-modul-berechtigungen-gruppen-user-grants
plan: 06
provides: "/admin/groups Admin-UI, alle i18n-Schluessel der gesamten Phase 15 (inkl. adminModules.grants.*/activationDialog.*, admin.users.grants.*, admin.groups.grants.matrixCheckboxLabel)"
provides:
- "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)"
affects: [15-08]
actuals:
tokens: 12861
tasks: 3
commits: 3
tech-stack:
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"
key-files:
created:
- 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
modified:
- apps/web/src/app/(portal)/admin/modules/page.tsx
- apps/web/src/app/(portal)/admin/users/page.tsx
key-decisions:
- "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"
requirements-completed: [PERM-03]
coverage:
- id: D1
description: "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"
requirement: "PERM-03"
verification:
- kind: unit
ref: "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)"
status: pass
- kind: other
ref: "grep-Nachweise aus Task-1-acceptance_criteria (sticky left-0/top-0, truncate, title=, aria-label, adminNote, grantsLink) + pnpm --filter @tessera/web run build"
status: pass
human_judgment: false
- id: D2
description: "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"
requirement: "PERM-03"
verification:
- kind: unit
ref: "apps/web/src/app/(portal)/admin/modules/grants/grants-matrix.test.tsx (describe 'ActivateModuleDialog': 3 Tests -- alle drei Buttons, Deaktivierung + Hinweistext ohne Standardgruppe, Aufrufreihenfolge)"
status: pass
- kind: other
ref: "grep-Nachweise aus Task-2-acceptance_criteria (max-w-sm, noDefaultGroupHint, disabled, break-words, ActivateModuleDialog in page.tsx) + pnpm --filter @tessera/web run build"
status: pass
human_judgment: false
- id: D3
description: "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"
requirement: "PERM-03"
verification:
- kind: unit
ref: "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)"
status: pass
- kind: other
ref: "grep-Nachweise aus Task-3-acceptance_criteria (max-w-2xl, noActiveModules, directCheckboxLabel, module-grants/users, detailsButton) + pnpm --filter @tessera/web run build"
status: pass
human_judgment: false
- id: D4
description: "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"
verification: []
human_judgment: true
rationale: "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."
duration: ca. 30min
completed: 2026-08-04
status: 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.