Context
src/lib/email.ts currently only logs OTPs to the console. Teachers receive zero notification when assigned to relief — they have to check the dashboard. Every competitor sends email at minimum.
Dive additionally pushes Telegram/WhatsApp; that's a separate P1 ticket. This issue covers email + the acknowledgement loop, which is the table-stakes baseline.
Scope
- Real email provider wired up (Postmark / Resend / string.sg notification service — pick whichever has MOE-friendly data residency).
- Send-on-assign: when a relief assignment is created or changed, notify the relief teacher with date, period, class, subject, room, and any lesson notes from the absent teacher.
- One-tap Acknowledge link in the email → marks assignment as acknowledged in DB → visible on admin dashboard.
- Resend / unacknowledged warning surfaced on dashboard.
- Notifier abstraction (`Notifier` interface) so Telegram/WhatsApp can slot in later without rewrites.
Acceptance criteria
- Assigning a teacher in src/app/api/relief-assignments triggers an email.
- Email contains a signed acknowledgement URL (no login required).
- `ReliefAssignment` has `acknowledgedAt` field; dashboard shows ✓ / pending.
- Admin can manually resend.
- OTP email path migrated to the same Notifier.
Refs
- docs/research/competitive-gap-analysis.md §4 items 2 & 4
- Eric's data-residency concern (§2 pain 6) — favour string.sg/MOE-hosted provider
Context
src/lib/email.ts currently only logs OTPs to the console. Teachers receive zero notification when assigned to relief — they have to check the dashboard. Every competitor sends email at minimum.
Dive additionally pushes Telegram/WhatsApp; that's a separate P1 ticket. This issue covers email + the acknowledgement loop, which is the table-stakes baseline.
Scope
Acceptance criteria
Refs