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.

Portfolio Dashboard

Overview

The Portfolio Dashboard is a role-aware, widget-based analytics hub that provides each user with a curated, domain-organized view of their organization’s data. It is divided into eight tabs — one personal daily-briefing tab and seven specialty tabs — each showing widgets relevant to a specific operational domain. Users can customize the Personal tab and reorganize widgets. SuperAdmins can additionally customize all specialty tabs. Widgets are role-gated: each user sees only widgets appropriate to their system role. The dashboard is accessible at /overview/:tab. The available tab IDs map to:
Tab IDURL SegmentDomain
dashboard/overview/dashboardPersonal (daily briefing)
projects/overview/projectsProject lifecycle
finance/overview/financeFinancial health
grants/overview/grantsGrant pipeline
users/overview/usersTeam capacity
mission/overview/missionImpact and strategy
compliance/overview/complianceAudit and risk
platform/overview/platformPlatform intelligence (SuperAdmin only)
Legacy ?tab= query parameter URLs are automatically redirected to the path-based format. The active tab ID is persisted in currentUserProfile.widgetPreferences.activeDashboardId.

Component Architecture

DashboardTabs                         (src/features/dashboard/DashboardTabs.tsx)
└── DynamicDashboard                  (src/components/DynamicDashboard.tsx)
    ├── useWidgetDashboard             (src/hooks/useWidgetDashboard.ts)
    └── WidgetDashboardBase           (src/components/widgets/WidgetDashboardBase.tsx)
        └── [Widget Components]       (lazy-loaded per category)

DashboardTabs

src/features/dashboard/DashboardTabs.tsx is the entry point. It:
  • Reads :tab from the URL and maps it to a DashboardTabId
  • Builds the tab list (SuperAdmins get the extra platform tab)
  • Renders <PageTabs> for navigation
  • Renders the active tab’s <DynamicDashboard> with the appropriate props
  • Passes domain-specific infoWidgets (quick stat cards) where applicable

DynamicDashboard

src/components/DynamicDashboard.tsx is the shared dashboard container. It accepts:
PropTypeDescription
dashboardIdDashboardTabIdIdentifies which dashboard to load (default: 'projects')
categoryFilter(category: WidgetCategory) => booleanOptional override; falls back to DashboardPresetService
showGreetingbooleanShow personalized greeting header (default: true on projects)
titlestringTitle override
infoWidgetsReactNodeQuick stat cards rendered above the widget grid
Permission model: Only the dashboard (Personal) tab is editable by all users. All other tabs require the isSuperAdmin RBAC check to enable edit controls. Density preference: The grid gap class is derived from currentUserProfile.dashboardPreferences.density:
DensityGap
compactgap-2 md:gap-3 (8px / 12px)
comfortable (default)gap-3 md:gap-4 (12px / 16px)
spaciousgap-4 md:gap-6 (16px / 24px)
The component renders via WidgetDashboardBase, which handles drag-and-drop reordering, loading skeletons, the widget library panel, and empty states.

WidgetDashboardBase

src/components/widgets/WidgetDashboardBase.tsx is the shared rendering engine for all dashboards. It consumes the useWidgetDashboard hook’s state and actions to provide:
  • Drag-and-drop widget reordering (via dnd-kit or similar)
  • Loading skeleton layout
  • Widget library slide-in panel (setShowLibrary)
  • Empty state with optional “Add Widgets” call-to-action
  • Lazy-loaded widget rendering

Widget System

Widget Categories

Each widget belongs to exactly one WidgetCategory. The category determines which dashboard tab the widget appears on:
CategoryTab
PERSONALPersonal (dashboard)
PROJECTSProjects
FINANCEFinance
GRANTSGrants
USERSUsers
MISSIONMission
COMPLIANCECompliance
PLATFORMPlatform
The TAB_CATEGORY_FILTERS map in DashboardPresetService defines the filter function for each tab. The Personal tab uses c === WidgetCategory.PERSONAL, ensuring only personal-briefing widgets appear there, not all 80+ widgets across other categories.

Widget Counts by Tab

