Skip to main content

Admin Dashboard

The admin package is the Green Goods operator canvas. This page is the single UI contract for packages/admin.

Purpose

  • packages/admin owns canvas shell composition and admin-only workflows.
  • @green-goods/shared owns reusable UI primitives, shared config helpers, hooks, providers, stores, and Storybook-backed foundations.
  • If a pattern is reusable across admin surfaces, move it to shared instead of creating a package-local copy.

Canonical Routes

  • Primary canvas surfaces: /hub, /garden, /community, /actions
  • Hub canonical deep links: /hub/work/:workId, /hub/work/submit, /hub/assess/create, /hub/certify/:assessmentId, /hub/certify/create, /hub/history/:historyEventId
  • Secondary garden flows: /garden/create, /garden/impact/hypercerts/:hypercertId, /community/treasury/vault, /community/governance/strategies, /community/governance/signal-pool/:poolType
  • Team campaign flows: /cookies, /cookies/deploy
  • Removed admin top-level routes intentionally fall through to the admin 404 unless they are listed in the redirect policy below.

Redirect Policy

  • Canonical admin bookmarks stay inside the admin app: /hub, /garden, /community, /actions, and their documented nested routes.
  • Public garden URLs redirect to the client app: /gardens, /gardens/:gardenId, and /gardens/:gardenId/* map to the same path on greengoods.app.
  • The admin SPA keeps a client-side fallback for public garden URLs so local and static preview environments match production redirects.
  • Removed admin route families are not blanket-redirected. Add a specific destination here before preserving any legacy admin bookmark.

Shell Contract

  • CanvasLayout is the canonical shell.
  • The shell is AppBar + .workspace-canvas + MainSheet + NavigationBar.
  • AppBar is the sticky z-sticky h-14 context region with GardenChip on the left and search, notifications, settings, and profile controls on the right. The three global actions open the AdminSideSheet; settings and profile are desktop-only (on mobile those surfaces live in the Profile tab, and the bell remains).
  • NavigationBar is navigation only. The canonical items are Hub, Garden, Community, and Actions (plus the mobile-only Profile tab).
  • Controlled Chrome is the admin material boundary: subtle liquid glass is permitted on NavigationBar/FAB only. The AppBar root stays transparent so the workspace canvas tone reads behind it. Route cards, forms, tables, lists, dense content, dialogs, and the account side sheet stay solid.
  • ConnectShell is the disconnected full-screen state with a centered connect prompt and no navigation.
  • Admin owns CanvasLayout, AdminSideSheet, CommandPalette, AccountProfilePanel, AccountSettingsPanel, AccountSurface, ConnectShell, and PageHeader.
  • Account panels render AccountProfilePanel and AccountSettingsPanel as separate contents of the AdminSideSheet account inspector (routed through the right-sheet registry). AccountSurface remains the route-level tabbed account surface for mobile profile navigation — tabs Account | Settings ("Account" is the mobile name for the desktop Profile sheet content; notifications never appear as a tab).
  • Shared owns AppBar, NavigationBar, GardenChip, MainSheet, NotificationPanel, and SheetErrorBoundary. The LeftSheet/RightSheet/BottomSheet renderers are deleted — every workspace overlay uses AdminDialog (centered on desktop, with the variant-specific mobile geometry in the dialog contract); the three global AppBar surfaces are the one AdminSideSheet exception.

Preferred Primitives

  • AppBar
  • NavigationBar
  • MainSheet
  • GardenChip
  • CommandPalette
  • AccountSettingsPanel
  • ConnectShell
  • PageHeader
  • AdminBadge
  • AdminButton
  • AdminCard
  • AdminCheckbox
  • AdminDialog
  • AdminFab
  • AdminFilterChip
  • AdminLinearProgress
  • AdminListItem
  • AdminSearchToolbar
  • AdminSelectableCard
  • AdminChoiceGroup
  • AdminConfirmDialog
  • AdminSideSheet
  • AdminSortSelect
  • AdminTabRail
  • AdminTextField
  • AdminTooltip
  • AdminViewActions
  • Shared foundations where appropriate: ListToolbar, SortSelect, Card, Alert, StatusBadge, FormField

Card And Selection Grammar

  • Use WorkbenchCard for workbench records, action queue items, assessment queue items, and other records operators scan, compare, and act on in a grid or list.
  • Use AdminCard for compact modules, stats, settings, status panels, and supporting detail regions.
  • Use AdminSelectableCard for richer exclusive or multi-select choices where the option needs a title, description, icon, or metadata.
  • Use AdminChoiceGroup for compact single-select preferences and context switches inside dense panels.
  • Use AdminTabRail for route-local exclusive modes or tabs.
  • Use AdminFilterChip for compact toolbar filters.
  • Avoid adding new direct shared Card usage in admin route work unless the route is intentionally consuming an existing shared, non-admin surface.

Surface Primitives

  • .workspace-canvas is the default canvas workspace treatment.
  • .surface-section creates a rounded full-width section wrapper.
  • .surface-inset creates nested panels inside a section.
  • .surface-card creates interactive tiles.

Dialog Contract

  • Admin dashboard modals use AdminDialog for form and command-palette surfaces and AdminConfirmDialog for destructive or confirmation flows.
  • AdminDialog renders a centered M3 dialog on desktop. On mobile, standard workspace action/detail surfaces and flow creation surfaces render as full-width bottom sheets; compact confirm and palette dialogs stay inset.
  • Dialog actions belong in the pinned actions slot. Keep cancel, save, confirm, retry, and close controls outside the scrollable body.
  • Form and detail content scrolls inside the dialog body, not on the page behind the scrim.
  • Every admin dialog must expose a visible surface above the scrim, a non-zero panel rect, accessible title and description wiring, a close or cancel affordance, reduced-motion-safe behavior, and visible pinned actions when there is something to commit or cancel.
  • The command palette uses the palette variant of AdminDialog so search behavior and keyboard navigation live inside the same admin modal contract.
  • AdminDialog sizes follow a three-tier scale by action weight — sm is reserved for confirm/alert dialogs, md (the default) hosts a simple single-purpose action (one form, one concern), lg hosts richer single-view content (lists with per-item actions, multi-field or multi-column layouts). xl/2xl tiers no longer exist. A guard test (packages/admin/src/__tests__/components/AdminDialogStandard.guard.test.ts) fails any consumer passing an out-of-scale size or an ad-hoc max-w-* override.
  • Full-surface action flows (Submit Work, Create Assessment, Create Hypercert) use the flow variant of AdminDialog with size="lg" and the shared ADMIN_FLOW_DIALOG_CLASS sizing constant. The flow variant suppresses the structured header and inner padding so the shared ActionFlowShell owns the visible header + scrolling body + pinned footer; it stays a centered, scrim-backed modal on desktop and a full-width bottom sheet on mobile, not a fullscreen takeover.
  • DialogShell remains available for shared or non-admin surfaces. Do not use it for admin dashboard dialogs.
  • The three global AppBar surfaces (Profile, Settings, Notifications) are the one side-sheet exception: AdminSideSheet shares the dialog chrome (scrim, hairline header, close button, tone, instant-exit) with side-sheet geometry — right-docked within the canvas chrome bounds at ≥640px, and the compact inset bottom-sheet geometry below. Panels compose SheetBody / SheetFooter inside it (the shell adds no padding). Usage is locked to CanvasLayout and the registry to the three content ids by packages/admin/src/__tests__/components/AdminSideSheetStandard.guard.test.ts.

Dialog interior grammar

Every variant shares one chrome — the action-flow reference anatomy — so standard, confirm, and palette dialogs read as the same product as the flows:

  • Chrome. Header is a hairline-bottom bar (px-4 py-3 sm:px-6) with an optional inline icon, a text-lg semibold Title Case title, and a text-sm sentence-case description. The body is the scrollable region between header and footer. The actions slot renders as a pinned footer bar — hairline top border on --surface-raised (the same anatomy SheetFooter gives the flows). AdminDialog owns all of this; consumers never rebuild header or footer chrome.
  • One header, one footer. Interior content never repeats a heading the dialog header states (no Card.Header inside a dialog), and a non-flow dialog that passes actions must not also nest a SheetFooter. Both are guard-tested.
  • Footer Cancel exits the dialog. A footer Cancel runs the same guarded close as the X / scrim / Escape (confirm-before-discard when the form is dirty), and is disabled only while a write is in flight — it is not a form-reset, and is not disabled just because the form is pristine (a disabled-when-clean Cancel reads as broken). Save stays disabled until the form is dirty and valid.
  • Body sections separate with space-y-4/space-y-6; section eyebrows use the label-xs/label-md utilities in sentence case — no ad-hoc uppercase tracking-wide. A field label (the title that labels a control) is different: it routes through FormField (stacked field) or AdminSettingRow (inline setting row), also sentence case — never the label-xs text-text-soft eyebrow token, which reads smaller and greyer than the fields beside it. Label/value pairs render as one-line definition rows (label left in text-text-soft, value right), not stacked icon rows.
  • Two-column layouts are lg-only, using the established grids (lg:grid-cols-[minmax(0,2fr)_minmax(18rem,1fr)] or the WorkDetail 3/2 split): form/primary content left, load-bearing preview/meta right. md and mobile always collapse to one column.
  • Loading reserves final geometry. Skeletons match the rendered component's dimensions (the same card/row shapes in skeleton-shimmer, or explicit min-heights); never swap "Loading…" text into a value slot. A dialog's first paint fixes its height for the data it awaits.
  • Empty states use the shared EmptyState (icon + title + description) centered in the reserved region — never bare text stretched to fill.
  • Open-state pattern. Dialogs stay mounted with a controlled open; {cond ? <Dialog isOpen /> : null} skips the exit animation (close flash) and is forbidden. Closes that can never animate (hidden tab) are handled centrally by AdminDialog's instant-exit + the CanvasLayout close safety net.
  • Tone. Every dialog passes the tone of its mounting workspace (the portal escapes [data-tone]); accent and status color inside dialogs go through --tone-*/--m3-* indirection only, so the [data-theme="dark"] ladder controls dark variants centrally. The account inspector deliberately stays on the neutral hub tone (global chrome, not workspace content).

