* feat(notifications): add email notification preferences
Implement the email notification preferences PRD with org-level defaults
and personal opt-outs enforced at the email enqueue chokepoint.
- Add toggle map, profile.settings jsonb, and shouldSendEmail resolver
- Extend profile and organization update APIs with emailNotifications
- Pass preference context through all toggleable email send call sites
- Add Settings > Notifications page with personal and org admin sections
Co-authored-by: Rotimi Best <[email protected]>
* feat(notifications): group email toggles into course, cohort, and enrollment sections
Organize personal and org notification settings into scannable sections
so users can find course- or cohort-related emails quickly.
Co-authored-by: Rotimi Best <[email protected]>
* fix(notifications): use Exercise instead of Quiz in toggle copy
Co-authored-by: Rotimi Best <[email protected]>
* fix(notifications): only mark certification email sent when enqueued
Skip setMemberCertificationEmailSent when notification preferences or
missing Redis prevent the completion email from being queued, so the
email can still send if preferences change later.
Co-authored-by: Rotimi Best <[email protected]>
* fix(notifications): address PR review feedback
- Add EmailPreferenceLookupCache to avoid N+1 settings lookups in batch
processors and multi-recipient email enqueue paths
- Replace $effect store hydration in notifications settings with onMount
and explicit sync after save
- Remove thin api email-preferences re-export; import from db queries
- Hide tutor-only personal toggles (newStudent, newSubmission) for students
Co-authored-by: Rotimi Best <[email protected]>
* fix(notifications): hydrate toggles after store load and add profile settings migration
- One-time store hydration via onMount plus guarded effect when profile/org
load after mount, without clobbering in-progress edits
- Add drizzle migration for profile.settings jsonb column
Co-authored-by: Rotimi Best <[email protected]>
---------
* feat(cohorts): rename Programs to Cohorts with data migration
- Add cohort database tables and Drizzle migration alongside legacy program tables
- Introduce migrate-programs-to-cohorts script to copy existing program data safely
- Rename API routes (/cohort), services, queries, validation, and error codes
- Rename dashboard feature module, routes (/cohorts), navigation, and translations
- Update docs, emails, and internal compliance cron endpoints for cohort terminology
- Keep legacy program tables in place for rollback until explicitly removed
Co-authored-by: Rotimi Best <[email protected]>
* fix(db): restore migrate:answer-data script alongside cohort migration
Co-authored-by: Rotimi Best <[email protected]>
* fix(db): cast program enums when migrating data to cohort tables
Co-authored-by: Rotimi Best <[email protected]>
* fix(db): truncate cohort tables before program-to-cohort copy
Co-authored-by: Rotimi Best <[email protected]>
* fix(db): report accurate row counts in programs-to-cohorts migration
Co-authored-by: Rotimi Best <[email protected]>
---------
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Rotimi Best <[email protected]>