Skip to content

feat(directory): Phase 2 — Roles + Entitlements + Comments#412

Open
larsgeorge-db wants to merge 1 commit into
feat/directory-frontendfrom
feat/directory-phase2
Open

feat(directory): Phase 2 — Roles + Entitlements + Comments#412
larsgeorge-db wants to merge 1 commit into
feat/directory-frontendfrom
feat/directory-phase2

Conversation

@larsgeorge-db
Copy link
Copy Markdown
Collaborator

Phase 2 of the directory-lookup-and-principal-picker plan (#375). Stacked on #407 — base is feat/directory-frontend so the diff here is Phase-2 only. Will rebase onto main once #406 and #407 land.

Plan: #375
Backend: #406
Phase 1 frontend: #407

Summary

Three multi-principal RBAC surfaces migrated to PrincipalPicker. No backend changes — every existing endpoint accepts the same payload shape it already did.

  • Roles form (role-form-dialog.tsx): assigned_groups comma-separated Input → PrincipalPicker(multi, groups). Legacy comma-string records still rehydrate via the new normaliseAssignedGroups helper.
  • Entitlements page (entitlements.tsx): persona-group "add" Select with a hard-coded 14-name demo list → PrincipalPicker(multi, groups). The picker renders its own selected-badges so the separate Badge row was dropped. PUT /api/entitlements/personas/{id}/groups payload unchanged.
  • Comment composer (comment-sidebar.tsx): audience gains PrincipalPicker(multi, users+groups) alongside (not replacing) the existing Teams + App Roles checkbox lists. Picked users emit as plain emails, picked groups as plain group names; team:* / role:* tokens flow through unchanged. The round-trip is extracted into buildAudienceTokens / parseAudienceTokens.

Helpers added

  • lib/audience.tsparseAudienceTokens(audience) / buildAudienceTokens(parsed) for the mixed-token round-trip.
  • lib/assigned-groups.tsnormaliseAssignedGroups(raw) tolerates both string[] and legacy comma-string shapes.

Test plan

  • Type-check clean (yarn type-check).
  • Lint clean on touched files (0 errors; only pre-existing repo-wide warnings remain).
  • Full frontend suite: 562 passed, 6 skipped, 0 failed.
  • New tests (12):
    • lib/audience.test.ts (6): split team:/role:/principal mix, defensive guards for null/empty/non-string, prefix-only tokens preserve their empty id, round-trip identity.
    • lib/assigned-groups.test.ts (6): array passthrough, empty-string drop, comma-split + trim, trailing/repeated commas, nullish / non-string-non-array → [], empty string → [].
  • Per-page form-dialog tests with the picker rendered inside Radix dialogs remain deferred to Playwright E2E — matches the existing skip convention in this repo (role-form-dialog.test, team-form-dialog.test, project-form-dialog.test, tag-selector.test all skipped for the same Radix-in-jsdom reason). The new helpers cover the load-bearing data transforms.
  • Manual smoke test of all three surfaces in both configured + unconfigured modes (left for reviewer / follow-up).

Acceptance criteria from the plan

  • Roles: admin can add/remove groups from assigned_groups via the picker; group-membership-based authorization unchanged (no backend changes).
  • Roles: pre-existing comma-string assigned_groups records render and save without breakage (covered by normaliseAssignedGroups).
  • Entitlements: a persona's group list can be added to / removed from via the picker; same PUT body shape.
  • Comments: mixed audience (users + groups + team:* + role:*) round-trips correctly; existing notification fan-out for team/role tokens unaffected (no backend changes; existing parsing in comment renderer preserved).
  • Picker tooltips on selected badges expose the underlying email / GUID (carried over from Phase 1 picker).

@larsgeorge-db larsgeorge-db requested a review from a team as a code owner May 21, 2026 12:34
Stacks on Phase 1 frontend (#407). Migrates the multi-principal RBAC
surfaces to PrincipalPicker per plan #375.

UI changes:
- settings/role-form-dialog.tsx — assigned_groups text-with-commas
  Input replaced with PrincipalPicker(multi, groups). Legacy
  comma-separated strings still rehydrate via the new
  normaliseAssignedGroups helper.
- views/entitlements.tsx — Persona group-assignment "add" Select
  with hard-coded demo list replaced with PrincipalPicker(multi,
  groups). The picker renders the current selection as its own
  badges, so the separate Badge row is gone. Backend
  PUT /api/entitlements/personas/{id}/groups accepts the same
  List[str] payload.
- comments/comment-sidebar.tsx — Audience field gains a
  PrincipalPicker(multi, users+groups) alongside (not replacing)
  the existing Teams / App Roles checkbox lists. Picked users emit
  as plain emails, picked groups as plain group names; existing
  team:* / role:* tokens flow through unchanged. parseAudienceTokens
  + buildAudienceTokens helpers extracted for round-trip clarity.

Helpers (pure functions, fully tested):
- lib/audience.ts — parseAudienceTokens / buildAudienceTokens.
- lib/assigned-groups.ts — normaliseAssignedGroups.

Tests (12 new, 562 total passing):
- lib/audience.test.ts (6): mixed audience round-trip, defensive
  guards (null / empty / non-string), empty-id passthrough for
  prefix-only tokens.
- lib/assigned-groups.test.ts (6): array passthrough, comma-string
  split + trim, empty / nullish / non-string-non-array → []
  fallback.

Per-page form tests with the picker rendered inside Radix dialogs
remain deferred to Playwright E2E (matches the existing convention
in this repo: role-form-dialog.test, team-form-dialog.test, and
project-form-dialog.test are all skipped for the same Radix-in-jsdom
reason). The new helpers cover the load-bearing data transforms.

No backend changes -- existing PUT/POST endpoints accept the same
payload shapes they already did.
larsgeorge-db added a commit that referenced this pull request May 21, 2026
…r scope

Documents what shipped under PRs #406 / #407 / #412 / #413 / #416 / #417:

- Renames the integration's manager / routes / settings keys in the
  PRD to match the implementation (Directory layer, /api/directory/*,
  DIRECTORY_* settings, Settings → Directory tab).
- Documents the DirectoryProvider interface and the
  (DirectoryProviderContext, DirectoryProviderConfig) factory
  signature so future provider plug-ins know what to implement.
- Documents the v1 provider set, which expanded during planning
  from Entra-only to entra + lakebase + file. The Lakebase table
  schema and CSV format are included so operators have a single
  reference.
- Preserves story content, the disambiguation rule, both picker
  modes, storage-compatibility guarantees, and graceful-degradation
  rules from the PRD body unchanged.
- Re-confirms the out-of-scope list (Okta/Ping, service principals,
  OBO, profile photos, manager hierarchy, role/team Select replacement,
  CSV bulk import) which the abstraction makes cheap to revisit.
larsgeorge-db added a commit that referenced this pull request May 21, 2026
…r scope

Documents what shipped under PRs #406 / #407 / #412 / #413 / #416 / #417:

- Renames the integration's manager / routes / settings keys in the
  PRD to match the implementation (Directory layer, /api/directory/*,
  DIRECTORY_* settings, Settings → Directory tab).
- Documents the DirectoryProvider interface and the
  (DirectoryProviderContext, DirectoryProviderConfig) factory
  signature so future provider plug-ins know what to implement.
- Documents the v1 provider set, which expanded during planning
  from Entra-only to entra + lakebase + file. The Lakebase table
  schema and CSV format are included so operators have a single
  reference.
- Preserves story content, the disambiguation rule, both picker
  modes, storage-compatibility guarantees, and graceful-degradation
  rules from the PRD body unchanged.
- Re-confirms the out-of-scope list (Okta/Ping, service principals,
  OBO, profile photos, manager hierarchy, role/team Select replacement,
  CSV bulk import) which the abstraction makes cheap to revisit.
@larsgeorge-db larsgeorge-db force-pushed the feat/directory-frontend branch from 398400c to 23fd4f7 Compare May 21, 2026 21:35
@larsgeorge-db larsgeorge-db force-pushed the feat/directory-phase2 branch from 6778b9a to 58eaf6b Compare May 21, 2026 21:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant