Funds

Comprehensive manual QA testing plan for funds — organizational buckets for campaigns (~160 tests).

Context#

Comprehensive manual QA testing plan for funds — organizational buckets that group campaigns for reporting and CRM integration. Every campaign belongs to a fund. A special General fund exists by default and cannot be edited or deleted. When a custom fund is deleted, its campaigns are automatically reassigned to General.


How to Use This Plan#

  • Work through each section sequentially
  • For every setting: change it, save, refresh the page, and confirm it persisted
  • Mark each item pass/fail as you go
  • Pre-requisites: At least 2-3 campaigns should exist to test linking/reassignment

Key Concepts#

ConceptDetails
General FundSystem default fund (ID: fund_0000000000000000). Cannot be edited or deleted. Auto-receives unassigned campaigns. Marked with lock icon
Custom FundsUser-created funds with name, color, internal note, and optional external ID. Fully editable and deletable
Campaign LinkCampaigns reference a fund via fund_id. Every campaign must belong to exactly one fund
Deletion BehaviorDeleting a custom fund moves all its campaigns to the General fund
FundChipColored chip component used in campaign lists showing fund name with fund color
External CRM IDOptional tracking ID for syncing transactions to an external CRM (e.g., Salesforce)

1. Funds List Page#

Route: /dashboard/{org}/finances/funds

1.1 Data Table Display#

  • Page loads and displays all funds in a table
  • Columns visible:
    • Name — sortable, title variant. Shows lock icon next to "General" fund
    • Raised — currency-formatted amount (e.g., $1,234.56), right-aligned, sortable
    • Campaigns — campaign count, right-aligned, sortable
    • External ID — CRM identifier text, sortable
    • Created — formatted date in org timezone, sortable
    • ID — fund identifier, sortable
  • All column data matches expected values for each fund
  • General fund always present in list
  • Lock icon visible only on General fund row
  • Currency formatting is correct across all funds

1.2 Sorting#

  • Default sort: Amount Raised (descending)
  • Name — click to sort alphabetically asc/desc
  • Amount Raised — click to sort by raised amount
  • Campaigns — click to sort by campaign count
  • External ID — click to sort
  • Created Date — click to sort by creation date
  • Sorting persists in URL params
  • Sort order survives page refresh

1.3 Filtering#

  • Amount Raised (range filter):
    • Set min amount → filters correctly
    • Set max amount → filters correctly
    • Set both min and max → range filter works
  • Campaigns (count range filter):
    • Set min campaign count → filters correctly
    • Set max campaign count → filters correctly
    • Set both → range filter works
  • Combining both filters works correctly
  • Clearing filters restores full list
  • Filter state persists in URL params
  • Search by fund name — matches correctly (partial match)
  • Search by fund ID — matches correctly (prefix match)
  • Clear search restores full list
  • Search works in combination with filters

1.5 Pagination#

  • Pagination controls appear when funds exceed page size
  • Page size options: 20, 50, 100
  • Navigating pages works correctly
  • Page state persists in URL

1.6 Mobile Responsive#

  • Resize to mobile width — table switches to card view
  • Mobile card shows:
    • Fund name (with lock icon if General)
    • Raised amount (formatted with currency)
    • Campaign count chip (e.g., "3 Campaigns")
    • Actions menu accessible
  • Skeleton loading state shows while data fetches
  • Card data matches table data

1.7 Row Actions#

  • On a custom (non-General) fund:
    • "Edit" → navigates to fund detail page
    • "Delete" → opens DeleteFundModal
  • On the General fund:
    • "Edit" → navigates to fund detail page (shows read-only view)
    • "Delete" action is NOT available (General cannot be deleted)

1.8 Empty States#

  • No funds (except General): Shows empty state with "Create new fund" button
  • Filters applied, no results: Shows filter empty state with "Clear Filters" button
  • Error loading: Shows error state with retry button

1.9 Add Fund Button#

  • "Add Fund" button visible at top of page
  • Clicking opens NewFundModal

2. Fund Creation#

