Donors
Donors
Component File: src/features/donors/components/DonorHub.tsx Route / navigation: Base path `/donors` (PageView donors). Tool selection: Zustand `donorTool` (DonorTool in src/store/types.ts), mirrored to `/donors/{donorTool}` (and `/donors/{donorTool}/{recordId}` for deep links); see navigationSlice.ts, navigationUrl.ts, and PageRouter.tsx. Access Level: Parent Org and Fund Users with Donor Hub access (position-based) Last Updated: March 30, 2026
Overview
The Donors section is a navigation landing page that provides access to all donor-related tools. It displays a grid of tool cards for managing donors, donations, giving overview, custom donation pages, and the donor portal.
Recent Changes
Donor summary type filters — CRM, Giving Overview, RPC (Mar 30, 2026)
- Behavior: Filters on
donors.donation_typeare exclusive for the labeled dropdowns: One-time onlyone-time, Recurring onlyrecurring, Both onlyboth(Giving Overviewget_giving_grid_page, Donors CRMget_donors_page/fetchDonorsPage, legacyfetchDonorsPaginated. Revenue Reconciliation donor rollups match the same semantics.) - Exception: Donor Payment Management “Active recurring” uses internal `recurring-all` →
IN ('recurring','both')so subscription workflows still include mixed-pattern donors. - KPIs: Summary tiles / stats that count “recurring donors” for dashboards may still use
IN ('recurring','both')— that is separate from the list filters (seeDEVELOPER-PLAYBOOK.mddonor-summary section). - Migrations:
supabase/migrations/20260430140000_giving_overview_exclusive_type_filters.sql,20260430150000_get_donors_page_exclusive_donation_type_filters.sql. TypeScript:DonorsPageDonationTypeFilterinsrc/lib/db/donors.ts.
Giving Overview RPC Fix + Load Error UX (Mar 22, 2026)
- P0 — `get_giving_grid_page` SQL error (42P01): The final
jsonb_aggfor donor rows referenced aliasdpwithoutFROM donor_pivoted dp, so the RPC threw on every call. The UI showed zeros and an empty grid (React Query error with no dedicated error state). Fix: Subquery(SELECT jsonb_agg(...) ORDER BY ... FROM donor_pivoted dp). Migration:supabase/migrations/20260322160000_fix_get_giving_grid_page_jsonb_agg_from.sql. - P1 — Error UX:
DonorGivingGridnow usesretry: 2, a fatal-loadEmptyState+ “Try again”, and a stale-data destructiveAlertwhen a refetch fails while previous data is still shown. New i18n:donorHub.givingGrid.loadErrorTitle,loadErrorDescription,retryLoad(all 7 locales). - P2 — Drill-down parity:
fetchDonationsForDonorInRangenow filterspayment_status = 'completed'andstatus <> 'voided'to match the grid RPC. - Files Modified:
DonorGivingGrid.tsx,src/lib/db/donations.ts,7 × donorHub.json, new migration SQL.
Giving Overview Cross-Org Inflation Fix (Feb 25, 2026)
- P0 — Step 2 missing `organization_id` filter:
fetchDonorGivingGridStep 2 fetched ALL donations for paginated donors without filtering by org. Cross-org donors (e.g., SoulFormation donating to both CWW and Deeper Walk) had their other-org donations counted in the CWW grid. CWW Jan 2026 showed $7,800.10 instead of correct $7,426.54 ($373.56 leaked from Deeper Walk + Bonfire). Dec 2025 had $11,020 leaked. 12-month total inflated by $20,283.56. Added.eq('organization_id', orgId)to Step 2 (matching Step 0 and Step 5 pattern). - Files Modified:
src/lib/db/donations.ts
Giving Overview Data & UX Fix (Feb 19, 2026)
- P0 — PostgREST 1000-row limit:
fetchDonorGivingGriddonations query had no.limit()— PostgREST silently capped at 1000 rows, truncating donation data for orgs with many donors. Added.limit(50000)(matches pattern used across all other donation queries indonations.ts). - P0 — Column truncation: Removed
MAX_VISIBLE_COLS(was 8 for month, 6 for year) that hard-capped visible columns via.slice(). AlldisplayColumnsnow render; table already hasoverflow-x-autofor horizontal scrolling. - P1 — Removed 6/12/24 pill: Replaced the 6 mo / 12 mo / 24 mo time range pills with a
MonthPickercomponent ("Starting from" label). Users can now select any start month up to 5 years back. Default: 12 months ago.monthsBackis computed dynamically from the selected start month to the current month. - P1 — Org-wide stats:
Total Giving,Avg Monthly, andActive This Monthstat tiles were previously computed from the current page of 100 donors only. AddedorgGrandTotalandactiveThisMonthfields toDonorGivingGridResult— backend now runs a separate org-wide aggregate query whentotalCount > pageSize. - P2 — Query gate: Replaced
!entitiesLoadingwithisMappingInitialized()per Playbook §35. - P3 — Dead type: Removed
MonthsBacktype alias (no longer needed after pill removal). - i18n: Added
donorHub.givingGrid.startingFromkey to all 6 locale files. - Files Modified:
DonorGivingGrid.tsx,src/lib/db/donations.ts, 6 ×donorHub.json
Giving Overview Table & Header Fix (Feb 17, 2026)
- P0 — PageHeader Color: Removed
titleStyle/subtitleStylewith--sidebar-section-manage— hub-page color pattern was incorrectly applied to a sub-tool page. Now uses defaulttext-foreground/text-muted-foregroundlike all other sub-tool pages. - P0 — Table Layout: Added
min-w-[1000px]to<Table>to enable horizontal scrolling instead of column compression. Made "Donor" column the flex column (removedw-[180px]). Bumped month columns fromw-[90px]→w-[100px]. - P0 — Column Header Regression: Replaced
formatMonthYear()(outputs "February 2026") with newformatMonthYearShort()(outputs "Feb 2025") — previous audit introduced long month names that overflowed 90px columns. - P1 — Sticky Totals Row: Changed
bg-muted/50→bg-mutedon sticky totals cell to prevent content bleed-through on horizontal scroll. - P1 — Column Headers: Added
whitespace-nowrapto all<TableHead>elements to prevent header text wrapping. - P1 — i18n: XLSX export sheet name now uses
t('donorHub.givingOverview')instead of hardcoded English. - New Utility: Added
formatMonthYearShort()tosrc/lib/dateUtils.tsfor compact pivot table column headers. - Files Modified:
DonorGivingGrid.tsx,src/lib/dateUtils.ts
Giving Overview Audit & Compliance Fix (Feb 17, 2026)
- P0 — Styling: Removed hardcoded
text-green-600 dark:text-green-400from "Active This Month" stat tile (plain count, not financial indicator — usestext-foregroundnow) - P0 — Desktop Gating: Fixed
DesktopOnlyWarningpattern from Fragment-sibling to standard nested pattern (matches DonorPageManager, MarketingCampaigns, etc.). Added customdescriptionprop. - P1 — i18n: Wired all ~30 hardcoded English strings to
t()via newdonorHub:givingGrid.*keys across all 6 locale files (en, de, es, fr, th, zh). Reusesactions:export,actions:sort,actions:filter,actions:clearFilters,actions:searchfrom shared namespace. - P1 — Dead Code: Removed unused
usePermissionsimport - P1 — Date Utils: Replaced 3 direct
toLocaleDateString()/toISOString().split('T')[0]calls withformatMonthYear(),formatDate(), andtoLocalDateString()fromsrc/lib/dateUtils.ts - P2 — Table: Added
table-fixed w-fullto pivot table. Changedmin-w-[Xpx]→w-[Xpx]on allTableHeadelements. - P2 — Semantic Colors: Changed missed recurring cell highlight from
bg-red-50 dark:bg-red-950/20tobg-destructive/5 dark:bg-destructive/10(theme-safe) - P3 — Dead CSS: Removed redundant
sm:space-y-6onhidden md:blockdiv (sm breakpoint never visible) - Files Modified:
DonorGivingGrid.tsx, 6 ×donorHub.json
Giving Overview: header sort + Last gift column (April 5, 2026)
- Sort UX: Toolbar sort dropdown removed. Donor (name A–Z / Z–A), Last gift (most recent first; second click resets to name A–Z), and Total (high / low) use clickable headers with the same affordance as other CRM tables (
GivingGridSortableTh+cycleSortPair). Sort bySelectremains for narrow widths only (sm:hidden). - Column: Last gift shows
lastDonationDateper row (fromget_giving_grid_page/DonorGivingGridRow); included in CSV/XLSX/PDF export headers. - Files:
DonorGivingGrid.tsx,donorHub.json(all locales —givingGrid.lastGift)
Giving Overview Tool Added (Feb 17, 2026)
- New Tool: "Giving Overview" (
giving-overview) added to Donor Hub. Displays a 12-month rolling grid of donor giving activity (donors as rows, months as columns), with color-coded cells for missed recurring donations, row-level totals, clickable donor names, stat tiles, by Month/by Year toggle, 6/12/24-month range selector, search, sort, filter, pagination, and CSV/XLSX/PDF export. - Data Layer (current):
fetchGivingGridPage()→ Postgres RPC `get_giving_grid_page` insrc/lib/db/donations.ts. Legacy multi-call `fetchDonorGivingGrid()` remains in the same file for reference / parity checks only — production UI uses the RPC. - Type: Added
'giving-overview'toDonorTooltype insrc/store/types.ts. - Routing: Lazy-loaded
DonorGivingGridinPageRouter.tsx. - Search Index: Added to
src/lib/searchIndex.ts(keywords: giving, grid, overview, donor elf). - i18n: Added
givingOverviewandgivingOverviewDesckeys to all 6donorHub.jsonlocale files. - Files Created:
src/features/donors/components/DonorGivingGrid.tsx - Files Modified:
store/types.ts,PageRouter.tsx,DonorHub.tsx,db/donations.ts,searchIndex.ts, 6 ×donorHub.json
Quick Actions Enhancement (Feb 17, 2026)
- 3 New Action Types:
first-time-donor-thank(Heart icon, green),lapsed-recurring(UserMinus icon, red — 2+ missed payments),large-donation(TrendingUp icon, green — >2× donor average). - Per-type icons: Each action type now has a distinct icon and color instead of all using AlertCircle.
- "View Giving Overview" link: Added to the bottom of QuickActionsCard, navigates to
donors/giving-overview. - Files Modified:
src/lib/db.ts(QuickActionItemtype,fetchQuickActionsData()),src/features/dashboard/components/QuickActionsCard.tsx
UI Features
Tool Cards (5 hub routes)
1. Donor Management (donor-management) — DonorsCRM 2. Donations (donations) — DonationsManager 3. Giving Overview (giving-overview) — DonorGivingGrid (desktop only) 4. Giving Pages (giving-pages) — DonorPageManager 5. Donor Portal (donor-portal) — DonorPortal (often volunteer/donor portal flows)
Features
- Grid layout (responsive: 1/2/3 columns)
- Icon-based tool cards
- Hover effects
- Visibility toggle (edit mode)
- Tool descriptions
- Click to navigate
Navigation Mapping
| Tool ID | Routes To | Component |
|---------|-----------|-----------|
| `donor-management` | Donors → Donors | DonorsCRM |
| `donations` | Donors → Donations | DonationsManager |
| `giving-overview` | Donors → Giving Overview | DonorGivingGrid |
| `giving-pages` | Donors → Giving Pages | DonorPageManager |
| `donor-portal` | Donors → Portal | DonorPortal |
Public Pages (No Hub Navigation)
| Component | URL | Description |
|-----------|-----|-------------|
| DonorLandingPage | `/{nonprofit}/{page-slug}` | Public donation page |State Management
Local State
None - pure navigation component
Global state (Zustand)
useAppStore:currentPage,donorTool,navigateTo('donors', toolId),setDonorTool- Hub grid visibility / tier:
HubGrid+usePermissions/useTierAccess(same pattern as other hubs)
Related Documentation
- 01-DONORS-CRM.md - Donor management
- 02-DONATIONS-MANAGER.md - Donation tracking
- 03-DONOR-PAGE-BUILDER.md - Create donation pages
- 04-DONOR-PORTAL.md - Donor self-service
- 05-DONOR-PAGE-MANAGER.md - Manage pages
- 06-DONOR-PAGE-PREVIEW.md - Preview pages
- 05-DONOR-PAYMENT-MANAGEMENT.md - Payment management
- 08-DONOR-LANDING-PAGE.md - Public donation page
Synced from IFMmvp-Frontend documentation: pages/donor-hub/00-DONOR-HUB.md
Ready to Get Started?
See how Alignmint can simplify your nonprofit's operations. Schedule a free demo with our team and we'll walk you through everything.
Questions? Email us at steven@getalignmint.org