feat(web): port settings and admin surfaces into the SPA (Phase P4-2) #1434

Merged
fred merged 1 commits from feat/webui-p4-2-settings-admin into next 2026-08-27 00:03:39 +00:00
Collaborator

Phase P increment P4-2: ports the two remaining dashboard surfaces into the SPA.

  • spa/pages/settings.tsx — faithful port of the legacy settings page (profile / appearance / notifications / providers tabs, SSO provider section reused from the shared components tree).
  • spa/pages/admin.tsx — faithful port of the legacy admin page (user management: list/create/promote/ban/delete; system health dashboard).
  • AdminGuard added to spa/guards.tsx as the route-level equivalent of the legacy AdminRoleGuard (unauthenticated → /login, non-admin → /).
  • routes.tsx: /settings and /admin placeholders replaced with the real pages; /admin nested under AdminGuard.
  • New specs: 11 tests covering guard redirects, user listing, error+retry, ban endpoint wiring, health tab, profile save via authClient, preference save wiring, provider listing + connection test.

Evidence: apps/web vitest 292/292 (27 files), typecheck 0 errors, eslint clean. Legacy app/ tree untouched — removal remains P5 per docs/webui/PHASE-P-STRUCTURE.md.

Base: next.

Phase P increment P4-2: ports the two remaining dashboard surfaces into the SPA. - spa/pages/settings.tsx — faithful port of the legacy settings page (profile / appearance / notifications / providers tabs, SSO provider section reused from the shared components tree). - spa/pages/admin.tsx — faithful port of the legacy admin page (user management: list/create/promote/ban/delete; system health dashboard). - AdminGuard added to spa/guards.tsx as the route-level equivalent of the legacy AdminRoleGuard (unauthenticated → /login, non-admin → /). - routes.tsx: /settings and /admin placeholders replaced with the real pages; /admin nested under AdminGuard. - New specs: 11 tests covering guard redirects, user listing, error+retry, ban endpoint wiring, health tab, profile save via authClient, preference save wiring, provider listing + connection test. Evidence: apps/web vitest 292/292 (27 files), typecheck 0 errors, eslint clean. Legacy app/ tree untouched — removal remains P5 per docs/webui/PHASE-P-STRUCTURE.md. Base: next.
fred added 1 commit 2026-08-26 22:56:32 +00:00
Ports the legacy Next settings page (profile/appearance/notifications/
providers tabs) and admin page (user management + system health) into
spa/pages as faithful ports, replaces the /settings and /admin route
placeholders, and adds a route-level AdminGuard mirroring the legacy
AdminRoleGuard semantics (unauthenticated -> /login, non-admin -> /).
Legacy app/ tree untouched; removal stays in P5 per the increment map.
rev-code-01 approved these changes 2026-08-26 23:06:40 +00:00
rev-code-01 left a comment
Member

Code review by rev-code-01, pinned to 2f41bb5f (author fred; Gate-16 clean). Independent verification of the lane verdict (fleet/lanes/webui-audit/findings/pr1434-review.md) — and this review adds the red-side evidence the lane explicitly could not produce under its read-only constraint.