TabApproximate Widget Count
Personal~16 available; ~10 shown by default (role-dependent)
Projects7
Finance13
Grants12
Users11
Mission9
Compliance10
Platform1 (quota-usage)

Role-Based Widget Assignment

Widgets are assigned to roles at the registry level. The useWidgetDashboard hook filters the full widget registry down to those assigned to the current user’s role before handing them to DashboardPresetService. Roles and their typical access breadth:
RoleAccess Level
SUPER_ADMINAll widgets on all tabs
ADMINAll widgets except advanced SuperAdmin-only widgets
MANAGEROperational widgets relevant to team management
MEMBERLimited view — basic project and personal widgets
AUDITORCompliance, audit, and reporting widgets

Dashboard Preset Service

src/shared/platform/dashboardPresetService.ts is the source of truth for default widget configurations.

computePreset(role, dashboardId, assignedWidgets, categoryFilter?)

Computes the initial visible/hidden widget split for a user when they first access a dashboard tab. Algorithm:
  1. Look up the curated order for (dashboardId, role) from CURATED_ORDERS.
  2. Filter assignedWidgets by the tab’s category filter.
  3. Add curated widgets first (in curated order), if available for the role.
  4. For specialty tabs (not 'dashboard'): append remaining category widgets sorted by assignment.priority.
  5. For the Personal tab ('dashboard'): skip step 4 — only curated widgets are shown by default. Remaining widgets go to the library.
  6. Return { visibleOrder: string[], hidden: string[] }.
The Personal tab’s restriction prevents users from being shown all 80+ widgets on first load. They can add more from the widget library.

Curated Widget Orders

CURATED_ORDERS defines the default visible order per (DashboardTabId, SystemRole). Highlights: Personal tab — SUPER_ADMIN / ADMIN: organization-health-scorecard, ai-insights-feed, my-deadlines, action-items, quick-actions, personal-contract, deadline-summary, notification-preview, quick-time-entry Projects tab — SUPER_ADMIN / ADMIN: project-portfolio, project-phase-distribution, budget-vs-timeline, deliverable-tracker, project-risk-register, report-submission-pipeline, project-insights Finance tab — SUPER_ADMIN: cash-flow-forecast, operating-reserve-months, restricted-unrestricted-funds, fund-drawdown-schedule, burn-rate-comparison, donor-revenue-concentration, overhead-ratio, indirect-cost-recovery, subcontractor-spend, budget-modification-tracker, budget-utilization, budget-health, budget-composition Grants tab — SUPER_ADMIN: grant-revenue-pipeline, grant-application-calendar, grant-milestone-tracker, reporting-burden-heatmap, grant-health-matrix, grant-closeout-checklist, funder-relationship-health, grant-success-rate, benchmarking, predictive-closeout-risk, proposal-win-rate, active-grant-map Users tab — SUPER_ADMIN / ADMIN: team-capacity, team-capacity-forecast, staff-allocation-sankey, team-availability, leave-calendar-overview, grant-expertise-map, volunteer-contribution, cost-per-fte, user-utilization, submission-rate, grant-admin-workload Mission tab — SUPER_ADMIN / ADMIN: theory-of-change-tracker, impact-metrics-summary, mission-pillar-progress, strategic-impact-radar, sdg-alignment-dashboard, grant-portfolio-matrix, donor-stewardship-pipeline, grant-diversification, beneficiary-reach Compliance tab — SUPER_ADMIN / ADMIN / AUDITOR (same set): compliance-calendar, grantor-compliance-matrix, audit-readiness-score, compliance-score, policy-violation-trends, expense-eligibility-heatmap, document-compliance, funder-compliance, reporting-schedules, regulatory-risk-heatmap

Reserved Library Widgets

The RESERVED_LIBRARY_WIDGETS set contains widget IDs that are never shown by default and are only available for manual addition from the library. Currently: key-metrics.

Dashboard Customization

Edit Mode

When edit mode is active (isEditing === true), the header shows two buttons:
  • Add Widget — opens the widget library panel (setShowLibrary(true))
  • Done — exits edit mode (setIsEditing(false))
