Skip to main content

Documentation Index

Fetch the complete documentation index at: https://grantmaster.dev/llms.txt

Use this file to discover all available pages before exploring further.

Surface System (Page Surface Primitives)

Goal

Unify page-surface UI the same way the modal refactor unified overlays. The surface system is the shared contract for:
  • section shells
  • metric grids
  • search and filter toolbars
  • empty states
  • page-body notices
  • lightweight action cards
It should eliminate ad hoc page-level card markup and inline toolbar/empty-state compositions in feature code.
Naming note: These were originally prefixed Widget* but have been renamed to Surface* to avoid confusion with Dashboard Widgets (src/components/widgets/). The old Widget* names are still exported as deprecated aliases for backward compatibility.

Shared Primitives

  • src/components/ui/widget-system.tsx
    • SurfacePageHeader
    • SurfaceInsightList
    • SurfaceSection
    • SurfaceMetricCard
    • SurfaceMetricGrid
    • SurfaceToolbar
    • SurfaceEmptyState
    • SurfaceNotice
    • SurfaceActionBar
    • SurfaceActionCard
  • Existing foundations reused under the hood:
    • src/components/ui/card.tsx
    • src/components/ui/MetricCard.tsx
    • src/components/ui/EmptyState.tsx
    • src/components/ui/SearchFilterBar.tsx
    • src/components/widgets/infowidgets/KpiCard.tsx

Conventions

Sections

  • Use SurfaceSection for titled page sections with optional description and top-right action.
  • Prefer one clear section title and one concise supporting sentence.
  • Keep section actions short and task-oriented.

Metrics

  • Use SurfaceMetricGrid for dashboard or overview KPI strips.
  • Use the shared icon prop when a metric needs a stable semantic cue; do not reintroduce local card markup just to render icons.
  • Use MetricCard/KPI tone to communicate severity, not decorative color variation.
  • Only make metric cards clickable when they drill into a concrete filtered view.

Toolbars

  • Use SurfaceToolbar for search + filters + actions + result-count surfaces.
  • Search belongs on the left; filters follow search; actions stay on the right.
  • Result count and “Clear all” belong in the meta row beneath the controls.

Empty States

  • Use SurfaceEmptyState for zero-data and no-results states.
  • Title should explain the state directly.
  • Description should explain what to do next.
  • Include a primary action when the next step is obvious.

Notices

  • Use SurfaceNotice for inline information, warnings, or critical operational banners inside a page body.
  • Prefer SurfaceNotice over hand-authored amber/rose/primary cards when the content is explanatory rather than tabular.
  • Use tone="info" | "success" | "warning" | "danger" and keep the copy action-oriented.

Action Bars

  • Use SurfaceActionBar for bulk-selection and short-lived operational rails that pair a status summary with immediate actions.
  • Keep the title stateful and concrete, for example 3 entries selected.
  • Prefer SurfaceActionBar over bespoke full-width tinted bars when the content is primarily actions, not explanatory text.

Action Cards

  • Use SurfaceActionCard for lightweight navigational or drill-in cards.
  • Keep title short and body descriptive.
  • Reserve them for next-step navigation, not dense data display.

