Repository navigation
feat: Notification Preferences & Delivery Settings (Closes #242) - #263
Open
olubukolatanko209-doc wants to merge 1 commit into
Open
olubukolatanko209-doc wants to merge 1 commit into
olubukolatanko209-doc wants to merge 1 commit into
Conversation
…#242) Implements issue Lumina-eX#242 — Notification Preferences & Delivery Settings. ## Database - Add migration 011_notification_preferences.sql with a new notification_preferences table: - Per-event-category toggles for in-app and email channels (milestones, payments, disputes, deadlines) - Global channel master switches (email_enabled, push_enabled, in_app_enabled) - Delivery cadence column (instant | daily | weekly) - UNIQUE constraint on user_id; missing row treated as defaults ## Backend — lib/notification-preferences.ts - New service layer with typed NotificationPreferences interface - getNotificationPreferences(userId): returns stored prefs or defaults - updateNotificationPreferences(userId, input): UPSERT with partial merge - resetNotificationPreferences(userId): DELETE row, returns defaults - NotificationPreferencesError with typed error codes ## API — app/api/notifications/preferences/route.ts - GET /api/notifications/preferences — fetch preferences (60 req/min) - PUT /api/notifications/preferences — update preferences (30 req/min), Zod-validated, all fields optional, rejects empty bodies - DELETE /api/notifications/preferences — reset to defaults (10 req/min) - withAuth middleware, resolveUserIdByWallet, enforceRateLimit on all three handlers; consistent error shape { error, code } ## Frontend — app/settings/settings-client.tsx - Load preferences from API on mount via useEffect (cancellable) - Global channel toggles: Email / Push / In-app master switches - Per-event-category table: Milestones / Payments / Disputes / Deadlines × In-app / Email columns; channel toggles disable their column - Delivery cadence select: instant / daily / weekly - Security alerts section: clearly marked Always on, non-dismissible - Save status indicator: Saving… / Preferences saved / error message - Reset to defaults: confirmation dialog before DELETE call - Loading skeleton while prefs are fetching - Error banner with graceful fallback to defaults on load failure
|
@olubukolatanko209-doc Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Notification Preferences & Delivery Settings
Closes #242
Overview
This PR implements per-user notification preferences with full backend persistence, a redesigned settings UI, and graceful handling of all loading, validation, and error states. Users can now independently configure in-app and email notifications for each event category, choose a delivery cadence, and reset to defaults.
What Changed
Database —
lib/db/migrations/011_notification_preferences.sqlnotification_preferencestable with:email_enabled,push_enabled,in_app_enabled)instant|daily|weekly) with CHECK constraintUNIQUE(user_id)constraint; missing rows treated as platform defaultsuser_idfor fast lookupsBackend service —
lib/notification-preferences.tsNotificationPreferencesinterface with 15 fieldsgetNotificationPreferences(userId)— returns stored prefs orDEFAULT_PREFERENCESon first visitupdateNotificationPreferences(userId, input)— UPSERT with partial merge (only supplied fields are updated)resetNotificationPreferences(userId)— DELETE row, returns defaultsNotificationPreferencesErrorwith typed error codesAPI —
app/api/notifications/preferences/route.tsGET /api/notifications/preferences— fetch preferences (60 req/min rate limit)PUT /api/notifications/preferences— update preferences (30 req/min), Zod-validated, all fields optional, rejects empty bodies with 400DELETE /api/notifications/preferences— reset to defaults (10 req/min)withAuth+resolveUserIdByWalletfor authentication{ error, code }with appropriate HTTP status codes (400, 401, 404, 422, 429, 503)Cache-Control: private, no-storeon GET responsesFrontend —
app/settings/settings-client.tsxuseEffectAcceptance Criteria
Files Changed
lib/db/migrations/011_notification_preferences.sqllib/notification-preferences.tsapp/api/notifications/preferences/route.tsapp/settings/settings-client.tsxTesting
next/*module resolution issues in the repo are unrelated)withAuthmiddleware,resolveUserIdByWallet,enforceRateLimit, Zod validation, snake_case to camelCase DB mappingNotes
IF NOT EXISTSguards for idempotencyON CONFLICT (user_id) DO UPDATEfor atomic partial updates