Skip to main content

Notification System

Architecture flow

User action (mutation succeeds) → showSuccessToast(title, message) enqueues to Zustand store → Sonner toast (if enabled) → NotificationFlusher picks up queue → createNotification GraphQL mutation → persisted to app_notification table → bell badge polls userUnreadNotificationCount every 30s → user opens panel → immediate refetch.

Frontend files

  • src/stores/notification-store.ts — Zustand store: unread count, flush queue, settings
  • src/lib/show-notifications.tsxshowSuccessToast, showFailedToast, showInfoToast, showWarningToast
  • src/features/notifications/notification-flusher.tsx — flushes queue to backend, polls unread count every 30s
  • src/features/notifications/notification-bell.tsx — bell icon with unread badge in header
  • src/features/notifications/notification-panel.tsx — Sheet panel: list, mark-read, settings
  • src/features/notifications/hooks/use-notifications.ts — all GraphQL hooks
  • src/components/layout/authenticated-layout.tsx — mounts <NotificationFlusher /> and <NotificationBell />

Notification message format

The message field uses · as separator: "Domain · Application Title · Visibility". Examples: "Application · My Tax App · Internal", "Document · My Tax App · External", "My Tax App" (plain).

Where notifications are fired

  • Comment hooks: useCreateApplicationComment(applicationId, applicationTitle?)applicationTitle must be passed by the caller
  • Application CRUD: useCreateApplication, useUpdateApplication
  • Applicant hooks: use useApplicationContext() internally
  • Document review: useReviewApplicationDocument(applicationId, applicationTitle?)