Skip to main content

Per-Clinic Notification Preferences + New Crons

Problem

  1. All clinics get all email notifications regardless of whether they want them — waste of emails
  2. No missed-appointment emails exist yet
  3. No post-appointment feedback/Google Review email exists
  4. WhatsApp events fire toasts in the frontend even when the WhatsApp module is OFF for that clinic
  5. The admin notification settings page has only 2 toggles; needs 3 more

What We’re Building

  1. Extend notificationPreferences JSONB with 3 new fields + Google Review URL
  2. New cron: Missed Appointments email/WhatsApp
  3. New cron: Feedback & Review email
  4. Extend NotificationSettings.tsx with new toggles (plain language, no jargon)
  5. Gate WhatsApp toasts in the frontend behind module-ON check
  6. Superadmin: per-clinic notification preferences view

1. Extended notificationPreferences

Schema change (no migration needed — JSONB is additive)

New fields added to clinics.notificationPreferences:
{
  // existing
  stockAlertEmailEnabled?: boolean;
  eodEmailEnabled?: boolean;
  // new
  appointmentRemindersEnabled?: boolean;  // default true (opt-out)
  missedAppointmentsEnabled?: boolean;    // default false (opt-in, new feature)
  feedbackEmailEnabled?: boolean;         // default false (opt-in, new feature)
  googleReviewUrl?: string;               // clinic's Google review link
}
Default behaviour: if a key is absent, treat as true for reminders (preserves existing behaviour), false for new features (opt-in).

Type update

Update NotificationPreferences interface in serverComm.ts and clinics.ts schema type.

2. New Cron: Missed Appointments

File: server/src/scheduled/missed-appointments.ts

Logic

  1. Run every day at 9 AM UTC (14:00 PKT) — existing 0 9 * * * trigger
  2. Find appointments where:
    • status = 'confirmed' (never completed/cancelled)
    • appointmentDate = yesterday (PKT)
  3. Group by clinic
  4. For each clinic: check notificationPreferences.missedAppointmentsEnabled === true
  5. For each missed appointment:
    • Send email to patient: “We missed you — [clinic name]” — short, friendly message with rebook link
    • If WhatsApp module ON for clinic: send missed_appointment WhatsApp template (template already exists in whatsapp.ts:sendMissedAppointment())
  6. Mark appointment status = 'missed' after notifying

Guard

  • Skip if missedAppointmentsEnabled !== true
  • Skip if patient has no email
  • No duplicate sends: check if appointment already in missed status

3. New Cron: Feedback & Review Email

File: server/src/scheduled/feedback-email.ts

Logic

  1. Run every day at 9 AM UTC — existing 0 9 * * * trigger
  2. Find appointments where:
    • status = 'completed'
    • appointmentDate = 2 days ago (PKT) — give patient time before asking for review
    • No feedback email already sent (check feedbackEmailSentAt field on appointment, or use activity log)
  3. Group by clinic
  4. For each clinic: check notificationPreferences.feedbackEmailEnabled === true AND notificationPreferences.googleReviewUrl is set
  5. Send friendly email: “How was your visit? — [clinic name]” with Google Review button
  6. Mark appointment to prevent duplicate sends

Appointment schema addition

Add feedbackEmailSentAt timestamp to appointments table (via ensureAppointmentsSchema).

Email template

  • Subject: “How was your visit at [Clinic Name]?”
  • Body: short thank-you note + prominent “Leave a Review on Google” button
  • Footer: clinic address, phone

4. Admin Notifications UI

File: ui/src/components/settings/NotificationSettings.tsx

Current state

Two toggles: inventory alerts, EOD report.

New state — 5 toggles total

Section: Patient Emails
ToggleLabelDefault
appointmentRemindersEnabledAppointment remindersON
missedAppointmentsEnabledMissed visit follow-upsOFF
feedbackEmailEnabledPost-visit feedback & review requestOFF
When feedbackEmailEnabled is turned ON, show a text input:
“Google Review link — paste your clinic’s Google review URL here so we can include it in feedback emails”
Section: Staff Emails (existing)
  • Low stock alerts
  • End-of-day report

Language rules

  • No “cron”, “trigger”, “event”, “notification preference” — plain English only
  • Every toggle has a one-line subtitle explaining what it does and when it fires
  • Example: “Appointment reminders — We’ll remind patients by email (and WhatsApp if enabled) 24 hours before their visit.”

5. WhatsApp Toast Gating

Problem

WhatsApp-related toasts show in the UI regardless of whether the clinic has WhatsApp configured.

Solution

  1. Fetch WhatsApp module status once on clinic context load:
    • Already have GET /whatsapp/config which returns { isConfigured: boolean, ... }
    • Store whatsappEnabled: boolean in clinic context
  2. In every place that shows a WhatsApp toast or triggers a WhatsApp action in the UI:
    • Gate: if (!whatsappEnabled) return — no toast, no action shown
  3. Affected files: any component that renders WhatsApp send buttons or shows WhatsApp delivery status toasts

Implementation

  • Add whatsappEnabled to ClinicContext type
  • Fetch once in ClinicProvider alongside other clinic data
  • Export a useWhatsApp() hook that returns { enabled: boolean } for simple gating

6. Superadmin: Per-Clinic Notifications

Location: Superadmin portal → Clinic detail page → “Notifications” tab Shows a read-only summary of each clinic’s notification preferences:
  • Which emails are on/off
  • Google Review URL (truncated)
  • WhatsApp module status
Superadmin can toggle preferences directly (calls same PUT endpoint with superadmin auth).

Out of Scope

  • Per-doctor notification routing
  • SMS notifications
  • Patient-side notification opt-out portal
  • Cron execution logs/history UI (separate ops feature)