2.1 New Fund Modal#

  • Click "Add Fund" button — modal opens
  • Name field:
    • Required — cannot submit empty
    • Max 40 characters enforced
    • Placeholder: "Enter fund name"
  • Internal Note field:
    • Optional textarea (3 rows)
    • Placeholder: "Enter internal note"
    • Can be left empty
  • No color field in creation modal (set on detail page)
  • No external ID field in creation modal (set on detail page)
  • Create button:
    • Disabled until name is provided
    • Shows loading state during submission
    • Cannot close modal during submission
  • On success:
    • Modal closes
    • Funds list cache invalidated
    • Navigates to new fund's detail page
    • New fund appears in funds list
  • Cancel button closes modal without creating

3. Fund Detail Page — General Fund#

Route: /dashboard/{org}/finances/funds/{generalFundId}

3.1 Header#

  • Fund name "General" displays as title
  • Lock icon visible next to name
  • Breadcrumb links back to "Funds" list
  • Footer shows: "${amount} raised" with fund currency

3.2 Read-Only Callout#

  • Callout banner displayed at top: "General is your default fund. Unassigned campaigns go here automatically. This fund can't be edited or deleted."
  • No editable form fields shown
  • No color picker shown
  • No internal note field shown
  • No external ID field shown

3.3 Linked Campaigns Card#

  • Card title shows: "{count} Linked Campaign(s)"
  • Displays first 5 campaigns as clickable links to campaign detail pages
  • Each link shows campaign name
  • If > 5 campaigns: "See all" link appears → navigates to campaigns list filtered by this fund (/dashboard/{org}/fundraising/campaigns?fund={fundId})
  • If 0 campaigns: Empty state message: "Visit the Campaigns page to create a new campaign or edit an existing one to assign it to this fund"
  • Campaign links navigate to correct campaign detail pages

3.4 No Delete Card#

  • No "Delete Fund" card or button shown for General fund

4. Fund Detail Page — Custom Funds#

Route: /dashboard/{org}/finances/funds/{fundId}

4.1 Header#

  • Fund name displays as title (no lock icon)
  • Breadcrumb links back to "Funds" list
  • Footer shows: "${amount} raised" with fund currency

4.2 Main Details Card (Editable)#

  • Name field:
    • Shows current fund name
    • Change name → Save → Refresh → Name persisted
    • Empty name → validation error (save disabled)
    • Max 40 characters enforced
    • Name updates in header after save
    • Name updates in funds list
    • Name updates in FundChip wherever displayed (campaign list, etc.)
  • Color field (color picker):
    • Shows current color swatch + hex input
    • Click swatch → color picker opens
    • Pick a color → hex value updates
    • Type valid hex → color swatch updates
    • Change color → Save → Refresh → Color persisted
    • Color required — cannot save without color
    • Invalid hex → validation error shown
    • Color reflects in FundChip across all views (background + text color)
  • Internal Note field (textarea):
    • Shows current internal note (or empty)
    • 3 rows visible
    • Enter note → Save → Refresh → Note persisted
    • Clear note → Save → Refresh → Note cleared
    • Optional — can save with empty note

Save Behavior#

  • Save button disabled until changes are made
  • Save button disabled if validation fails (empty name, missing color)
  • Both name AND color must be set for save to be enabled
  • Saving shows loading state
  • Success toast appears after save
  • Error toast appears on failure
  • Funds cache invalidated after save

4.3 Linked Campaigns Card (Read-Only)#

  • Card title shows: "{count} Linked Campaign(s)"
  • Displays first 5 campaigns as clickable links to campaign detail pages
  • Each link shows campaign name
  • If > 5 campaigns: "See all" link appears → navigates to campaigns list filtered by this fund (/dashboard/{org}/fundraising/campaigns?fund={fundId})
  • If 0 campaigns: Empty state message with link to Campaigns page
  • Campaign links navigate to correct campaign detail pages
  • Count matches actual number of campaigns in this fund

4.4 External CRM ID Card (Editable)#

  • Description: "Add a tracking ID from your external CRM. All transactions from this fund's campaigns will be synced to this ID."
  • External ID field:
    • Text input
    • Shows current external ID (or empty)
    • Enter ID → Save → Refresh → ID persisted
    • Clear ID → Save → Refresh → ID cleared
    • Optional — can save with empty ID
  • Save button independent from Main Details card (or same card — verify)
  • Success toast appears after save

4.5 Delete Fund Card#

  • Description: "Permanently delete this fund. All associated campaigns and transactions will be automatically re-assigned to your 'General' fund."
  • Delete button is red/destructive
  • Clicking opens DeleteFundModal

