Skip to main content

Quick Actions Card

Quick Actions Card

Component File: src/features/dashboard/components/QuickActionsCard.tsx Access Level: All authenticated users Last Updated: January 29, 2026 Status: ✅ Implemented

---

Overview

The Quick Actions Card is an addable dashboard component that surfaces actionable insights requiring director attention. It displays up to 5 items sorted by urgency (days overdue), with clickable jumpoffs to resolve each issue.

UI Features

Main Display

  • Title: "Quick Actions" with count badge
  • Action Items: Up to 5 items, sorted by days overdue (most urgent first)
  • Each Item Shows:
  • Status indicator (🔴 red for urgent, 🟡 yellow for attention needed)
  • Description (e.g., "John Smith hasn't made his monthly donation")
  • Context (e.g., "Expected Jan 15 • 5 days late")
  • Clickable jumpoff link
  • View All Link: If more than 5 items, show "View all X items" link

Empty State

When no action items exist:

✅ All caught up! No action items right now.

Action Types (v1)

1. Late Recurring Donor

Detection Logic:

// Donor has donation_type = 'recurring' or 'both'
// AND last_donation_date + recurring_frequency + 3 days < today
// AND last_donation_date is within the last 90 days (excludes churned donors)

function getExpectedDonationDate(lastDonation: Date, frequency: string): Date {
  const expected = new Date(lastDonation);
  switch (frequency) {
    case 'weekly': expected.setDate(expected.getDate() + 7); break;
    case 'monthly': expected.setMonth(expected.getMonth() + 1); break;
    case 'quarterly': expected.setMonth(expected.getMonth() + 3); break;
    case 'annually': expected.setFullYear(expected.getFullYear() + 1); break;
  }
  return expected;
}

function isDonationLate(expectedDate: Date, graceDays = 3): boolean {
  const today = new Date();
  const graceDate = new Date(expectedDate);
  graceDate.setDate(graceDate.getDate() + graceDays);
  return today > graceDate;
}

// 90-day lookback filter: excludes donors who haven't donated in 3+ months
// These are considered "churned" and not actionable quick actions
const maxLookbackDate = new Date();
maxLookbackDate.setDate(maxLookbackDate.getDate() - 90);

Display:

  • Icon: 🔴 (if > 7 days late) or 🟡 (3-7 days late)
  • Text: "{First Name} {Last Name} hasn't made their {frequency} donation"
  • Context: "Expected {date} • {X} days late"
  • Jumpoff: Navigate to donor profile in Donors CRM

Jumpoff Action:

setCurrentPage('donors');
setDonorTool('donors');
// Open donor detail view with donorId

2. Overdue Pledge

Detection Logic:

SELECT * FROM pledges 
WHERE expected_date < CURRENT_DATE 
  AND amount_received < amount
  AND status != 'cancelled'
ORDER BY expected_date ASC

Display:

  • Icon: 🟡
  • Text: "{Donor Name}'s pledge is overdue"
  • Context: "${amount} expected {date}"
  • Jumpoff: Navigate to Pledges in Accounting Hub

Jumpoff Action:

setCurrentPage('fund-accounting');
setAccountingTool('pledges');

3. Overdue Grant

Detection Logic:

SELECT * FROM grants_receivable 
WHERE expected_date < CURRENT_DATE 
  AND amount_received < amount
  AND status != 'cancelled'
ORDER BY expected_date ASC

Display:

  • Icon: 🟡
  • Text: "{Grant Name} payment pending"
  • Context: "${amount} expected {date}"
  • Jumpoff: Navigate to Grants Receivable in Accounting Hub

Jumpoff Action:

setCurrentPage('fund-accounting');
setAccountingTool('grants-receivable');

Future Action Types (v2+)

| Action Type | Detection | Jumpoff |
|-------------|-----------|---------|
| Overdue Todos | `todos.due_date < today AND completed = false` | Todo List |
| Unacknowledged Donations | Donations without thank-you email | Donor profile |
| Failed Recurring Payments | Stripe webhook data | Donor Payment Management |
| Budget Variance | Expenses > 110% of budget | Budget Report |
| Reconciliation Reminder | Bank account not reconciled in 30+ days | Reconciliation |

Data Requirements

QuickActionItem Interface

