Files

15 KiB
Raw Permalink Blame History

phase, plan, subsystem, tags, requires, provides, affects, actuals, tech-stack, key-files, key-decisions, patterns-established, requirements-completed, coverage, duration, completed, status
phase plan subsystem tags requires provides affects actuals tech-stack key-files key-decisions patterns-established requirements-completed coverage duration completed status
16-ad-gruppen-synchronisation 04 ui
nextjs
next-intl
groups
module-grants
vitest
tailwind
phase provides
16-ad-gruppen-synchronisation (Plan 16-02) GroupsService.update() serverseitige Namenssperre fuer importierte Gruppen (BadRequestException), internalName-Feld mit Leerstring-Normalisierung auf null, UpdateGroupDto ohne ldapDn (D-07), internalName in GET /groups
GroupFormModal.tsx auf drei Zustaende zurueckgebaut (Anlegen / lokal bearbeiten / importiert bearbeiten) — kein AD-Bindungspfad mehr in der Oberflaeche (D-07)
Gesperrtes Namensfeld mit Herkunftshinweis + AD-DN-Anzeigezeile und editierbarem internen Namen fuer importierte Gruppen (D-03, D-04)
Sichtbarer, ursachenunterscheidender Speicherfehler im Dialog (409 vs. generisch) statt stummem catch{}
Group.internalName im Frontend-Typ; Gruppenliste zeigt internalName ?? name mit AD-Name als Tooltip (D-04)
Sechs verwaiste admin.groups.ldapBind.*-Schluessel aus de.json/en.json entfernt, Schluesselparitaet erhalten
16-05-anzeige-fallback-frontend
tokens tasks commits
5732 3 3
added patterns
isImported = group?.ldapDn != null als alleiniges Unterscheidungsmerkmal lokal vs. importiert im Dialog (nicht der noch nicht in jedem Fall nachgetragene ldapObjectGuid-Identitaetsschluessel)
Ein einziger method: 'PATCH'-Aufrufblock bedient sowohl den lokalen Rename- als auch den internalName-Pfad, Payload wird vorher per isImported-Verzweigung gebaut — verhindert einen zweiten Schreibpfad im Bearbeiten-Zustand
created modified
apps/web/src/app/(portal)/admin/groups/components/GroupFormModal.tsx
apps/web/src/app/(portal)/admin/groups/page.tsx
apps/web/src/app/(portal)/admin/groups/groups-page.test.tsx
apps/web/src/messages/de.json
apps/web/src/messages/en.json
Group.internalName wurde bereits in Task 1 zum Interface in page.tsx ergaenzt (Rule 3, Blocker: GroupFormModal.tsx kompiliert sonst nicht), obwohl die Erweiterung explizit als Task-2-Auftrag im Plan stand — Task 2 fand das Feld vor und konzentrierte sich auf die Namenszelle plus Tests
Der verwaiste ldapBind-Uebersetzungsblock im next-intl-Mock der Testdatei wurde zusaetzlich zu den beiden Sprachdateien entfernt (nicht explizit im Plan gefordert, aber tote Kopie desselben Schluesselbaums) — ersetzt durch die sieben neuen Task-1-Schluessel im selben Mock
PERM-02 bleibt unveraendert auf Pending — dieser Plan schliesst laut eigener Vorgabe erst mit Plan 16-05
Sichtbarer text-sm text-destructive-Fehlerzustand im GroupFormModal fuer JEDEN Fehlerfall (Netzwerk, 409, generisch 4xx/5xx), kein stummer catch{} mehr in dieser Komponente
id description requirement verification human_judgment
D1 GroupFormModal bietet in keinem der drei Zustaende einen Weg, eine lokale Gruppe an eine AD-Gruppe zu binden — kein Radio-Steuerelement, kein Discovery-Aufruf, kein zweiter PATCH im Anlegen-Zweig (D-07) PERM-02
kind ref status
unit apps/web/src/app/(portal)/admin/groups/groups-page.test.tsx#GroupFormModal (via AdminGroupsPage) — D-07: no AD-binding path (2 Faelle) pass
kind ref status
other grep -c 'ldap/groups' GroupFormModal.tsx == 0; grep -c 'type="radio"' GroupFormModal.tsx == 0; grep -c "method: 'PATCH'" GroupFormModal.tsx == 1 pass
false
id description requirement verification human_judgment
D2 Namensfeld einer importierten Gruppe ist deaktiviert, zeigt den AD-Namen, traegt Herkunftshinweis und AD-DN-Anzeigezeile; internalName ist separat editierbar und wird ausschliesslich per PATCH { internalName } gespeichert (D-03, D-04) PERM-02
kind ref status
unit apps/web/src/app/(portal)/admin/groups/groups-page.test.tsx#GroupFormModal (via AdminGroupsPage) — locks the name field and shows an editable internal-name field for an imported group pass
false
id description requirement verification human_judgment rationale
D3 Speicherfehler wird im Dialog sichtbar angezeigt (nicht stumm verschluckt), Dialog bleibt offen, Eingaben bleiben erhalten; 409 zeigt saveErrorNameTaken statt saveError PERM-02
kind ref status
other grep -q '409' GroupFormModal.tsx; Code-Review des handleSubmit-Fehlerpfads (kein return vor onClose() im Erfolgsfall, saveError-State bleibt bei Fehler gesetzt, Formular wird nicht zurueckgesetzt) pass
true Kein automatisierter Test simuliert den 409-Response-Pfad oder das Offenbleiben des Dialogs nach einem fehlgeschlagenen Speichern in dieser Session — der manuelle Browser-Durchklick aus dem Plan-Verification-Block ist als WINDOWS.md #5 offen und deckt diesen Zustand mit ab.
id description requirement verification human_judgment
D4 Gruppenliste zeigt internalName ?? name (nullish, nicht truthiness) mit AD-Namen als title-Tooltip; Badge-Spalte unveraendert als alleiniger importiert-Marker PERM-02
kind ref status
unit apps/web/src/app/(portal)/admin/groups/groups-page.test.tsx#AdminGroupsPage — shows the internal name.../falls back to the stored name... (2 Faelle) pass
kind ref status
other grep -c 'internalName ?? group.name' page.tsx == 1; grep -c 'internalName ||' page.tsx == 0; grep -c 'boundBadge' page.tsx == 1 pass
false
id description requirement verification human_judgment
D5 Sechs verwaiste admin.groups.ldapBind.*-Schluessel sind aus de.json UND en.json entfernt; kein Quelltext in apps/web referenziert sie noch; beide Sprachdateien tragen unter admin.groups denselben Schluesselsatz PERM-02
kind ref status
other grep -c '"ldapBind"' de.json/en.json == 0; grep -rc 'ldapBind.' apps/web/src --include=*.tsx --include=*.ts == 0; node-Schluesselparitaets-Check admin.groups de vs. en pass
false
3min (Commit-Spanne 16:28:07 bis 16:30:49; Kontext-Lesen davor nicht mitgerechnet) 2026-08-06 complete

