Documentation Index
Fetch the complete documentation index at: https://grantmaster.dev/llms.txt
Use this file to discover all available pages before exploring further.
Common Gotchas
Known pitfalls for developers and AI agents working in the GrantMaster codebase. These are enforced by ESLint rules, convention, or hard-won experience.
Collection Name Mismatches
The Firestore collection name does not always match the domain concept. These trip up everyone:
| Collection Name | Stores | Domain Feature |
|---|
employees | User documents | Users / HR |
timesheets | JournalEntry documents | Journals / Time Tracking |
retrospectives | MonthlyJournalSubmission documents | Journal Submissions |
Always check src/core/firestoreCollections.ts for the canonical mapping.
Deprecated APIs
| Deprecated | Use Instead | Notes |
|---|
useValidatedForm | useZodForm from @/hooks/useZodForm | Provides schema validation, toast notifications, handleSubmit, isSubmitting, isDirty |
useOrganization | useTenant() from @/contexts/TenantContext | Canonical way to access org context |
Styling Rules (ESLint-enforced)
The no-legacy-tailwind-palette rule blocks builds if you use wrong color tokens:
| Never Use | Use Instead | Semantic Role |
|---|
gray-* | slate-* | Neutral palette |
green-* | emerald-* | Success states |
red-* | rose-* | Error states |
blue-* for primary | primary-* | Primary actions |
| — | amber-* | Warning states |
Dark mode backgrounds: Use dark:bg-{color}-500/10 for subtle tints.
Component Rules (ESLint-enforced)
These rules prevent design system bypass:
| Rule | What It Blocks | Use Instead |
|---|
no-raw-button-with-design-system | Raw <button> with inline classes | <Button> from @/components/ui/button |
no-raw-text-input-with-design-system | Raw <input> / <textarea> | <Input> / <Textarea> from @/components/ui/ |
no-status-badge-bypass | Custom status badges | <StatusBadge> from @/components/ui/StatusBadge |
Always use mapDomainStatus() to convert domain strings (e.g. 'approved') to the 6 base StatusType values before passing to <StatusBadge>.
Architecture Rules (ESLint-enforced)
| Rule | What It Blocks | Correct Pattern |
|---|
no-direct-firestore-import | Importing Firestore directly | Use typed refs from src/core/firestoreCollections.ts |
no-direct-onsnapshot | Calling onSnapshot directly | Use the service layer |
no-eventbus-in-components | EventBus usage in React components | Emit events only from services |
require-firestore-limit | Firestore queries without limit() | Always add explicit limit() to every query |
no-derived-state | Stale derived state patterns | Compute derived values during render |
prefer-typed-firestore-refs | Untyped collection references | Use canonical refs from firestoreCollections |
Port Conflicts
These ports are hardcoded and must not be changed:
| Service | Port | Notes |
|---|
| Vite Dev Server | 3000 | Hardcoded for Firebase integration — never change |
| Firebase Emulator UI | 4000 | |
| Functions Emulator | 5001 | |
| Firestore Emulator | 8080 | |
| Auth Emulator | 9099 | |
| Storage Emulator | 9199 | |
Never start Firebase emulators or Vite dev server directly — ask the user if they’re already running to avoid port conflicts.
UI Conventions
Common mistakes when building pages:
- Always wrap page content in
<PageLayout> — never build page structure manually
- Always use
<PageHeader> for page titles — never manual <h1>
- Always use
<PageTabs> and render tab content externally — never pass content via the tab’s content prop
- Always derive major-section tab state from the router — never keep
/overview, /onboarding, /AI, or /workflow tab selection in shared component state
- Always keep breadcrumbs and titles distinct — breadcrumbs show ancestors, title shows the active page/tab once
- Always use
<SecondarySidebar> for left-side navigation — never build custom sidebar navs inline
- Icons:
lucide-react only. Sizes: w-4 h-4 (buttons/badges), w-5 h-5 (headers), w-8 h-8 (stat cards)
- Use
cn() from @/lib/utils for class merging — never string concatenation
Provider Hierarchy
The provider order matters. Three tiers:
- GlobalProviders — auth, theme, i18n, query client
- DomainProviders — tenant, notifications, feature flags
- FeatureProviders — feature-specific contexts (wrapped at route level)
Adding a provider at the wrong tier can cause missing context errors or unnecessary re-renders.
- Use
useZodForm (not useValidatedForm) — see Deprecated APIs above
- Zod schemas live in
src/schemas/ for shared schemas or src/features/[feature]/types/ for feature-specific
- Use
FormFieldController from useZodForm for controlled inputs
- Use
<FormSection>, <FormGrid>, <FormField>, <FormActions> for layout — never build form layouts manually