interface QuickActionItem {
  id: string;
  type: 'late-recurring-donor' | 'overdue-pledge' | 'overdue-grant';
  priority: 'urgent' | 'attention';  // urgent = red, attention = yellow
  title: string;                      // Main description
  context: string;                    // Secondary info (date, amount)
  daysOverdue: number;                // For sorting
  jumpoff: {
    page: PageView;
    tool?: string;
    params?: Record<string, string>; // e.g., { donorId: 'xxx' }
  };
}

interface QuickActionsData {
  items: QuickActionItem[];
  totalCount: number;  // Total items (may be > 5)
}

Sorting & Limiting

1. Sort by: daysOverdue descending (most urgent first) 2. Limit: Show top 5 items 3. View All: If totalCount > 5, show link to expanded view

Grace Period & Lookback

  • Recurring Donors: 3 days after expected date before showing alert
  • 90-Day Lookback: Only donors with last_donation_date within the last 90 days are shown. Donors who haven't donated in 3+ months are considered "churned" and excluded from quick actions.
  • Pledges/Grants: Show immediately when past expected date

Dashboard Registration

Store Types (src/store/types.ts)

// Add to DashboardComponent type
export type DashboardComponent = 
  | 'donations-chart' 
  | 'recent-donations' 
  | 'to-do-list' 
  | 'top-donors'
  | 'fund-summary'
  | 'quick-actions';  // NEW

// Add to ADDABLE_DASHBOARD_COMPONENTS
export const ADDABLE_DASHBOARD_COMPONENTS: DashboardComponent[] = [
  'top-donors',
  'fund-summary',
  'quick-actions',  // NEW
];

// Add to DASHBOARD_COMPONENT_INFO
export const DASHBOARD_COMPONENT_INFO: Record<DashboardComponent, { label: string; description: string }> = {
  // ... existing entries
  'quick-actions': { 
    label: 'Quick Actions', 
    description: 'Actionable alerts for late donations, overdue pledges, and more' 
  },
};

App.tsx Render Switch

case 'quick-actions':
  return <QuickActionsCard />;

Implementation Files

| File | Purpose |
|------|---------|
| `src/features/dashboard/components/QuickActionsCard.tsx` | UI component |
| `src/lib/db.ts` | Add `fetchQuickActionsData()` function |
| `src/hooks/useDashboardMetrics.ts` | Add `useQuickActions()` hook |
| `src/store/types.ts` | Register component type |
| `src/App.tsx` | Add render case |

UI Mockup

With Items

┌─────────────────────────────────────────────────────┐
│  ⚡ Quick Actions                              (3)  │
├─────────────────────────────────────────────────────┤
│  🔴 John Smith hasn't made his monthly donation    │
│     Expected Jan 15 • 5 days late                  │
│     View Donor →                                   │
├─────────────────────────────────────────────────────┤
│  🟡 Sarah Johnson's pledge is overdue             │
│     $500 expected Jan 20                           │
│     View Pledge →                                  │
├─────────────────────────────────────────────────────┤
│  🟡 Community Foundation grant payment pending     │
│     $10,000 expected Jan 10                        │
│     View Grant →                                   │
├─────────────────────────────────────────────────────┤
│  View all 7 items →                                │
└─────────────────────────────────────────────────────┘

Empty State

┌─────────────────────────────────────────────────────┐
│  ⚡ Quick Actions                                   │
│                                                     │
│  ✅ All caught up! No action items right now.      │
│                                                     │
└─────────────────────────────────────────────────────┘

Styling

Following existing dashboard component patterns:

// Priority colors
const priorityStyles = {
  urgent: 'text-red-600 dark:text-red-400',
  attention: 'text-amber-600 dark:text-amber-400',
};

// Card styling
<Card className="hover:shadow-md transition-shadow">
  <CardHeader className="pb-2">
    <div className="flex items-center justify-between">
      <CardTitle className="flex items-center gap-2">
        <Zap className="h-5 w-5" />
        Quick Actions
      </CardTitle>
      {totalCount > 0 && (
        <Badge variant="secondary">{totalCount}</Badge>
      )}
    </div>
  </CardHeader>
  <CardContent>
    {/* Action items */}
  </CardContent>
</Card>

Related Documentation


Synced from IFMmvp-Frontend documentation: pages/dashboard/QUICK-ACTIONS-CARD.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