Phase 16 Plan 4: Gruppen-Dialog auf Anlegen/Umbenennen/Import-Herkunft zurueckgebaut Summary

GroupFormModal.tsx verliert restlos die AD-Radio-Auswahl und den zweistufigen Anlegen-dann-Binden-Ablauf (D-07); an ihre Stelle tritt ein gesperrtes Namensfeld mit sichtbarer AD-Herkunft plus editierbarem internem Namen fuer importierte Gruppen (D-03/D-04), ein sichtbarer statt stummer Speicherfehler mit Namenskollisions-Unterscheidung, und eine Gruppenliste, die den internen Namen mit AD-Name-Tooltip zeigt.

Performance

  • Duration: 3 min (Commit-Spanne 30affbb 16:28:07 Uhr bis e900b43 16:30:49 Uhr; Lesen von PLAN.md, 16-02-SUMMARY.md, PATTERNS.md, UI-SPEC.md und der Zieldateien davor nicht mitgerechnet)
  • Started: 2026-08-06T16:28:07+02:00 (erster Task-Commit)
  • Completed: 2026-08-06T16:30:49+02:00 (dritter Task-Commit)
  • Tasks: 3
  • Files modified: 5

Accomplishments

  • GroupFormModal.tsx auf drei klar getrennte Zustaende zurueckgebaut (Anlegen / lokal bearbeiten / importiert bearbeiten) — der komplette AD-Bindungsblock (Discovery-State, useEffect, Radio-Liste, Bindung-entfernen-Link, zweiter PATCH im Anlegen-Zweig) ist restlos entfernt; ein manipulierter Client kann eine lokale Gruppe ueber diesen Dialog nicht mehr an AD binden (D-07)
  • Importierte Gruppen zeigen ein deaktiviertes Namensfeld mit Herkunftshinweis, eine reine AD-DN:-Anzeigezeile und ein unabhaengiges, editierbares Feld "Interner Name" — gespeichert wird in diesem Zustand ausschliesslich { internalName } per PATCH, nie name (D-03, D-04)
  • Ein einziger method: 'PATCH'-Aufrufblock bedient sowohl den lokalen Rename- als auch den internalName-Pfad — die Payload wird vor dem Aufruf per isImported-Verzweigung gebaut, kein zweiter Schreibpfad
  • Speicherfehler werden nicht mehr stumm verschluckt: der bisherige catch {} ist durch einen sichtbaren text-sm text-destructive-Zustand ersetzt, der bei HTTP 409 saveErrorNameTaken statt der generischen saveError-Meldung zeigt; der Dialog bleibt in jedem Fehlerfall offen, Eingaben bleiben erhalten
  • Group.internalName im Frontend-Typ ergaenzt; die Namenszelle der Gruppenliste rendert internalName ?? name (Nullish, nicht Truthiness) mit dem AD-Namen als title-Tooltip — die Badge-Spalte bleibt unveraendert der alleinige "importiert vs. lokal"-Marker
  • Sechs verwaiste admin.groups.ldapBind.*-Schluessel aus de.json UND en.json entfernt, inklusive des toten Duplikats im Test-Uebersetzungs-Mock; Schluesselparitaet zwischen beiden Sprachen bestaetigt per Node-Check
  • Vollstaendige Web-Testsuite gruen: 32 Testdateien, 192 Tests (davon 4 neue Faelle aus diesem Plan)