Initial Migrations

  • src/features/grantors/components/pages/grantors-page/ui.tsx This file now delegates its reusable metric, toolbar, section, empty-state, and action-card surfaces to the shared widget system instead of defining a parallel local design language.
  • src/components/ui/GrantOperatingSystem.tsx The operating-page adapter now delegates page headers, KPI strips, and insight strips to the shared widget system so existing pages can inherit the unified surface language without immediate rewrites, and now exposes OperatingNotice as the matching inline notice wrapper.
  • src/features/users/components/UsersPage.tsx The people overview now uses OperatingKpiStrip for its summary metrics instead of maintaining a local UserStatsCard grid in two separate render paths.
  • src/features/grantors/components/GrantorStatsWidget.tsx The standalone grantor summary widget now uses SurfaceMetricGrid instead of bespoke metric card markup.
  • src/features/projects/components/ProjectStatsWidget.tsx The standalone project summary widget now uses SurfaceMetricGrid instead of bespoke metric card markup.
  • src/features/projects/components/ProjectsToolbar.tsx Project search, filter, and action controls now sit on SurfaceToolbar.
  • src/features/projects/components/ProjectsAllView.tsx The main projects overview now uses SurfaceMetricGrid and SurfaceEmptyState instead of a local KPI cluster and plain empty-state shell.
  • src/features/projects/components/ProjectTemplateManager.tsx Template management now uses SurfaceEmptyState for the no-template path.
  • src/features/projects/components/TemplateSelector.tsx Template picking now uses SurfaceEmptyState for the no-templates path.
  • src/features/projects/components/ProjectsList.tsx The list view now uses SurfaceEmptyState for the zero-results path.
  • src/features/projects/components/ProjectsPageContent.tsx Project list fallback states now use SurfaceEmptyState, and dead local page-action scaffolding was removed.
  • src/features/projects/components/project-editor/ProjectSettingsTab.tsx The project-editor budget line surface now uses SurfaceSection and SurfaceEmptyState.
  • src/features/projects/components/project-details/tabs/ProjectSettingsTab.tsx The project-details budget line surface now uses SurfaceSection and SurfaceEmptyState.
  • src/features/relations/components/pages/relations-page/RelationsEmptyState.tsx The page-level no-data state now uses SurfaceEmptyState.
  • src/features/extensions/components/ExtensionDetailPanel.tsx Repeated detail cards now use SurfaceMetricGrid, SurfaceSection, and SurfaceEmptyState.
  • src/features/users/components/TrainingDashboard.tsx The training dashboard summary strip, training-category metrics, and compliance fallback now use SurfaceMetricGrid, SurfaceSection, and SurfaceEmptyState.
  • src/features/users/components/CapacityDashboard.tsx The capacity dashboard pulse summary and inline operational notices now use the shared metric and notice primitives.
  • src/features/users/components/ValuesManagement.tsx Values and behaviors now use the shared metric grid and empty-state shell for their overview and fallback surfaces.
  • src/features/users/components/StaffAssignmentPlanner.tsx The staffing planner summary, loading shell, and no-user fallback now use the shared widget primitives.
  • src/features/users/components/CostAllocationDashboard.tsx Cost allocation overview metrics, WNT warnings, WNT guidance, and no-data branches now use the shared widget primitives.
  • src/features/users/components/time-off-planning/tabs/MyTimeOffTab.tsx Time-off balances, loading/error fallbacks, and the Arbodienst warning now use the shared widget primitives.
  • src/components/TeamStatsWidget.tsx The personnel summary widget now uses SurfaceMetricGrid.
  • src/features/users/org-chart/components/OrgChartSummaryGrid.tsx The org chart summary strip now uses SurfaceMetricGrid.
  • src/features/users/org-chart/components/OrgChartDashboard.tsx The org chart page now uses SurfaceEmptyState and SurfaceNotice for no-data and span-of-control guidance states.
  • src/features/users/components/UserDocumentsDashboard.tsx Document stats, type/compliance summaries, and the attention shell now use the shared widget primitives.
  • src/features/grants/post-award/components/PostAwardTabContent.tsx Post-award overview metrics, zero states, and the report-deadline warning now use the shared widget primitives.
  • src/components/PersonnelDirectory.tsx The personnel directory summary strip, compliance warning chip, and no-results state now use the shared widget primitives.
  • src/components/TimeOffManagement.tsx The legacy time-off management surface now uses the shared widget primitives for leave balances, Arbodienst warnings, and loading/error empty states.
  • src/components/TeamOversightDashboard.tsx The team oversight dashboard now uses SurfaceMetricGrid, SurfaceToolbar, and SurfaceEmptyState instead of local KPI cards and a hand-built filter shell.
  • src/components/manager/BulkJournalApproval.tsx Bulk journal approval now uses the shared metric grid, empty state, and modal shell for the approval/rejection comment flow.
  • src/components/security/SecurityAlertsPanel.tsx Security alerts now use the shared metric grid, empty state, and modal shell for event detail review.
  • src/components/manager/BulkJournalApproval.tsx Bulk journal review now uses SurfaceActionBar for the selected-entry action rail instead of a bespoke tinted bulk-actions bar.
  • src/features/notifications/components/NotificationHistory.tsx Notification bulk selection now uses SurfaceActionBar for the selected-notification action rail.
  • src/features/journals/components/pages/ManagerReviewDashboard.tsx Manager review now uses SurfaceActionBar for the selected-submission approval rail.
  • src/features/users/components/users-page/UsersListView.tsx The floating users bulk-selection control now uses SurfaceActionBar instead of a custom dark pill treatment.
  • src/features/users/components/department-structure/DepartmentStructure.tsx Department governance bulk actions now use SurfaceActionBar instead of inline select-and-apply controls in the filter toolbar.
  • src/features/users/components/department-structure/components/UsersAssignmentDialog.tsx The department assignment dialog now uses SurfaceActionBar for selection-state controls so select-all and selected-count behavior matches the shared action-rail language.
  • src/features/admin/pages/AdminApprovalsPage.tsx The approvals queue now uses SurfaceActionBar for note, assignee, and bulk decision controls instead of a bespoke card-level action grid.
  • src/features/grantors/components/GrantorAnalyticsTab.tsx Grantor analytics now uses SurfaceEmptyState and SurfaceNotice for its no-data and concentration-risk states.
  • src/features/grantors/components/compliance-matrix/ComplianceConflictsBanner.tsx The compliance conflicts banner now uses SurfaceNotice for its shared warning shell while preserving the nested severity cards and dismiss action.
  • src/features/projects/components/portfolio/PortfolioBudgetOverviewTab.tsx Portfolio budget notes now use SurfaceNotice instead of a bespoke amber informational card.
  • src/features/projects/components/ProjectComplianceTab.tsx The project compliance header, KPI strip, no-documents notice, and zero states now use SurfaceSection, SurfaceMetricGrid, SurfaceNotice, and SurfaceEmptyState.
  • src/features/projects/components/ProjectPhaseManagement.tsx Project phase management now delegates to shared validation and modal building blocks instead of carrying bespoke inline dialog and requirements UI.
  • src/features/projects/components/project-editor/ProjectTeamTab.tsx The project-editor team tab now uses SurfaceNotice, SurfaceEmptyState, and SurfaceMetricGrid for its guidance, zero state, and summary strip.
  • src/features/projects/components/project-details/tabs/ProjectTeamTab.tsx The project-details team tab now uses the same shared notice, empty-state, and metric-grid surfaces as the editor flow.
  • src/features/projects/components/phase-management/PhaseValidationResults.tsx Phase validation requirements now use SurfaceSection plus SurfaceNotice cards for blockers, warnings, and success-ready states.
  • src/features/projects/components/phase-management/PhaseAdvanceModal.tsx The phase-advance flow now uses the shared AppModal, ModalNotice, and ModalFooter contract.
  • src/features/projects/components/phase-management/PhaseArchiveModal.tsx The phase-archive flow now uses the shared AppModal, ModalNotice, and ModalFooter contract.
  • src/components/widgets/finance/BudgetModificationTrackerWidget.tsx Budget-modification approval and volatility alerts now use SurfaceNotice.
  • src/components/widgets/finance/BurnRateComparisonWidget.tsx Overspend warnings and budget guidance now use SurfaceNotice.
  • src/components/widgets/finance/CashFlowForecastWidget.tsx Negative-cash-flow and runway guidance banners now use SurfaceNotice.
  • src/components/widgets/finance/SubcontractorSpendWidget.tsx The subaward-threshold alert now uses SurfaceNotice.
  • src/components/widgets/finance/GrantDiversificationWidget.tsx The concentration-risk warning now uses SurfaceNotice without flattening the underlying HHI visualization.