Admin Color Roles

  • --tone-action is for filled action backgrounds. It uses deep steps so white text clears contrast in light and dark mode.
  • --tone-on-action is the text/icon color on filled action backgrounds.
  • --tone-on-surface-accent is for colored text/icons on solid surfaces.
  • --tone-focus-ring is the only focus-ring role. It resolves to action tone in light mode and on-surface accent in dark mode so keyboard focus remains visible.
  • --m3-outline is the control-grade boundary for fields, chips, and outlined buttons.
  • --m3-outline-variant is a decorative hairline. Do not use it as the only boundary for a control.
  • Shared Canvas components consumed by admin cannot rely on arbitrary Tailwind utility classes authored in packages/shared/src; admin does not scan that package for generated utilities. Use inline token styles or admin CSS overrides for shared Canvas color/layout behaviors that must render inside admin.

Canvas UI Heuristics

  • Admin is an operator canvas. Use utility copy and operational hierarchy over marketing language.
  • Default page sequence is PageHeader, then the primary workspace, then any secondary inspector (AdminDialog, centered on desktop).
  • Start with layout and flow before introducing cards or elevated panels.
  • Cards and elevated surfaces are for discrete records, action targets, and bounded interaction zones. They are not the default page structure.
  • Prefer one dominant workspace surface per route and plain layout within it. Avoid nested stacks of rounded bordered panels.
  • Avoid hero sections, decorative promo callouts, and decorative gradients behind routine product UI.
  • Use route-tint atmosphere, transparent AppBar continuity, Controlled Chrome depth on Navigation/FAB, and high-contrast state accents to establish hierarchy without turning the UI into a promo surface.
  • Use /hub as the reference implementation for canvas composition and interaction density.
  • bun run check:design-tokens enforces the material boundary: admin source must not add glass, backdrop blur, or decorative gradients outside packages/admin/src/index.css and admin-m3-overrides.css.
  • Route content, canvas tone changes, FAB menus, sheets, and interaction state use the admin motion role aliases (--admin-motion-*) layered on top of the canonical --spring-* tokens.