Task Commits

Each task was committed atomically:

  1. Task 1: GroupFormModal auf drei Zustaende zurueckbauen (D-03, D-04, D-07) - 30affbb (feat)
  2. Task 2: Namensanzeige mit Fallback in der Gruppenliste (D-04) - 6802b48 (feat)
  3. Task 3: Verwaiste Uebersetzungsschluessel entfernen und D-07 im Test festnageln - e900b43 (test)

Plan metadata: wird mit diesem Summary committet

Note: Kein TDD-Task in diesem Plan (tdd nicht auf einem Task gesetzt) — je ein feat/feat/test-Commit.

Files Created/Modified

  • apps/web/src/app/(portal)/admin/groups/components/GroupFormModal.tsx - AD-Bindungsblock vollstaendig entfernt; drei Zustaende (Anlegen/lokal/importiert), gesperrtes Namensfeld, internalName-Feld, sichtbarer 409-unterscheidender Fehlerzustand
  • apps/web/src/app/(portal)/admin/groups/page.tsx - Group.internalName: string | null im Interface; Namenszelle nutzt internalName ?? name mit title={name}-Tooltip
  • apps/web/src/app/(portal)/admin/groups/groups-page.test.tsx - internalName im lokalen Group-Interface und in mockGroups; neue mockGroupsWithInternalName-Fixture; vier neue Testfaelle (Namensfallback + Tooltip, D-07 kein AD-Request beim Anlegen, gesperrtes Namensfeld + internes Namensfeld beim Bearbeiten einer importierten Gruppe); toter ldapBind-Block im Uebersetzungs-Mock durch die sieben neuen Task-1-Schluessel ersetzt
  • apps/web/src/messages/de.json / en.json - sieben neue admin.groups.*-Schluessel (nameLockedHint, adDnLabel, internalName, internalNameHint, createLdapHint, goToLdap, saveErrorNameTaken); sechs verwaiste ldapBind.*-Schluessel entfernt

Decisions Made

  • Group.internalName wurde bereits in Task 1 zum Group-Interface in page.tsx ergaenzt, nicht erst in Task 2 wie im Plan vorgesehen — GroupFormModal.tsx referenziert group.internalName und kompiliert ohne das Feld nicht (tsc --noEmit ist Teil von Task 1s eigenem Verify-Block). Task 2 fand das Feld bereits vor und konzentrierte sich wie geplant auf die Namenszelle und die zugehoerigen Tests.
  • Der verwaiste ldapBind-Block im next-intl-Uebersetzungs-Mock der Testdatei wurde zusaetzlich zu den beiden echten Sprachdateien entfernt — das war nicht woertlich im Plan gefordert, aber eine tote Kopie desselben Schluesselbaums, deren Entfernung im Sinne von Task 3 liegt (keine verwaisten Schluesselverweise irgendwo im Web-Verzeichnis).
  • PERM-02 bleibt in REQUIREMENTS.md unveraendert auf [ ] (Pending) — dieser Plan liefert den vierten von fuenf geplanten Bausteinen; das Requirement schliesst laut Plan-Vorgabe erst mit Plan 16-05.

Deviations from Plan

Auto-fixed Issues

