Skip to main content

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_type are exclusive for the labeled dropdowns: One-time only one-time, Recurring only recurring, Both only both (Giving Overview get_giving_grid_page, Donors CRM get_donors_page / fetchDonorsPage, legacy fetchDonorsPaginated. 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 (see DEVELOPER-PLAYBOOK.md donor-summary section).
  • Migrations: supabase/migrations/20260430140000_giving_overview_exclusive_type_filters.sql, 20260430150000_get_donors_page_exclusive_donation_type_filters.sql. TypeScript: DonorsPageDonationTypeFilter in src/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_agg for donor rows referenced alias dp without FROM 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: DonorGivingGrid now uses retry: 2, a fatal-load EmptyState + “Try again”, and a stale-data destructive Alert when a refetch fails while previous data is still shown. New i18n: donorHub.givingGrid.loadErrorTitle, loadErrorDescription, retryLoad (all 7 locales).
  • P2 — Drill-down parity: fetchDonationsForDonorInRange now filters payment_status = 'completed' and status <> '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: fetchDonorGivingGrid Step 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: fetchDonorGivingGrid donations 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 in donations.ts).
  • P0 — Column truncation: Removed MAX_VISIBLE_COLS (was 8 for month, 6 for year) that hard-capped visible columns via .slice(). All displayColumns now render; table already has overflow-x-auto for horizontal scrolling.
  • P1 — Removed 6/12/24 pill: Replaced the 6 mo / 12 mo / 24 mo time range pills with a MonthPicker component ("Starting from" label). Users can now select any start month up to 5 years back. Default: 12 months ago. monthsBack is computed dynamically from the selected start month to the current month.
  • P1 — Org-wide stats: Total Giving, Avg Monthly, and Active This Month stat tiles were previously computed from the current page of 100 donors only. Added orgGrandTotal and activeThisMonth fields to DonorGivingGridResult — backend now runs a separate org-wide aggregate query when totalCount > pageSize.
  • P2 — Query gate: Replaced !entitiesLoading with isMappingInitialized() per Playbook §35.
  • P3 — Dead type: Removed MonthsBack type alias (no longer needed after pill removal).
  • i18n: Added donorHub.givingGrid.startingFrom key 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/subtitleStyle with --sidebar-section-manage — hub-page color pattern was incorrectly applied to a sub-tool page. Now uses default text-foreground/text-muted-foreground like 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 (removed w-[180px]). Bumped month columns from w-[90px]w-[100px].
  • P0 — Column Header Regression: Replaced formatMonthYear() (outputs "February 2026") with new formatMonthYearShort() (outputs "Feb 2025") — previous audit introduced long month names that overflowed 90px columns.
  • P1 — Sticky Totals Row: Changed bg-muted/50bg-muted on sticky totals cell to prevent content bleed-through on horizontal scroll.
  • P1 — Column Headers: Added whitespace-nowrap to 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() to src/lib/dateUtils.ts for 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-400 from "Active This Month" stat tile (plain count, not financial indicator — uses text-foreground now)
  • P0 — Desktop Gating: Fixed DesktopOnlyWarning pattern from Fragment-sibling to standard nested pattern (matches DonorPageManager, MarketingCampaigns, etc.). Added custom description prop.
  • P1 — i18n: Wired all ~30 hardcoded English strings to t() via new donorHub:givingGrid.* keys across all 6 locale files (en, de, es, fr, th, zh). Reuses actions:export, actions:sort, actions:filter, actions:clearFilters, actions:search from shared namespace.
  • P1 — Dead Code: Removed unused usePermissions import
  • P1 — Date Utils: Replaced 3 direct toLocaleDateString()/toISOString().split('T')[0] calls with formatMonthYear(), formatDate(), and toLocalDateString() from src/lib/dateUtils.ts
  • P2 — Table: Added table-fixed w-full to pivot table. Changed min-w-[Xpx]w-[Xpx] on all TableHead elements.
  • P2 — Semantic Colors: Changed missed recurring cell highlight from bg-red-50 dark:bg-red-950/20 to bg-destructive/5 dark:bg-destructive/10 (theme-safe)
  • P3 — Dead CSS: Removed redundant sm:space-y-6 on hidden md:block div (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 by Select remains for narrow widths only (sm:hidden).
  • Column: Last gift shows lastDonationDate per row (from get_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` in src/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' to DonorTool type in src/store/types.ts.
  • Routing: Lazy-loaded DonorGivingGrid in PageRouter.tsx.
  • Search Index: Added to src/lib/searchIndex.ts (keywords: giving, grid, overview, donor elf).
  • i18n: Added givingOverview and givingOverviewDesc keys to all 6 donorHub.json locale 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 (QuickActionItem type, 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


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

Ready to get started?Start Plus Trial