Unified Canvas Route Contract

  • Every primary route must answer one operator question, not three.
  • The first viewport must show: route title, one short scope sentence, current garden context when applicable, one control row, and the first actionable content.
  • Default page anatomy is PageHeader -> one dominant workspace canvas -> optional secondary inspector (AdminDialog, centered on desktop, or a single rail).
  • Use one control grammar per route. Choose one of: stage tabs, view tabs, or a list toolbar. Do not stack multiple competing switchers and banners above the workspace.
  • Use at most one strong CTA in the header. If a FAB exists for the route, the header should not duplicate the same creation action.
  • View-level actions must flow through ViewAction + AdminViewActions on desktop and the same action set through useViewActions for tablet/mobile FAB speed dial. Do not add duplicate inline edit buttons inside route bodies for the same action; contextual row actions are allowed when the row is the only discoverable setting control.
  • Summary metrics are optional and should appear only when they improve decisions. Cap summary strips at three items for operational routes.
  • Prefer layout, rows, and sections over card mosaics. Use cards only for discrete records, bounded workflows, or detail modules that need separation.
  • Default detail behavior is an inspector or secondary pane, not a new pile of inline cards.
  • Use utility copy. Titles identify the workspace. Descriptions explain scope, freshness, or what the operator can do next.
  • Keep imagery subordinate to status, title, and actions unless the operator is explicitly reviewing media itself.
  • Reduced-motion support is mandatory. Stage changes, row selection, and sheet open/close must retain state clarity without relying on travel or spring lift.
  • Route and tab changes should animate only the named route content group. AppBar, Navigation/FAB, and the canvas root stay visually stationary.

