feat(admin): design unification with the dashboard app [WIP]#1268
Merged
Conversation
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>
Member
Author
|
Phase 1 — tokens & global styles ✅ (
Next: Phase 2 — UI primitives + form layout. |
… 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>
Member
Author
|
Unification complete — all 4 phases + refinements landed. ✅
Refinements on top (each built + linted green):
Note for merging: this branch was cut from |
…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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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, andpage 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.mdPhases (each built/linted/type-checked + eyeballed before the next)
globals.css; reconcile token names; retire the// CONSOLEchrome + grid texture.ui/*(add avatar/dropdown-menu/switch), replaceFormShell/FormSectionwithSettingsSection+Field.Notes
\markers, chartreuse signal) is retired by explicit owner decision.clients/admin/**only; unrelated in-flight chat/realtime work in the tree is never staged here.🤖 Generated with Claude Code