Notification Panel Component
Notification Panel Component
Component File: src/features/auth/components/NotificationPanel.tsx Usage: Display system notifications Access Level: All authenticated users (hidden for volunteer/donor portal users) Last Updated: March 13, 2026
---
Recent Updates (March 13, 2026)
- Cross-Tenant Notification Leak Fix (P0) — Fixed 8 notification creation paths in
stripe-webhook/index.ts,send-donation-alert/index.ts, and thenotify_on_new_donationDB trigger that used globally-scoped.eq('role', 'parent_org')queries withoutorganization_idfiltering. This caused ALLparent_orgusers across ALL tenants to receive notifications for every other tenant's Stripe events (webhook failures, refunds, payment failures, card expirations, event registrations, donation alerts). Fix: each path now resolves the parent org fromorganizations.parent_organization_idbefore queryingorganization_users. Deleted 18 leaked notification rows across 6 affected users. `send-webhook-failure-alert` is email-only to internal ops (WEBHOOK_FAILURE_OPS_EMAIL/ default inbox); it does not insert in-app notifications—`logWebhookFailure` instripe-webhookstill creates scoped in-app `webhook_failure` rows for parent org admins. - `webhook_failure` Alert Category —
getCategoryFromTypenow explicitly mapswebhook_failureto'alert'category (AlertTriangle icon, warning badge) instead of falling through to'system'(Package icon). - `NotificationType` Union Completeness — Added
webhook_failure,payment_failed,recurring_payment_failed,card_expiringto the TypeScript union type innotifications.ts.
Previous Updates (March 6, 2026)
- Orphaned Subscription Auto-Cancel (Webhook) —
handleInvoicePaymentFailedinstripe-webhook/index.tsnow auto-cancels orphaned subscriptions on failure. If a subscription ispast_dueorincompletewith zero paid invoices, it's cancelled immediately and the notification is suppressed. This is the safety net for subscriptions created by abandoned payment forms that slip past frontend cleanup. - DonationsManager Orphaned-Subscription Leak Fixed —
DonationsManager.tsxcalledcreatePaymentIntentfor recurring embedded payments but never stored the returnedsubscriptionIdor calledcancelAbandonedSubscription()on cancel. AddedpendingSubscriptionIdstate + cancel-on-abandon flow (matchingDonorLandingPage.tsxandDonationForm.tsxpattern). - Stripe Cleanup — Cancelled 20 orphaned/stale subscriptions (13 for Jacqueline Rae
cus_SfZLQfAvXrT0zu, 7 for Steven Leipzigcus_TsOcwfP5uT46Uwincluding his legitimate $1.00/month InFocus sub — cancelled indefinitely per admin request). Deleted 15 spamrecurring_payment_failednotification rows from March 6 retry wave.
Previous Updates (February 27, 2026)
- Donation Notification Deep-Link Fix — Fixed critical bug where clicking a donation notification did not open the donor's profile. Root cause:
navigateTo('donors', 'donors')used the pre-rename tool slug; after the slug standardization, the correct slug is'donor-management'. DonorsCRM never mounted, sopendingSearchResultwas never consumed. Fix: Changed both call sites (payment alert + general donor notification) tonavigateTo('donors', 'donor-management'). - Per-Notification Delete Removed — Removed individual hover trash icons from each notification row. Deletion now uses the single-flow pattern: trash icon in header enters selection mode → select items → confirm delete. This reduces visual clutter and matches the playbook §15 deletion UX spec.
- Auto-Read on Open — Opening the notification panel now automatically marks all unread notifications as read after 1.5 seconds. The manual "Mark all read" button has been removed since it's redundant. The brief delay lets the user see which notifications were new before the unread dot fades.
- Close Button WCAG Fix — Close button enlarged from
h-8 w-8tomin-h-[44px] min-w-[44px]per WCAG 2.5.5 touch target guidelines (playbook §23).
Previous Updates (February 27, 2026)
- Todo Notification Deep-Link — Clicking a
todo_overdue,todo_due_soon, ortodo_assignednotification now navigates to the dashboard and highlights the specific to-do item (scrolls into view with 3-second highlight). Previously, these notifications linked to/dashboardwith no deep-link, so users had no way to find the referenced to-do. Notification links now include?todoId={id}andhandleNotificationClickextracts the ID and callssetHighlightedTodoId()— matching the existingPendingItemsPanelpattern. - Todo Notification Terminology — Notification titles/messages changed from "Task" to "To-Do" to match the UI component naming ("To-Do List"). E.g., "Overdue Task" → "Overdue To-Do", "Task Due Tomorrow" → "To-Do Due Tomorrow".
Previous Updates (February 18, 2026)
- Clear All / Bulk Delete Fix — Fixed critical bug where "Clear all" and bulk delete confirmation buttons did nothing. Root cause:
AlertDialogAction(Radix primitive) auto-closes the dialog on click, unmounting the component before the asynconClickhandler can execute. Fix: ReplacedAlertDialogActionwith a regularButtonin both confirmation dialogs so the dialog stays open until the async operation completes, then closes programmatically. Panel now auto-closes after a successful "Clear all" since there's nothing left to show. - Card Expiry Donor Email Reminders — New donor-facing email system sends branded HTML reminders at 30 days, 14 days, and on expiration. Emails include "Update Payment Method" CTA linking to
https://alignmint.app(donor portal OTP login → Stripe billing portal). Dedup viaemail_logs(card_expiry_remindertype). Suppressed if card was updated in last 7 days. Cron:check-expiring-cardsdaily at 9 AM UTC. - Payment Failure Deep-Link Fix — Fixed critical bug where clicking a
payment_failed,recurring_payment_failed, orcard_expiringnotification did not navigate to the correct page. Root cause: notificationlinkstored as/donors/{uuid}(path segment) but click handler only parsed?donorId=query params. Now extracts donor UUID from path segments via regex. - Role-Aware Routing — Payment failure notifications now route
parent_orgusers to Donor Payment Management (administration→donor-payment-management) with donor context passed vianavigateTo(..., { donorId })(andsetSelectedDonorfor compatibility). Fund users route to DonorsCRM (donors→donor-management) with donor profile opened viapendingSearchResult. - Alert Category —
payment_failed,recurring_payment_failed, andcard_expiringnotification types now correctly categorized as'alert'(⚠️ AlertTriangle icon, warning badge) instead of falling through to'system'.
Previous Updates (February 14, 2026)
- Per-Notification Delete — Historical note from Jan 2026 (superseded on Feb 27 by selection-mode-only delete flow)
- Clear All Button — Historical note from Jan 2026 (superseded on Feb 27; current UX has no separate Clear All button)
- Bulk Delete Performance — Replaced N parallel
DELETErequests with a single.in('id', ids)bulk query via newdeleteNotificationsBulk()DB function - Error Recovery — If a delete or clear-all operation fails at the DB level, the panel now re-fetches notifications from the database to restore accurate state (prevents optimistic update divergence)
- i18n Compliance — All toast messages in
useNotificationActionsnow uset()translation keys instead of hardcoded English strings. New keys added to all 6 locale files:clearAll,clearAllTitle,clearAllConfirm,clearAllSuccess,clearAllFailed
Previous Updates (January 18, 2026)
- Fixed Donation Notification Deep-Link - Fixed bug where clicking a donation notification navigated to the Donor Hub landing page instead of the specific donor's profile. Root cause:
navigateTo('donors', null)was passingnullas the tool, so DonorsCRM never mounted to process thependingSearchResult. Fix: Changed tonavigateTo('donors', 'donors')so DonorsCRM mounts and opens the donor profile.
Previous Updates (January 17, 2026)
- Fixed Navigation Bug - Fixed critical bug where clicking "View" on a notification did not navigate to the target page. Root cause:
markNotificationAsReadwas using wrong column name (is_readinstead ofread), causing a database error that prevented navigation.
Previous Updates (January 16, 2026)
- Database-Synced Actions - All notification actions (mark as read, delete) now properly sync to the database via
useNotificationActionshook
Previous Updates (January 13, 2026)
- Click-to-Navigate - Clicking a notification now navigates to the relevant page (if link exists)
- Selective Delete - Users can now select specific notifications to delete instead of clearing all at once
- Selection Mode - Click the trash icon to enter selection mode, then select individual notifications
- Confirmation Dialog - Delete action now requires confirmation before permanently removing notifications
- Select All/Deselect All - Quick toggle to select or deselect all notifications
---
Overview
The Notification Panel displays system notifications, alerts, and updates to users. It shows donation alerts, approval requests, system messages, and activity updates. Users can mark notifications as read or selectively delete them.
UI Features
Notification Types
- Donation Alerts - New donations received
- Approval Requests - Pending approvals (expenses, reimbursements, hours)
- System Messages - Important system updates
- Activity Updates - Team activity notifications
Features
- Unread count badge
- Mark as read (individual or all)
- Click-to-navigate — Clicking a notification navigates to the related page
- Auto-read on open — All unread notifications are marked as read 1.5 seconds after opening the panel
- Multi-select delete — Click the trash icon in header to enter selection mode with checkboxes and confirmation dialog
- Select all / Deselect all toggle
- Real-time connection status indicator (Wifi icon)
- Timestamp display (relative: "just now", "5m ago", "2h ago", "3d ago")
Notification Structure
Notification Object
- id (uuid) - Unique identifier
- type (enum) - 'donation', 'approval', 'system', 'activity'
- title (string) - Notification title
- message (string) - Notification message
- read (boolean) - Read status
- timestamp (datetime) - When created
- link (string, nullable) - Navigation link
- action (string, nullable) - Action button text
State Management
Zustand Store (notificationSlice)
notifications- Array of notificationsunreadCount- Count of unread notificationsnotificationsRealtimeConnected- Real-time connection status
useNotificationActions Hook
The useNotificationActions hook provides database-synced actions:
import { useNotificationActions } from '../../../hooks/useNotificationActions';
const { markAsRead, markAllAsRead, deleteNotifications, clearAll } = useNotificationActions();| Function | Description |
|----------|-------------|
| `markAsRead(id)` | Mark single notification as read (syncs to DB) |
| `markAllAsRead()` | Mark all notifications as read (syncs to DB) |
| `deleteNotifications(ids)` | Delete specific notifications by ID array |
| `clearAll()` | Delete all notifications for current user |
Note: These functions update local state immediately (optimistic update) then persist to the database. If the DB operation fails, the hook automatically re-fetches notifications from the database to restore accurate state. deleteNotifications uses a single bulk .in('id', ids) query for performance.
Navigation Links by Notification Type
| Type | Navigation Target |
|------|-------------------|
| `expense_submitted` | `/fund-accounting?tool=expenses` |
| `expense_approved` | `/tools?tool=reimbursements` |
| `expense_revision_requested` | `/tools?tool=reimbursements` |
| `waiver_pending` | `/tools?tool=sign-waivers` |
| `waiver_signed` | `/administration?tool=waiver-management` |
| `todo_assigned` | `/dashboard?todoId={id}` → Navigates to dashboard, highlights specific to-do via `setHighlightedTodoId` || `todo_due_soon` | `/dashboard?todoId={id}` → Same as `todo_assigned` || `todo_overdue` | `/dashboard?todoId={id}` → Same as `todo_assigned` || `file_uploaded` | `/my-workspace/my-files` (path sync; internal tool `dropbox`) |
| `donation_received` | `/donors?tool=donor-management&donor={donor_id}` → Opens donor profile via `pendingSearchResult` || `payment_failed` | Parent org → `administration?tool=donor-payment-management` (via navigation context + selected donor). Fund user → `donors?tool=donor-management` (via `pendingSearchResult`) |
| `recurring_payment_failed` | Same as `payment_failed` |
| `card_expiring` | Same as `payment_failed` |
Related Documentation
- 01-HEADER.md
- 04-TODO-LIST.md
- 05-NOTIFICATION-SYSTEM.md - Historical backend notification system specification
Synced from IFMmvp-Frontend documentation: pages/components/03-NOTIFICATION-PANEL.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