docs(15-07): complete Freigabe-Matrix, Aktivierungsdialog und Benutzer-Detail-Grants plan
This commit is contained in:
@@ -511,7 +511,7 @@ Plans:
|
|||||||
|
|
||||||
**Neue Modelle**: `Group` (tenantId, name, ldapDn?), `GroupMembership` (userId, groupId, source MANUAL|LDAP), `ModuleGrant` (tenantId, moduleId, groupId? | userId?)
|
**Neue Modelle**: `Group` (tenantId, name, ldapDn?), `GroupMembership` (userId, groupId, source MANUAL|LDAP), `ModuleGrant` (tenantId, moduleId, groupId? | userId?)
|
||||||
|
|
||||||
**Plans**: 5/8 plans executed
|
**Plans**: 6/8 plans executed
|
||||||
|
|
||||||
Plans:
|
Plans:
|
||||||
**Wave 1**
|
**Wave 1**
|
||||||
@@ -531,7 +531,7 @@ Plans:
|
|||||||
|
|
||||||
**Wave 4** *(blocked on Wave 3 completion)*
|
**Wave 4** *(blocked on Wave 3 completion)*
|
||||||
|
|
||||||
- [ ] 15-07-PLAN.md — Freigabe-Matrix, Benutzer-Detail und Aktivierungsdialog
|
- [x] 15-07-PLAN.md — Freigabe-Matrix, Benutzer-Detail und Aktivierungsdialog
|
||||||
- [ ] 15-08-PLAN.md — Serverseitige 403-Modulsperre und Marketplace-Kennzeichnung
|
- [ ] 15-08-PLAN.md — Serverseitige 403-Modulsperre und Marketplace-Kennzeichnung
|
||||||
|
|
||||||
**Wellen**: 1 → 15-01 · 2 → 15-02, 15-04, 15-05 · 3 → 15-03, 15-06 · 4 → 15-07, 15-08
|
**Wellen**: 1 → 15-01 · 2 → 15-02, 15-04, 15-05 · 3 → 15-03, 15-06 · 4 → 15-07, 15-08
|
||||||
@@ -559,4 +559,4 @@ Phases execute in numeric order: 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> 10
|
|||||||
| 12. Tender Notifications | 4/4 | In Progress| |
|
| 12. Tender Notifications | 4/4 | In Progress| |
|
||||||
| 13. Scraping Adapters & Cross-Source Deduplication | 6/6 | In Progress| |
|
| 13. Scraping Adapters & Cross-Source Deduplication | 6/6 | In Progress| |
|
||||||
| 14. RSS, Email-Alert Ingestion & Module Rollout | 5/5 | In Progress| |
|
| 14. RSS, Email-Alert Ingestion & Module Rollout | 5/5 | In Progress| |
|
||||||
| 15. Modul-Berechtigungen: Gruppen & User-Grants | 5/8 | In Progress| |
|
| 15. Modul-Berechtigungen: Gruppen & User-Grants | 6/8 | In Progress| |
|
||||||
|
|||||||
+11
-7
@@ -5,15 +5,15 @@ milestone_name: Ausschreibungs-Radar
|
|||||||
current_phase: 15
|
current_phase: 15
|
||||||
current_phase_name: modul-berechtigungen-gruppen-user-grants
|
current_phase_name: modul-berechtigungen-gruppen-user-grants
|
||||||
status: executing
|
status: executing
|
||||||
stopped_at: Completed 15-06-PLAN.md
|
stopped_at: Completed 15-07-PLAN.md
|
||||||
last_updated: "2026-08-04T17:06:12.249Z"
|
last_updated: "2026-08-04T17:28:00.601Z"
|
||||||
last_activity: 2026-08-04
|
last_activity: 2026-08-04
|
||||||
last_activity_desc: Phase 15 execution started
|
last_activity_desc: Phase 15 execution started
|
||||||
progress:
|
progress:
|
||||||
total_phases: 15
|
total_phases: 15
|
||||||
completed_phases: 13
|
completed_phases: 13
|
||||||
total_plans: 75
|
total_plans: 75
|
||||||
completed_plans: 71
|
completed_plans: 72
|
||||||
---
|
---
|
||||||
|
|
||||||
# Project State
|
# Project State
|
||||||
@@ -28,11 +28,11 @@ See: .planning/PROJECT.md (updated 2026-07-17)
|
|||||||
## Current Position
|
## Current Position
|
||||||
|
|
||||||
Phase: 15 (modul-berechtigungen-gruppen-user-grants) — EXECUTING
|
Phase: 15 (modul-berechtigungen-gruppen-user-grants) — EXECUTING
|
||||||
Plan: 6 of 8
|
Plan: 7 of 8
|
||||||
Status: Ready to execute
|
Status: Ready to execute
|
||||||
Last activity: 2026-08-04 — Phase 15 execution started
|
Last activity: 2026-08-04 — Phase 15 execution started
|
||||||
|
|
||||||
Progress: [██████████] 95%
|
Progress: [██████████] 96%
|
||||||
|
|
||||||
## Performance Metrics
|
## Performance Metrics
|
||||||
|
|
||||||
@@ -105,6 +105,7 @@ Progress: [██████████] 95%
|
|||||||
| Phase 15 P05 | 9min | 2 tasks | 5 files |
|
| Phase 15 P05 | 9min | 2 tasks | 5 files |
|
||||||
| Phase 15 P03 | 32min | 3 tasks | 8 files |
|
| Phase 15 P03 | 32min | 3 tasks | 8 files |
|
||||||
| Phase 15 P06 | 35min | 3 tasks | 8 files |
|
| Phase 15 P06 | 35min | 3 tasks | 8 files |
|
||||||
|
| Phase 15 P07 | 30min | 3 tasks | 7 files |
|
||||||
|
|
||||||
## Accumulated Context
|
## Accumulated Context
|
||||||
|
|
||||||
@@ -247,6 +248,9 @@ Recent decisions affecting current work:
|
|||||||
- [Phase ?]: [15-06]: Task 2/3-Split von page.tsx haelt jeden Task-Commit fuer sich buildbar (Task 2 ohne Import der erst in Task 3 entstehenden Komponenten)
|
- [Phase ?]: [15-06]: Task 2/3-Split von page.tsx haelt jeden Task-Commit fuer sich buildbar (Task 2 ohne Import der erst in Task 3 entstehenden Komponenten)
|
||||||
- [Phase ?]: [15-06]: Gruppen-Erstellung mit sofortiger AD-Bindung laeuft zweistufig (POST /groups, dann PATCH ldapDn), weil CreateGroupDto aus 15-02 nur name entgegennimmt
|
- [Phase ?]: [15-06]: Gruppen-Erstellung mit sofortiger AD-Bindung laeuft zweistufig (POST /groups, dann PATCH ldapDn), weil CreateGroupDto aus 15-02 nur name entgegennimmt
|
||||||
- [Phase ?]: [15-06]: matrixCheckboxLabel/directCheckboxLabel (Wave-4-Schluessel) als next-intl-ICU-select mit granted-Parameter modelliert, ein Schluessel bedient freigeben/entziehen
|
- [Phase ?]: [15-06]: matrixCheckboxLabel/directCheckboxLabel (Wave-4-Schluessel) als next-intl-ICU-select mit granted-Parameter modelliert, ein Schluessel bedient freigeben/entziehen
|
||||||
|
- [Phase ?]: [15-07]: aria-label des Grant-Checkboxes beschreibt die vom Klick ausgeloeste Aktion (granted: String(!isGranted)), nicht den aktuellen Haekchen-Zustand
|
||||||
|
- [Phase ?]: [15-07]: UserAccessModal leitet Gruppenmitgliedschafts-Chips ausschliesslich aus der Vereinigung aller viaGroups-Namen von GET /module-grants/users/:userId ab, kein zweiter Endpoint
|
||||||
|
- [Phase ?]: [15-07]: ActivateModuleDialog ruft onSuccess bereits nach dem erfolgreichen activate-Call auf, unabhaengig vom Ausgang des nachfolgenden module-grants-Calls
|
||||||
|
|
||||||
### Pending Todos
|
### Pending Todos
|
||||||
|
|
||||||
@@ -289,7 +293,7 @@ Items acknowledged and carried forward from previous milestone close:
|
|||||||
|
|
||||||
## Session Continuity
|
## Session Continuity
|
||||||
|
|
||||||
Last session: 2026-08-04T17:06:12.218Z
|
Last session: 2026-08-04T17:28:00.568Z
|
||||||
Stopped at: Completed 15-06-PLAN.md
|
Stopped at: Completed 15-07-PLAN.md
|
||||||
Resume file: None
|
Resume file: None
|
||||||
Last activity: 2026-07-14 - Built LDAP per-user exclude/denylist filter (9d1323f), migration applied on live DB, verified via Playwright: sync deactivated 4 excluded service accounts (administrator/krbtgt/guest/dns-ldap), 2 real LDAP users stay active, 0 wrongly created
|
Last activity: 2026-07-14 - Built LDAP per-user exclude/denylist filter (9d1323f), migration applied on live DB, verified via Playwright: sync deactivated 4 excluded service accounts (administrator/krbtgt/guest/dns-ldap), 2 real LDAP users stay active, 0 wrongly created
|
||||||
|
|||||||
+16
-3
@@ -1,10 +1,10 @@
|
|||||||
---
|
---
|
||||||
schema_version: 1
|
schema_version: 1
|
||||||
open_count: 1
|
open_count: 2
|
||||||
waived_count: 0
|
waived_count: 0
|
||||||
fixed_count: 0
|
fixed_count: 0
|
||||||
total_count: 1
|
total_count: 2
|
||||||
last_updated: 2026-08-04T17:05:42.026Z
|
last_updated: 2026-08-04T17:27:42.267Z
|
||||||
---
|
---
|
||||||
|
|
||||||
# Broken Windows Ledger
|
# Broken Windows Ledger
|
||||||
@@ -16,6 +16,7 @@ last_updated: 2026-08-04T17:05:42.026Z
|
|||||||
| id | phase | kind | file | line | description | status | reason | recorded_at | resolved_at |
|
| id | phase | kind | file | line | description | status | reason | recorded_at | resolved_at |
|
||||||
|----|-------|------|------|------|-------------|--------|--------|-------------|-------------|
|
|----|-------|------|------|------|-------------|--------|--------|-------------|-------------|
|
||||||
| 1 | 15 | unrun-verify | .planning/phases/15-modul-berechtigungen-gruppen-user-grants/15-06-PLAN.md | | 15-06 <verification>: manueller Browser-Durchklick (Anlegen/Umbenennen/Standardmarkierung/AD-Bindung/Mitglieder/Loeschdialog + Fehlerpfade bei abgeschalteter API) nicht ausgefuehrt — kein Browser-Tool in dieser Session verfuegbar | open | | 2026-08-04T17:05:42.026Z | |
|
| 1 | 15 | unrun-verify | .planning/phases/15-modul-berechtigungen-gruppen-user-grants/15-06-PLAN.md | | 15-06 <verification>: manueller Browser-Durchklick (Anlegen/Umbenennen/Standardmarkierung/AD-Bindung/Mitglieder/Loeschdialog + Fehlerpfade bei abgeschalteter API) nicht ausgefuehrt — kein Browser-Tool in dieser Session verfuegbar | open | | 2026-08-04T17:05:42.026Z | |
|
||||||
|
| 2 | 15 | unrun-verify | .planning/phases/15-modul-berechtigungen-gruppen-user-grants/15-07-PLAN.md | | Manueller Browser-Durchklick aus dem Plan-Verification-Block (Matrix-Freigabe setzen/entziehen, Aktivierungsdialog beide Wege, Direkt-Grant neben Gruppen-Grant, Fehlerfall bei gestoppter API, lange Namen) nicht ausgefuehrt -- kein Browser-Tool in dieser Session (15-07-SUMMARY.md D4) | open | | 2026-08-04T17:27:42.267Z | |
|
||||||
|
|
||||||
````json
|
````json
|
||||||
[
|
[
|
||||||
@@ -30,6 +31,18 @@ last_updated: 2026-08-04T17:05:42.026Z
|
|||||||
"reason": "",
|
"reason": "",
|
||||||
"recorded_at": "2026-08-04T17:05:42.026Z",
|
"recorded_at": "2026-08-04T17:05:42.026Z",
|
||||||
"resolved_at": null
|
"resolved_at": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"kind": "unrun-verify",
|
||||||
|
"phase": "15",
|
||||||
|
"file": ".planning/phases/15-modul-berechtigungen-gruppen-user-grants/15-07-PLAN.md",
|
||||||
|
"line": null,
|
||||||
|
"description": "Manueller Browser-Durchklick aus dem Plan-Verification-Block (Matrix-Freigabe setzen/entziehen, Aktivierungsdialog beide Wege, Direkt-Grant neben Gruppen-Grant, Fehlerfall bei gestoppter API, lange Namen) nicht ausgefuehrt -- kein Browser-Tool in dieser Session (15-07-SUMMARY.md D4)",
|
||||||
|
"status": "open",
|
||||||
|
"reason": "",
|
||||||
|
"recorded_at": "2026-08-04T17:27:42.267Z",
|
||||||
|
"resolved_at": null
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
````
|
````
|
||||||
|
|||||||
@@ -0,0 +1,173 @@
|
|||||||
|
---
|
||||||
|
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.
|
||||||
Reference in New Issue
Block a user