Skip to content

feat(admin): design unification with the dashboard app [WIP]#1268

Merged
iammukeshm merged 16 commits into
mainfrom
feat/admin-dashboard-unification
May 27, 2026
Merged

feat(admin): design unification with the dashboard app [WIP]#1268
iammukeshm merged 16 commits into
mainfrom
feat/admin-dashboard-unification

Conversation

@iammukeshm
Copy link
Copy Markdown
Member

WIP — tracking PR for the admin → dashboard design unification program.

Reskins the operator app (clients/admin) to fully adopt the tenant app's
(clients/dashboard) design system — tokens, components, layout chrome, and
page structures — so the two apps share one visual language. Approach: copy/
mirror
(two independent apps that look identical).

Spec: docs/superpowers/specs/2026-05-28-admin-dashboard-design-unification-design.md

Phases (each built/linted/type-checked + eyeballed before the next)

  • 1 — Tokens & global styles — port the dashboard's globals.css; reconcile token names; retire the // CONSOLE chrome + grid texture.
  • 2 — UI primitives + form layout — align ui/* (add avatar/dropdown-menu/switch), replace FormShell/FormSection with SettingsSection + Field.
  • 3 — App shell — rebuild sidebar/topbar on the dashboard shell.
  • 4 — Pages — settings (+ presigned avatar), roles/users, tenants, billing/webhooks/audits/notifications/health/impersonation, auth/login/overview.

Notes

  • Visual/structural reskin only — no route, permission, or data-layer changes.
  • Admin's console identity (masthead, \ markers, chartreuse signal) is retired by explicit owner decision.
  • The avatar presigned-upload fix (data-URL vs 2048-cap bug) lands in Phase 4 settings.
  • Touches clients/admin/** only; unrelated in-flight chat/realtime work in the tree is never staged here.

🤖 Generated with Claude Code

iammukeshm and others added 3 commits May 28, 2026 03:44
Whole-program spec for reskinning the admin app onto the dashboard's design
system (Approach A: copy/mirror). Covers end state, token reconciliation,
component + page inventory, and a 4-phase plan (tokens → primitives → shell →
pages), each phase verified before the next. Avatar presigned upload lands as
part of Phase 4 settings.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
AppHub.OnConnectedAsync only joins the channel groups a user already
belonged to at connect time, so a DM/channel created — or a membership
granted — after the socket is live never received channel:{id} broadcasts
until a page reload. The recipient saw nothing until refresh.

- AppHub: add membership-gated, idempotent JoinChannel(Guid) hub method
- dashboard chat-page: invoke JoinChannel on channel open + on reconnect
- FindOrCreateDm: push ChatChannelAdded to other participants' user:{id}
  group so a new DM appears in their channel rail without a refresh
- channel-rail: handle ChatChannelAdded (was pre-registered but unhandled)
- tests: JoinChannelTests (late-joiner receives; non-member rejected)
- docs: realtime.md group-join model note

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace admin's "Console" token system with the dashboard's (palette,
surfaces, shadows, radii, motion, base atmosphere/paper-grain) and switch
fonts to Figtree / Outfit / JetBrains Mono. A temporary LEGACY ADMIN SHIM
keeps the old console tokens (--accent-signal, --shadow-card*, --grid-alpha,
…) and utility classes (.meta, .section-rule, .card-shell, .code-chip,
.canvas-grid/mesh, .caret, .mono-tone*, …) resolving against the new palette
so unmigrated pages don't break; the shim is deleted as Phases 2–4 migrate
its consumers.

Part of the admin → dashboard design unification (spec + PR #1268).
Build: admin `npm run build` ✓.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@iammukeshm
Copy link
Copy Markdown
Member Author

Phase 1 — tokens & global styles ✅ (0d0d8e67)

  • clients/admin/src/styles/globals.css is now the dashboard's token system (palette, surfaces, shadows, radii, motion, base atmosphere/paper-grain).
  • Fonts switched to Figtree / Outfit / JetBrains Mono (admin index.html).
  • A temporary LEGACY ADMIN SHIM keeps old console tokens + utility classes (.meta, .section-rule, .card-shell, .code-chip, .canvas-grid, …) resolving against the new palette so unmigrated pages don't break; removed incrementally in Phases 2–4.
  • npm run build ✓.

Next: Phase 2 — UI primitives + form layout.

iammukeshm and others added 11 commits May 28, 2026 04:05
… upload

Bring the dashboard's component vocabulary into admin:
- ui/: add avatar, dropdown-menu, switch; align button/input/badge/card/
  dialog/label/skeleton/table to the dashboard versions (admin-only variants
  like button `signal` and badge `muted` preserved; `signal` now maps to the
  unified rose primary).
- list/: add SettingsSection, Field/SettingsField, EntityPageHeader, ToneIconTile
  (the dashboard's header-bar section + field-grid vocabulary). Existing
  FormShell/FormSection kept until their consumers migrate in Phase 4.
- file/: port the presigned ImageInput + use-file-upload hook + api/files
  client (replaces admin's broken data-URL avatar upload in Phase 4 settings).

Part of the admin → dashboard design unification (PR #1268).
Build: admin `npm run build` ✓, `eslint` ✓ (0 errors).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Retire the "Console" shell identity and adopt the dashboard's shell:
- sidebar: collapsible accordion (220/52px), persisted, active brand bar,
  collapsed hover tooltips; same nav destinations/icons/permission guards
  (numeric 01..09 prefixes dropped). nav-items restructured into
  sections/top/bottom; SidebarNavBody shared with mobile nav.
- mobile-nav: dashboard's Sheet-based MobileNavProvider/Trigger, auto-closes
  on route change, reuses the shared nav body.
- topbar: MobileNav trigger → TenantChip (static success dot, no caret) →
  NotificationBell → user dropdown (theme light/dark, Profile, Settings,
  destructive Sign-out w/ confirm). Standalone theme toggle + settings link
  folded into the dropdown; mono breadcrumb retired.
- app-shell: drop the chartreuse vignette + gridded backdrop; impersonation
  banner slot noted for later.
- brand-mark: gradient-square "F" + "fullstackhero / Admin" wordmark; the
  chartreuse FSH/admin lockup retired.

Part of the admin → dashboard design unification (PR #1268).
Build: admin `npm run build` ✓, `eslint` ✓ (0 errors).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three dashboard chat UX fixes:

- Hover action rail now tucks into the margin immediately beside the
  bubble instead of drifting to the far pane edge (own messages sit
  right → rail tucks left; others → right).
- Emoji picker is a portaled DropdownMenu anchored to the React button,
  so it can't be clipped or have its clicks stolen by the next
  virtualized message row (it was overflowing its row, and the row below
  intercepted the clicks and surfaced its own hover toolbar).
- Pinned messages get a discoverable "N pinned" bar under the channel
  header that opens the pinned list and jumps to a message — replaces the
  unlabelled header pin glyph. Inline PINNED tag + ring unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Restyle every admin page onto the dashboard's section/field/page-header
vocabulary (EntityPageHeader, SettingsSection, Field, list cards), retiring the
FormShell/FormSection 18rem rail, `\ SECTION` markers, and console classes:
- settings/* (profile now uses the presigned ImageInput — fixes the data-URL
  avatar bug), roles/*, users/*, tenants/* (list now matches the users/roles
  card-table + mobile-card pattern), billing/*, webhooks/*, audits/*,
  notifications, health, impersonation, auth/*, login, not-found, dashboard.

Also re-applies the PR #1267 fixes that this branch (cut from main) was missing:
- listRoles() unwraps the paged response (fixes the users/roles `.map` crash).
- /health added to the Vite dev proxy.
- tenant detail treats a 404 provisioning status as a neutral "Not tracked"
  state (no retry/poll storm) instead of a red FAILURE.

Part of the admin → dashboard design unification (PR #1268).
Build: admin `npm run build` ✓, `eslint` ✓ (0 errors).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- login: replace the inline dev callout with a dashboard-style demo-account
  popup dialog (pick → fills tenant/email/password → instant sign-in, gated on
  import.meta.env.DEV), and adopt the dashboard's AuthShell logo/header
  treatment. Existing login behavior preserved.
- tenants/detail: full modern pass — Overview hero, InfoRow key/value rows
  (replacing the old dl/DetailRow), and a proper provisioning step timeline
  with status nodes. The 404 "Not tracked" provisioning logic is kept intact.

Part of the admin → dashboard design unification (PR #1268).
Build: admin `npm run build` ✓, `eslint` ✓ (0 errors).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the full /tenants/new page with a CreateTenantDialog launched from the
Tenants list "New tenant" button (same fields, validation, operator-supplied
admin password, and createTenant mutation; on success closes, refreshes the
list, and routes to the new tenant). /tenants/new now redirects to /tenants so
existing links don't dead-end.

Part of the admin → dashboard design unification (PR #1268).
Build: admin `npm run build` ✓, `eslint` ✓ (0 errors).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- audits: detail now opens as a side Sheet from the list (selected-row state)
  instead of a full-page route, mirroring the dashboard; /audits/:id redirects
  to /audits. Same audit query, sections, and fields preserved.
- ui/select: new reusable modern single-select built on the DropdownMenu
  primitive (no new deps) — rounded trigger + chevron, rose-tinted active item.
  Replaces the raw native <select> filters on the impersonation list (status)
  and users list (role); filter state/behavior unchanged.

Part of the admin → dashboard design unification (PR #1268).
Build: admin `npm run build` ✓, `eslint` ✓ (0 errors).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- tenant-branding-card + active-grants-card: retire the console `\ BRANDING` /
  `// LIGHT PREVIEW` rail treatment; wrap in SettingsSection, modern rounded
  swatch grids, Field inputs, polished preview. All branding/palette/grant
  mutations and polling preserved.
- settings/layout: adopt the dashboard's editorial numbered left-nav shell
  (260px rail, active brand bar, "Settings · {section}" masthead, mobile pill
  tabs). Admin's actual tabs (Profile/Security/Sessions/Appearance) preserved.

Part of the admin → dashboard design unification (PR #1268).
Build: admin `npm run build` ✓, `eslint` ✓ (0 errors).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the native <details>/<summary> check rows with a controlled useState
toggle, and only render the expand affordance (chevron + click) when a check
actually reports details. Fixes rows appearing unresponsive.

Part of the admin → dashboard design unification (PR #1268).
Build: admin `npm run build` ✓.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirror the new-tenant dialog: user and role creation now open well-designed
popup dialogs from their list "New …" buttons instead of separate pages.
Same fields/validation/mutations (registerUser, upsertRole); on success close,
refresh the list query, and route to the created entity. /users/new and
/roles/new redirect to their lists.

Part of the admin → dashboard design unification (PR #1268).
Build: admin `npm run build` ✓, `eslint` ✓ (0 errors).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…sole" → "Dashboard"

- impersonation-banner: drop the radial-gradient tone wash and the icon
  square's linear-gradient; keep the solid muted background, tone border, and
  cross-tenant left ribbon.
- sidebar/mobile-nav: the logo sub-label and footer now read "Dashboard"
  instead of "Console".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@iammukeshm
Copy link
Copy Markdown
Member Author

Unification complete — all 4 phases + refinements landed.

Phase Status
1 — tokens & global styles 0d0d8e67
2 — UI primitives + form layout + presigned upload 17183bfc
3 — app shell 5c1ce68b
4 — all pages migrated (+ re-applied PR #1267 fixes) 064bbe3e

Refinements on top (each built + linted green):

  • Login demo-account dialog + AuthShell logo header
  • Tenant detail modernized (timeline) + tenant cards (branding/grants) de-consoled
  • New-tenant, new-user, new-role creation as modern dialogs (old /…/new routes redirect)
  • Audit detail as a side sheet
  • Modern ui/select replacing native <select> filters
  • Settings layout → dashboard's numbered left-nav
  • Health check rows expand reliably (controlled disclosure)
  • Dashboard tweaks: impersonation banner gradient flattened, sidebar "Console" → "Dashboard"

npm run build ✓ and eslint ✓ (0 errors) on every commit.

Note for merging: this branch was cut from main before #1267 merged, so it re-applies those same fixes (roles unwrap, /health proxy, provisioning 404). Sequencing #1267 then #1268 (or vice-versa) is conflict-free since the changes are identical. The legacy-shim block in globals.css can be trimmed in a later pass once nothing references the old console utilities.

iammukeshm and others added 2 commits May 28, 2026 05:07
…unification

# Conflicts:
#	clients/admin/src/pages/tenants/detail.tsx
#	clients/admin/src/styles/globals.css
Deactivating a tenant only flipped AppTenantInfo.IsActive in the store —
nothing in the auth or request pipeline checked it, so a deactivated
tenant's users could still log in and call the API.

- Add a post-auth guard in MultitenancyModule.ConfigureMiddleware that
  403s any request whose resolved tenant is non-root and inactive. It runs
  before every endpoint, so it covers the anonymous login/refresh requests
  too. Operators (JWT tenant claim == root) are exempt so they can still
  manage/reactivate deactivated tenants cross-tenant.
- Refresh the distributed-cache store on activate/deactivate. Finbuckle
  resolves tenants from a 60-min cache, and TenantService only wrote the
  EF store, so the guard would otherwise keep seeing the stale "active"
  copy for up to an hour.
- Add the missing regression test: TenantActivationTests only asserted the
  activation endpoint returned 200, never that access was denied. New test
  asserts a login attempt is not-403 while active and 403 once deactivated.

Scope: enforces IsActive (deactivation). ValidUpto/subscription-expiry is
intentionally not enforced here to avoid surprise lockouts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@iammukeshm iammukeshm merged commit 60e795d into main May 27, 2026
11 of 12 checks passed
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