1. [Rule 3 - Blocking] Group.internalName musste vor Task 2 ergaenzt werden, da GroupFormModal.tsx sonst nicht kompiliert

  • Found during: Task 1 (eigener tsc --noEmit-Verify-Schritt)
  • Issue: GroupFormModal.tsx liest group?.internalName und group.internalName (fuer den AD-DN-Block), aber das Group-Interface in page.tsx (aus dem der Typ importiert wird) hatte das Feld noch nicht — laut Plan sollte das erst in Task 2 ergaenzt werden. Ohne das Feld schlaegt tsc --noEmit bereits fuer Task 1 fehl.
  • Fix: internalName: string | null; zum Group-Interface in page.tsx als Teil des Task-1-Commits ergaenzt (identische Nullbarkeit wie ldapDn, wie vom Plan fuer Task 2 spezifiziert). Task 2 fand das Feld vor und musste es nicht erneut hinzufuegen.
  • Files modified: apps/web/src/app/(portal)/admin/groups/page.tsx
  • Verification: cd apps/web && npx tsc --noEmit fehlerfrei
  • Committed in: 30affbb (Task 1)

2. [Rule 1 - Bug] t('adDnLabel', { ldapDn: group.ldapDn }) liess sich nicht typsicher aufrufen

  • Found during: Task 1 (tsc --noEmit)
  • Issue: group.ldapDn ist string | null; die useTranslations-Interpolationsfunktion erwartet string | number | Date fuer Platzhalterwerte. Obwohl isImported an dieser Stelle logisch bereits group.ldapDn !== null garantiert, engt TypeScript den Typ ueber die separate isImported-Variable nicht ein.
  • Fix: group.ldapDn ?? '' an der Aufrufstelle — der Leerstring-Fall tritt zur Laufzeit nie ein, da der Block nur bei isImported === true gerendert wird.
  • Files modified: apps/web/src/app/(portal)/admin/groups/components/GroupFormModal.tsx
  • Verification: cd apps/web && npx tsc --noEmit fehlerfrei
  • Committed in: 30affbb (Task 1)

Total deviations: 2 auto-fixed (1× Rule 3 Blocker, 1× Rule 1 Typfehler) — beide innerhalb von Task 1 aufgetreten und behoben, kein Einfluss auf den Funktionsumfang. Impact on plan: Kein Scope Creep. Der Rule-3-Fix zieht lediglich eine im Plan fuer Task 2 vorgesehene Interface-Erweiterung um einen Task vor, weil eine harte Kompilierabhaengigkeit besteht, die der Plan nicht explizit adressiert hatte.

Issues Encountered

None.

User Setup Required

None - keine externe Service-Konfiguration erforderlich. Der laufende Docker-Stack (lokal) wurde von diesem Plan nicht angefasst; die Aenderungen liegen im Web-Quellcode und werden erst mit einem --build/--force-recreate durch den Nutzer wirksam.

Known Stubs

Keine. Beide neuen Testfaelle in groups-page.test.tsx (Namensfallback/Tooltip, D-07-Regression) laufen gegen echte Rendering-Pfade der Komponente, keine hartkodierten Platzhalterwerte.

Next Phase Readiness

  • GroupFormModal.tsx ist jetzt der endgueltige, dreizustandsfaehige Dialog fuer Plan 16-05 — kein weiterer Rueckbau an dieser Datei erwartet
  • internalName ?? name ist jetzt an allen vier geplanten Anzeigestellen implementiert: listForTenant() und beide getUserAccess()-Projektionen (16-02), sowie die Gruppenliste (dieser Plan). Verbleibend fuer Plan 16-05 laut 16-02-Summary: admin/modules/grants/page.tsx-Spaltenkopf (Surface Contract 5)
  • Der manuelle Browser-Durchklick aus dem Plan-Verification-Block (drei Dialog-Zustaende inkl. 409-Fehleranzeige, Namensanzeige mit Tooltip nach dem Speichern) ist als WINDOWS.md #5 offen — vor /gsd-ship nachzuholen, kein Browser-Tool in dieser Session verfuegbar
  • PERM-02 bleibt bewusst auf Pending — schliesst mit Plan 16-05

Phase: 16-ad-gruppen-synchronisation Completed: 2026-08-06

Self-Check: PASSED

Alle 5 in diesem Plan geaenderten Quelldateien sowie dieses Summary existieren auf der Festplatte; alle 4 Commit-Hashes (30affbb, 6802b48, e900b43, 6b33bb0) sind im lokalen Git-Log auffindbar.