Admin Route Recipe

Use this as the default assembly order for a new or refreshed primary admin route:

  1. Start with CanvasRouteFrame and the route header. The title names the workspace; the description states scope or freshness.
  2. Put view-level actions in AdminViewActions on desktop and the matching useViewActions action set for tablet/mobile FAB speed dial.
  3. Choose one control grammar: stage tabs, view tabs, or a list toolbar. Do not stack tabs, banners, and competing switchers above the work area.
  4. Use AdminSearchToolbar, AdminSortSelect, AdminFilterChip, and AdminTabRail for search, sorting, filtering, and route-local modes.
  5. Build one dominant workspace surface. Prefer rows, sections, and dense work areas over card mosaics.
  6. Use cards only for discrete records, bounded workflows, or detail modules that need separation.
  7. Open workspace action/detail flows in AdminDialog; use variant="flow" with size="lg" and ADMIN_FLOW_DIALOG_CLASS for full-surface creation/commit flows. Desktop centers these dialogs; mobile presents standard and flow as full-width bottom sheets.
  8. Use AdminSideSheet only for the three global AppBar surfaces: Profile, Settings, Notifications.
  9. Keep loading, empty, disabled, and error states calm and operational. Empty states should preserve final geometry and explain the next action.
  10. Reject hero sections, promotional gallery framing, decorative gradients, ad-hoc dialog widths, nested card stacks, and glass outside Navigation/FAB chrome.

Route Roles

  • /hub is the primary canvas workspace. Its primary object is the Hub workbench with Work, Assess, Certify, and History modes.
  • /hub is the gold-standard canvas route for composition, depth, responsive adaptation, and tactile state behavior.
  • /hub defaults to one dominant workbench surface under the stage rail. Detail, review, and read-only handoff flows open as AdminDialog inspectors (size="lg", workspace tone). Full-surface creation/commit flows (Submit Work, Create Assessment, Create Hypercert) open as an AdminDialog (variant="flow" + ADMIN_FLOW_DIALOG_CLASS) with a scrim — centered on desktop, full-width bottom sheet on mobile — hosting the shared ActionFlowShell grammar (pinned header + scrolling body + pinned footer). They are never fullscreen takeovers or non-modal routes.
  • Read-only Hub deep links (/hub/work/:workId, /hub/certify/:assessmentId, /hub/history/:historyEventId) are AdminDialog inspectors that render through the Hub controller and preserve shareable gardenAddress and sort query state. Full-surface creation/commit deep links (/hub/work/submit, /hub/assess/create, /hub/certify/create) open the action-flow dialog (full-width bottom sheet on mobile), not inspector sheets. Hub mode belongs to the path, not a view query parameter.
  • Certify stays visible for evaluator-only users as a read-only handoff state. Owner or operator authority unlocks mint actions.
  • /garden is the situational overview of a single garden. Its primary object is health, activity, and impact state.
  • /community is the operations workspace for membership, treasury, pools, and yield. Only one of those modes should dominate the canvas at a time.
  • /actions is the action registry and lifecycle workspace. It should feel like a managed catalog, not a marketing gallery.

Route Smells To Reject

  • /hub: stage navigation should reflect real operating modes rather than placeholder pipelines.
  • /hub: header chrome should keep the first actionable queue content within immediate reach.
  • /garden: large summary card grids before the operator reaches the actual overview or impact workspace.
  • /garden: mixing overview, impact, and settings as visually equivalent tabs without clearly changing page mode.
  • /community: a stat strip plus tab switcher plus contextual CTA banner plus card stack before the user reaches the active mode.
  • /community: treating treasury, members, pools, and yield as one blended page instead of four focused operational modes.
  • /actions: image-led card galleries that make the route read like a promotional browse surface instead of an admin registry.
  • /actions: filters that change presentation but never establish a clear “default management view” for scanning and editing records.

Page Families

  • List pages: PageHeader + ListToolbar + filters/sort + loading/empty/error + results
  • Detail and workbench pages: PageHeader or garden hero plus main/rail composition
  • Operational pages: PageHeader + stage/tabs + primary workspace + optional inspector

Migration Rules

  • DashboardLayout, Sidebar, and Header are legacy references, not starting points for new work.
  • Do not add admin-local primitive shims such as components/ui.
  • Do not add admin-local config helpers when the behavior belongs beside shared React Query or other shared config.
  • Do not introduce package-local utils/ for logic that should live in shared.
  • Keep reusable components consumed through the @green-goods/shared barrel.