Port fidelity, re-measured by my own diffs (fred's worktree read-only honored; ran in my own detached worktree at the same head):

  • settings: after normalizing the declared deltas ('use client' strip, export-default→export, blank-line removal), ZERO non-blank content lines differ from the legacy page.
  • admin: the only deltas are the declared wrapper changes ('use client', AdminRoleGuard import/wrapper removal, AdminContent merged into a named export) plus a placement comment. Endpoints unchanged in both files.

Gates, run by me at this head: vitest 292/292 (27 files), lint clean, typecheck exit 0. Environment note for reproducibility: typecheck on a FRESH worktree with unbuilt workspace deps fails with 23 errors in spa/chat files — control on origin/next fails IDENTICALLY (same files, zero diff), and building the workspace (turbo run build --filter=@mosaicstack/web...) restores exit 0. So the fresh-worktree red is build state, not the tree; anyone re-verifying must build deps first or they will see a spurious red.

Red controls (the lane's stated gap — both run by me, both discriminate):

  • Mutating AdminGuard's non-admin redirect target → admin.spec.tsx fails (guard redirect pinned).
  • Mutating the ban endpoint construction ('ban'→'banned' in the ternary) → admin.spec.tsx fails on the exact toHaveBeenCalledWith pin. My first attempt at this control was a silent no-op sed against a literal that does not exist (the endpoint is built by ternary) — worth recording as the trap: verify the mutation landed before concluding a pin is weak.
  • Zero-diff restores green after each (final restore: 62/62 across all 9 page spec files).

Shared-file changes read directly: routes.tsx replaces exactly the two placeholder routes and nests /admin under AdminGuard (chat/projects/tasks routes untouched); guards.tsx is a purely additive AdminGuard matching the legacy AdminRoleGuard state table (pending→Loading, no-session→/login, non-admin→/, admin→Outlet).

The lane's two MINOR findings (freshness-layer convention divergence; untested destructive mutations — role PATCH, create, delete) are accurate, pre-date this PR in severity, and are correctly scoped as tracked follow-ups before the legacy tree retires. Nothing blocking. CI 2808 was still running at review time; merge after terminal green per your gate. Approving.

Code review by rev-code-01, pinned to 2f41bb5f (author fred; Gate-16 clean). Independent verification of the lane verdict (fleet/lanes/webui-audit/findings/pr1434-review.md) — and this review adds the red-side evidence the lane explicitly could not produce under its read-only constraint. **Port fidelity, re-measured by my own diffs** (fred's worktree read-only honored; ran in my own detached worktree at the same head): - settings: after normalizing the declared deltas ('use client' strip, export-default→export, blank-line removal), ZERO non-blank content lines differ from the legacy page. - admin: the only deltas are the declared wrapper changes ('use client', AdminRoleGuard import/wrapper removal, AdminContent merged into a named export) plus a placement comment. Endpoints unchanged in both files. **Gates, run by me at this head:** vitest 292/292 (27 files), lint clean, typecheck exit 0. Environment note for reproducibility: typecheck on a FRESH worktree with unbuilt workspace deps fails with 23 errors in spa/chat files — control on origin/next fails IDENTICALLY (same files, zero diff), and building the workspace (`turbo run build --filter=@mosaicstack/web...`) restores exit 0. So the fresh-worktree red is build state, not the tree; anyone re-verifying must build deps first or they will see a spurious red. **Red controls (the lane's stated gap — both run by me, both discriminate):** - Mutating AdminGuard's non-admin redirect target → admin.spec.tsx fails (guard redirect pinned). - Mutating the ban endpoint construction ('ban'→'banned' in the ternary) → admin.spec.tsx fails on the exact toHaveBeenCalledWith pin. My first attempt at this control was a silent no-op sed against a literal that does not exist (the endpoint is built by ternary) — worth recording as the trap: verify the mutation landed before concluding a pin is weak. - Zero-diff restores green after each (final restore: 62/62 across all 9 page spec files). Shared-file changes read directly: routes.tsx replaces exactly the two placeholder routes and nests /admin under AdminGuard (chat/projects/tasks routes untouched); guards.tsx is a purely additive AdminGuard matching the legacy AdminRoleGuard state table (pending→Loading, no-session→/login, non-admin→/, admin→Outlet). The lane's two MINOR findings (freshness-layer convention divergence; untested destructive mutations — role PATCH, create, delete) are accurate, pre-date this PR in severity, and are correctly scoped as tracked follow-ups before the legacy tree retires. Nothing blocking. CI 2808 was still running at review time; merge after terminal green per your gate. Approving.
fred merged commit f8f8f97be7 into next 2026-08-27 00:03:39 +00:00
Sign in to join this conversation.