5. Fund Deletion#

5.1 Delete Fund Modal#

  • Shows fund name (read-only)
  • Destructive warning callout: "Campaigns assigned to this fund will be moved to the General fund."
  • Delete button is red/destructive
  • Shows loading state during deletion
  • Cannot close modal during deletion
  • On success:
    • Success toast: "Fund deleted"
    • Funds list cache invalidated
    • Navigates back to funds list (or triggers onSuccess callback)
    • Fund disappears from list
  • Cancel button closes modal without deleting

5.2 Campaign Reassignment After Deletion#

  • Create a custom fund with 2-3 campaigns assigned to it
  • Delete the fund
  • Verify: All campaigns now show "General" as their fund in campaigns list
  • Verify: General fund's campaign count increased by the number of reassigned campaigns
  • Verify: General fund's linked campaigns card shows the reassigned campaigns
  • Verify: Each reassigned campaign's settings page shows "General" as parent fund
  • Verify: FundChip for each campaign updated to General fund's appearance

5.3 Deletion Edge Cases#

  • Delete fund with 0 campaigns → clean deletion (no reassignment needed)
  • Delete fund with raised > 0 → still allowed (no raise restriction for funds)
  • Try to navigate to a deleted fund's URL → appropriate error/redirect
  • Delete fund while a campaign is open that belongs to it → campaign should show General fund on next load

6. Fund-Campaign Relationship#

6.1 New Campaign Default Fund#

  • Create a new campaign via New Campaign Modal
  • Fund dropdown shows all available funds
  • Default selection is General fund
  • Select a custom fund → Save → Campaign created with that fund
  • Don't change fund → Campaign created with General fund
  • Verify: Campaign appears in the selected fund's linked campaigns

6.2 Reassigning Campaigns Between Funds#

  • Go to a campaign's detail page → Settings tab → Financial card
  • Parent Fund dropdown shows all available funds
  • Current fund is pre-selected
  • Change to a different fund → Save → Refresh → Persisted
  • Verify: Campaign now appears in the new fund's linked campaigns
  • Verify: Campaign removed from the old fund's linked campaigns
  • Verify: Campaign list FundChip updated with new fund's name and color
  • Verify: Old fund's campaign count decremented
  • Verify: New fund's campaign count incremented

6.3 FundChip Display in Campaign List#

  • Campaign assigned to custom fund → FundChip shows fund name
  • Chip background: fund color at 20% opacity
  • Chip text: darkened version of fund color
  • Campaign assigned to General fund → FundChip shows "General"
  • Fund name change → chip text updates across campaigns list
  • Fund color change → chip color updates across campaigns list

6.4 Campaign List Fund Filter#

  • Go to campaigns list page
  • Fund filter shows all available funds as toggle options
  • Select one fund → only campaigns in that fund shown
  • Select multiple funds → campaigns in any selected fund shown
  • Clear fund filter → all campaigns shown
  • Filter persists in URL params

7. Cross-Cutting Concerns#

7.1 Save Behavior#

  • Save button disabled until changes are made
  • Save button disabled if validation fails
  • Saving shows loading state (spinner/disabled)
  • Success toast appears after save
  • Error toast appears on failure
  • Form data not lost on error

7.2 Loading States#

  • Funds list shows loading state while fetching
  • Fund detail page shows loading state while fetching
  • Linked campaigns card shows loading state

7.3 Error Handling#

  • Network error during save → error toast
  • Network error during load → error state with retry
  • Server validation error → displayed to user

7.4 URL State Persistence#

  • Funds list: filters, sort, pagination, search persist in URL params
  • Copy/paste URL with params → same view loads
  • Browser back/forward navigates between filter states

7.5 Data Consistency#

  • Create fund → appears in funds list immediately
  • Edit fund name → list reflects new name
  • Edit fund color → list and campaign FundChips reflect new color
  • Delete fund → disappears from list, campaigns reassigned
  • Campaign assigned to fund → linked campaigns count increments
  • Campaign unassigned from fund → linked campaigns count decrements
  • Fund appears in campaign creation modal's fund dropdown
  • Fund appears in campaign settings Financial card's fund dropdown

7.6 Mobile Responsiveness#

  • List page renders as cards on mobile
  • Detail page stacks cards vertically on mobile
  • Color picker works on mobile
  • Modal displays correctly on mobile