Skeleton Loading States

Route-level code splitting needs meaningful Suspense fallbacks. The skeleton system provides purpose-built loading placeholders that match common page layouts.

Components

ComponentFileUse Case
AppShellSkeletonsrc/components/ui/AppShellSkeleton.tsxFull app shell wrapper (sidebar + header + content area)
DashboardSkeletonsrc/components/ui/skeletons/DashboardSkeleton.tsxDashboard pages with KPI cards and chart grids
TablePageSkeletonsrc/components/ui/skeletons/TablePageSkeleton.tsxData table pages with toolbar and rows
FormPageSkeletonsrc/components/ui/skeletons/FormPageSkeleton.tsxForm-heavy pages with input fields
WorkspaceSkeletonsrc/components/ui/skeletons/WorkspaceSkeleton.tsxWorkspace/editor layouts with sidebar and main content
ApprovalsSkeletonsrc/components/ui/skeletons/ApprovalsSkeleton.tsxApproval queue pages

Usage

Use the appropriate skeleton as the fallback prop on React.Suspense boundaries wrapping lazy-loaded route components:
<Suspense fallback={<TablePageSkeleton />}>
  <LazyExpensesPage />
</Suspense>

Guidelines

  • Match the skeleton to the page pattern (dashboard, table, form, workspace)
  • Use AppShellSkeleton only for the top-level app shell boundary
  • Skeletons use Tailwind animate-pulse on bg-slate-200 dark:bg-slate-700 blocks