In non-edit mode, a single Edit button (with an animated pencil icon) enters edit mode. Edit mode is only available when canCustomizeDashboard is true:
const canCustomizeDashboard = dashboardId === 'dashboard' || isSuperAdmin;

Widget Counter

A pill badge shows {visibleWidgets.length}/{allCategoryWidgets.length} Widgets. If the user can customize the dashboard, clicking the pill opens the widget library. Otherwise, it is a static display.

User Preferences Storage

Widget visibility and order are persisted in currentUserProfile (the employees Firestore collection document for the user):
  • widgetPreferences — per-tab visible/hidden widget arrays and order
  • dashboardPreferences — display settings (density, showGreeting, showTooltips, showOnboardingProgress)

Personal Tab Features

The Personal tab (dashboardId === 'dashboard') has additional header elements not present on specialty tabs:

Personalized Greeting

When dashboardPreferences.showGreeting is enabled, the h1 displays a time-aware personalized greeting generated by getPersonalizedGreeting(currentUserProfile?.name) from src/lib/greetings.ts.

Dashboard Tooltip

DashboardTooltip (src/components/DashboardTooltip.tsx) renders a rotating contextual tip below the header when dashboardPreferences.showTooltips is enabled.

InfoWidgets

InfoWidgets are quick-stat cards rendered between the header and the widget grid. They are not part of the draggable widget system — they are always shown and cannot be hidden or reordered. Each specialty tab in DashboardTabs passes its own infoWidgets prop:
TabInfoWidgets Component
FinanceFinanceInfoWidgets
ProjectsProjectsInfoWidgets
GrantsGrantsInfoWidgets
UsersUsersInfoWidgets
MissionMissionInfoWidgets
ComplianceComplianceInfoWidgets

Dashboard Template Editor

DashboardTemplateEditor is a separate admin-only component that allows SuperAdmins to configure the default widget layout templates for each tab and role. It is distinct from the user-facing edit mode — it modifies the templates that are applied when new users (or users without existing preferences) first visit a dashboard tab.

useWidgetDashboard Hook

src/hooks/useWidgetDashboard.ts is the primary state management hook. It accepts:
{
  categoryFilter: (category: WidgetCategory) => boolean;
  dashboardId: DashboardTabId;
}
It returns { state, actions }: State:
  • isEditing — whether edit mode is active
  • visibleWidgets — ordered array of widget instances currently shown
  • hiddenWidgets — widget instances available in the library
  • allCategoryWidgets — all widgets for this category (visible + hidden)
  • isLoading — loading state for widget registry/preferences
Actions:
  • setIsEditing(boolean)
  • setShowLibrary(boolean)
  • moveWidget(fromIndex, toIndex) — reorder within the grid
  • showWidget(widgetId) / hideWidget(widgetId) — toggle library membership
  • resetToDefaults() — reapply DashboardPresetService.computePreset for the user’s role

Data Flow

1. User navigates to /overview/:tab
2. DashboardTabs reads :tab → selects DashboardTabId
3. DynamicDashboard resolves effectiveCategoryFilter via DashboardPresetService
4. useWidgetDashboard loads:
   a. User's role from RBACContext
   b. Widget registry (filtered by role + category)
   c. User preferences from currentUserProfile (or computes preset if none)
5. WidgetDashboardBase renders visible widgets (lazy-loaded)
6. User can drag to reorder or open widget library to show/hide
7. Changes are persisted back to currentUserProfile in Firestore

Key Files Reference

FilePurpose
src/features/dashboard/DashboardTabs.tsxTab navigation, tab → category mapping
src/features/dashboard/index.tsPublic API barrel export
src/components/DynamicDashboard.tsxDashboard container with header, edit controls
src/components/widgets/WidgetDashboardBase.tsxShared DnD/rendering engine
src/hooks/useWidgetDashboard.tsState management hook
src/shared/platform/dashboardPresetService.tsPreset service: category filters, curated orders
src/lib/greetings.tsPersonalized greeting generator
src/components/DashboardTooltip.tsxRotating contextual tip