8. End-to-End Flows#

8.1 Create Fund and Assign Campaigns#

  1. Click "Add Fund" → modal opens
  2. Enter fund name (e.g., "Ramadan 2026")
  3. Optionally enter internal note (e.g., "Ramadan fundraising bucket")
  4. Submit → redirected to new fund detail page
  5. Set a color (e.g., green #22C55E) → Save
  6. Set external CRM ID (e.g., "CRM-RAM-2026") → Save
  7. Go to an existing campaign → Settings → Financial card
  8. Change parent fund to "Ramadan 2026" → Save
  9. Go back to "Ramadan 2026" fund detail page
  10. Verify: Campaign appears in linked campaigns card
  11. Verify: Campaign count is 1
  12. Go to campaigns list → verify campaign shows green "Ramadan 2026" FundChip
  13. Create a new campaign → select "Ramadan 2026" as fund
  14. Verify: Fund detail now shows 2 linked campaigns

8.2 Delete Fund and Verify Reassignment#

  1. Ensure a custom fund has 3 campaigns assigned
  2. Note the campaign names
  3. Delete the fund via detail page
  4. Verify: Fund no longer in funds list
  5. Verify: All 3 campaigns now show "General" fund in campaigns list
  6. Verify: General fund linked campaigns includes the 3 campaigns
  7. Verify: Each campaign's Settings → Financial card shows "General" as parent fund
  8. Verify: FundChip color updated to General fund's appearance

8.3 General Fund Immutability#

  1. Navigate to General fund detail page
  2. Verify: Read-only callout is shown
  3. Verify: No editable form fields
  4. Verify: No external ID card
  5. Verify: No delete button
  6. Go to funds list → verify no "Delete" action on General row
  7. Delete a custom fund → verify campaigns go to General
  8. Verify: General fund's campaign count reflects the reassignment
  9. Create a campaign without changing fund → verify it defaults to General

8.4 Color Propagation#

  1. Create a fund with a distinctive color (e.g., bright red #FF0000)
  2. Assign a campaign to this fund
  3. Go to campaigns list → verify FundChip shows red color (20% opacity background, darkened text)
  4. Go back to fund detail → change color to blue #0000FF → Save
  5. Go to campaigns list → verify FundChip now shows blue color
  6. Verify: Color update propagated without editing any campaign

8.5 External CRM ID Workflow#

  1. Create a fund
  2. Go to fund detail → External CRM ID card
  3. Enter an external ID (e.g., "SF-FUND-001") → Save
  4. Refresh → verify ID persisted
  5. Assign campaigns to this fund
  6. Verify: Transactions from these campaigns should sync to this external ID (backend behavior — check if any UI indicator exists)
  7. Clear the external ID → Save → Refresh → verify cleared
  8. Verify: Funds list shows empty External ID column for this fund

8.6 Fund as Campaign Filter#

  1. Create 2 custom funds with different colors
  2. Assign 2 campaigns to Fund A, 3 campaigns to Fund B, keep some in General
  3. Go to campaigns list
  4. Filter by Fund A → verify only 2 campaigns shown
  5. Filter by Fund B → verify only 3 campaigns shown
  6. Filter by General → verify remaining campaigns shown
  7. Filter by Fund A + Fund B → verify 5 campaigns shown
  8. Clear filter → verify all campaigns shown
  9. Verify filter persists in URL → copy URL, open in new tab, same filter active

8.7 Edge Cases#

  1. Fund name at max length (40 chars) → saves and displays correctly (truncated if needed in chips)
  2. Fund with a very long internal note → saves and displays in textarea
  3. Delete a fund with 0 campaigns → clean deletion
  4. Delete a fund with raised > 0 → still works (no raise restriction)
  5. Create multiple funds and verify sorting works correctly across all columns
  6. Fund with special characters in name → saves and displays correctly
  7. Multiple funds with similar names → both distinct in list and dropdowns
  8. Assign all campaigns to one fund → General fund has 0 campaigns (still works)

Verification Checklist#

AreaTotal TestsPassFailNotes
Funds List Page~30
Fund Creation~12
General Fund Detail~10
Custom Fund Detail~25
Fund Deletion~12
Fund-Campaign Relationship~20
Cross-Cutting~15
E2E Flows~35
TOTAL~160