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.

Frontend Architecture

StatusUpdatedCovered Directory
🟢 Stable2026-04-07src/

Technical Stack

  • Framework: React 19 (using New React Compiler and latest APIs).
  • Build Tool: Vite.
  • Styling: Tailwind CSS + shadcn/ui.
  • Icons: Lucide React.
  • Routing: React Router 7 (formerly Remix).

Application Structure

1. Component Hierarchy

The UI is organized into three layers of increasing complexity:
  • Shared Components (src/components/ui): Low-level, stateless atoms (Buttons, Inputs, Modals from shadcn).
  • Feature Components (src/features/.../components): Logic-aware components specific to a domain (e.g., GrantApplicationCard).
  • Pages: Orchestration level; maps routes to layouts and feature components. The preferred pattern is PageShell in src/features/*/ consuming declarative PageDef configs from pages.config.ts with RBAC filtering via usePageResolver. Legacy pages in src/pages/ still exist but new pages should use the PageShell pattern. See Declarative Modal & Page Pipeline for details.

2. State Management Strategy

GrantMaster avoids a heavy global state (like Redux) in favor of:
  • React Query: The default owner for server-backed data, async reads, cache invalidation, and optimistic updates.
  • React Context: For ambient app state and dependency injection boundaries such as auth, tenant, RBAC, toasts, and feature-level UI coordination.
  • Local component state / reducers: For ephemeral UI state that does not need cross-route caching.
  • URL as State: View filters and active record IDs are primarily stored in the URL search parameters to ensure linkability.

2.1 State Ownership Rules

Use these rules when deciding between Context, React Query, and direct service calls:
ConcernDefault ownerNotes
Firestore/API reads that should be cached, refreshed, or shared@tanstack/react-queryQuery hooks should call services, not Firebase directly from components.
Firestore/API writesReact Query mutation hooks + servicesMutations may show toasts and update/invalidate query caches.
Auth, tenant, RBAC, feature flags, theme, toastsReact ContextThese are ambient dependencies rather than record collections.
Page-only UI state (dialogs, tabs, selection, draft filters)useState / useReducer in component or feature hookKeep this local unless multiple distant branches need it.
Long-lived feature UI workflows with no server cache valueReact Context or reducer providerExample: wizard flow state, drag session state, multi-step editors.
Firebase accessService layer onlyComponents and contexts should go through services.

2.2 Anti-Patterns To Avoid

  • Do not store the same canonical server collection in both Context state and React Query cache.
  • Do not create a Context provider whose main job is to mirror query data into useState.
  • Do not expose a stateful provider API for a feature while also adding separate query hooks for the same underlying collection unless the provider is explicitly a migration wrapper.
  • Do not call Firestore directly from components; put reads and writes behind services and then consume them through hooks.
For most domain features, prefer this shape: If a provider still exists for backward compatibility, it should do one of two things:
  • Provide ambient non-data concerns only.
  • Be a thin compatibility wrapper around query-backed hooks with no duplicate canonical collection state.
Features that still keep fetched collections in provider useState are considered migration debt and should converge toward the pattern above.

3. Service-Layer Integration

Components DO NOT call Firestore or APIs directly. They use hooks that wrap Domain Services:

Provider Hierarchy

ModalManagerProvider (mounted in FeatureProviders) provides the imperative open(id, payload) / close() / confirm() API for the declarative modal system. ModalOrchestrator resolves and renders global-scope modals. See Declarative Modal & Page Pipeline.

Routing & Layouts

  • Protected Routes: Handled by <AppRoutes /> which redirects to /auth if no session is present. ProtectedPageRoute wraps routes with automatic 403 fallback when PageDef.permissions or PageDef.roles are not met.
  • Tenant-Awareness: The TenantProvider captures the current organization from the user profile; all subsequent service calls are automatically scoped.
  • Layouts: The root Layout.tsx provides the standardized Sidebar + Topbar structure. Individual pages use PageShell (preferred) or PageLayout to render headers, tabs, and actions.
  • PageShell Pattern: Pages declare a PageDef in pages.config.ts, resolve it via usePageResolver(pageDef, { handlers }), and wrap content in <PageShell config={resolved}>. Tabs and actions are automatically filtered by RBAC. See Component Cookbook for recipes.

Maintenance

Update this document when:
  • Adding a new global Provider.
  • Changing the primary routing library.
  • Introducing a new state management library (e.g., TanStack Query).

Update Checklist

  1. Verify the new Provider is added to src/contexts/providers.tsx.
  2. Check if the new routing impacts the LandingPageRedirect logic in App.tsx.