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
| Status | Updated | Covered Directory |
|---|---|---|
| 🟢 Stable | 2026-04-07 | src/ |
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 declarativePageDefconfigs frompages.config.tswith RBAC filtering viausePageResolver. Legacy pages insrc/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:| Concern | Default owner | Notes |
|---|---|---|
| Firestore/API reads that should be cached, refreshed, or shared | @tanstack/react-query | Query hooks should call services, not Firebase directly from components. |
| Firestore/API writes | React Query mutation hooks + services | Mutations may show toasts and update/invalidate query caches. |
| Auth, tenant, RBAC, feature flags, theme, toasts | React Context | These are ambient dependencies rather than record collections. |
| Page-only UI state (dialogs, tabs, selection, draft filters) | useState / useReducer in component or feature hook | Keep this local unless multiple distant branches need it. |
| Long-lived feature UI workflows with no server cache value | React Context or reducer provider | Example: wizard flow state, drag session state, multi-step editors. |
| Firebase access | Service layer only | Components 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.
2.3 Recommended Feature Pattern
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.
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/authif no session is present.ProtectedPageRoutewraps routes with automatic 403 fallback whenPageDef.permissionsorPageDef.rolesare not met. - Tenant-Awareness: The
TenantProvidercaptures the current organization from the user profile; all subsequent service calls are automatically scoped. - Layouts: The root
Layout.tsxprovides the standardized Sidebar + Topbar structure. Individual pages usePageShell(preferred) orPageLayoutto render headers, tabs, and actions. - PageShell Pattern: Pages declare a
PageDefinpages.config.ts, resolve it viausePageResolver(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
- Verify the new Provider is added to
src/contexts/providers.tsx. - Check if the new routing impacts the
LandingPageRedirectlogic inApp.tsx.