Grant Operating System Adapter

File: src/components/ui/GrantOperatingSystem.tsx A domain-specific adapter layer that wraps Surface primitives with grant-lifecycle semantics. These components delegate to the shared Surface system rather than introducing independent primitives.

Components

ComponentWrapsPurpose
OperatingPageHeaderSurfacePageHeaderPage header with grant lifecycle context
OperatingKpiStripSurfaceMetricGridKPI strip for grant operating metrics (budget utilization, compliance scores, etc.)
OperatingInsightStripSurfaceInsightListContextual alerts/warnings tied to grant data (overdue reports, budget thresholds, etc.)
OperatingNoticeSurfaceNoticeInline operational notices with grant-specific tone mapping
OperatingFilterBarCustom card layoutFilter controls for grant operating views (stage, status, date range)
LifecycleStageMapVisual representation of grant lifecycle stages (stub — reserved for future implementation)

Associated Types

interface OperatingKpi {
  label: string;
  value: string | number;
  icon?: ElementType;
  trend?: string;
  tone?: 'default' | 'success' | 'warning' | 'danger';
}

interface OperatingInsight {
  title: string;
  description: string;
  severity: 'info' | 'warning' | 'danger';
  action?: OperatingAction;
}

interface OperatingAction {
  label: string;
  onClick: () => void;
}

interface LifecycleStage {
  id: string;
  label: string;
  status: 'completed' | 'active' | 'upcoming';
}

Usage Pattern

Pages that consume GrantOperatingSystem get centralized headers, KPI strips, and insight strips without maintaining local metric card markup:
import { OperatingKpiStrip, OperatingInsightStrip } from '@/components/ui/GrantOperatingSystem';

<OperatingKpiStrip kpis={[
  { label: 'Budget Used', value: '72%', tone: 'warning' },
  { label: 'Reports Due', value: 3, tone: 'danger' },
]} />

<OperatingInsightStrip insights={[
  { title: 'Budget Alert', description: 'Grant X is at 90% utilization', severity: 'warning' },
]} />

Current Consumers

  • src/features/grants/discovery/components/GrantDiscoveryPage.tsx — KPI strip, insight strip, filter bar, lifecycle map
  • src/features/grants/post-award/components/PostAwardPage.tsx — KPI strip, insight strip
  • src/features/users/components/UsersPage.tsx — KPI strip, insight strip

After this foundation batch, the highest-value next migrations are:
  • pages that already consume GrantOperatingSystem This gives you centralized headers/KPIs/insights without local rewrites.
  • remaining dashboard pages that still hand-roll KPI strips, notices, or empty states in the same file These benefit most from SurfaceMetricGrid, SurfaceSection, and SurfaceEmptyState.
  • remaining standalone search/filter shells These should converge on SurfaceToolbar rather than custom search bars.
  • repeated “detail panel” card stacks These should converge on SurfaceSection and shared info-card patterns.
  • page-level template and list zero states in Projects and Grantors These should converge on SurfaceEmptyState so list, template, and setup screens stop inventing their own fallback shells.
  • bespoke section headers and notice cards in operating pages These should converge on SurfaceSection and a shared notice treatment so the page body matches the overlay and toolbar consistency work.
  • standalone legacy dashboards outside the Grantors, Grants, Projects, Users, and Extensions migration path These should migrate only when they are still using raw four-card grids or local warning shells rather than an existing centralized feature UI.
  • visual regression coverage for the centralized page surfaces The widget layer now has enough reach that browser-level regression checks will catch real drift across multiple features.
  • a broader repo typecheck The shared surface layer is now centralized enough that a wider compile check has real payoff, even if unrelated baseline issues still need triage.
  • selective cleanup of remaining explanatory finance/project notice cards Only convert panels that are actually notices; leave metric cards, gauges, badges, and data-viz-specific status treatments alone.