Add Daily Digest notification preferences with custom time support
- Add daily_digest boolean and daily_digest_hour fields to NotificationPreference model - Update HandleDailyDigest to check user preferences and custom notification times - Change Daily Digest scheduler to run hourly (supports per-user custom times) - Update notification service DTOs for new fields - Add Daily Digest toggle and custom time to admin notification prefs page - Fix notification handlers to only notify users at their designated hour 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -33,6 +33,7 @@ type NotificationPrefResponse struct {
|
||||
TaskAssigned bool `json:"task_assigned"`
|
||||
ResidenceShared bool `json:"residence_shared"`
|
||||
WarrantyExpiring bool `json:"warranty_expiring"`
|
||||
DailyDigest bool `json:"daily_digest"`
|
||||
|
||||
// Email preferences
|
||||
EmailTaskCompleted bool `json:"email_task_completed"`
|
||||
@@ -41,6 +42,7 @@ type NotificationPrefResponse struct {
|
||||
TaskDueSoonHour *int `json:"task_due_soon_hour"`
|
||||
TaskOverdueHour *int `json:"task_overdue_hour"`
|
||||
WarrantyExpiringHour *int `json:"warranty_expiring_hour"`
|
||||
DailyDigestHour *int `json:"daily_digest_hour"`
|
||||
|
||||
CreatedAt string `json:"created_at"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
@@ -117,10 +119,12 @@ func (h *AdminNotificationPrefsHandler) List(c *gin.Context) {
|
||||
TaskAssigned: pref.TaskAssigned,
|
||||
ResidenceShared: pref.ResidenceShared,
|
||||
WarrantyExpiring: pref.WarrantyExpiring,
|
||||
DailyDigest: pref.DailyDigest,
|
||||
EmailTaskCompleted: pref.EmailTaskCompleted,
|
||||
TaskDueSoonHour: pref.TaskDueSoonHour,
|
||||
TaskOverdueHour: pref.TaskOverdueHour,
|
||||
WarrantyExpiringHour: pref.WarrantyExpiringHour,
|
||||
DailyDigestHour: pref.DailyDigestHour,
|
||||
CreatedAt: pref.CreatedAt.Format("2006-01-02T15:04:05Z"),
|
||||
UpdatedAt: pref.UpdatedAt.Format("2006-01-02T15:04:05Z"),
|
||||
}
|
||||
@@ -161,10 +165,12 @@ func (h *AdminNotificationPrefsHandler) Get(c *gin.Context) {
|
||||
TaskAssigned: pref.TaskAssigned,
|
||||
ResidenceShared: pref.ResidenceShared,
|
||||
WarrantyExpiring: pref.WarrantyExpiring,
|
||||
DailyDigest: pref.DailyDigest,
|
||||
EmailTaskCompleted: pref.EmailTaskCompleted,
|
||||
TaskDueSoonHour: pref.TaskDueSoonHour,
|
||||
TaskOverdueHour: pref.TaskOverdueHour,
|
||||
WarrantyExpiringHour: pref.WarrantyExpiringHour,
|
||||
DailyDigestHour: pref.DailyDigestHour,
|
||||
CreatedAt: pref.CreatedAt.Format("2006-01-02T15:04:05Z"),
|
||||
UpdatedAt: pref.UpdatedAt.Format("2006-01-02T15:04:05Z"),
|
||||
})
|
||||
@@ -178,6 +184,7 @@ type UpdateNotificationPrefRequest struct {
|
||||
TaskAssigned *bool `json:"task_assigned"`
|
||||
ResidenceShared *bool `json:"residence_shared"`
|
||||
WarrantyExpiring *bool `json:"warranty_expiring"`
|
||||
DailyDigest *bool `json:"daily_digest"`
|
||||
|
||||
// Email preferences
|
||||
EmailTaskCompleted *bool `json:"email_task_completed"`
|
||||
@@ -186,6 +193,7 @@ type UpdateNotificationPrefRequest struct {
|
||||
TaskDueSoonHour *int `json:"task_due_soon_hour"`
|
||||
TaskOverdueHour *int `json:"task_overdue_hour"`
|
||||
WarrantyExpiringHour *int `json:"warranty_expiring_hour"`
|
||||
DailyDigestHour *int `json:"daily_digest_hour"`
|
||||
}
|
||||
|
||||
// Update handles PUT /api/admin/notification-prefs/:id
|
||||
@@ -231,6 +239,9 @@ func (h *AdminNotificationPrefsHandler) Update(c *gin.Context) {
|
||||
if req.WarrantyExpiring != nil {
|
||||
pref.WarrantyExpiring = *req.WarrantyExpiring
|
||||
}
|
||||
if req.DailyDigest != nil {
|
||||
pref.DailyDigest = *req.DailyDigest
|
||||
}
|
||||
if req.EmailTaskCompleted != nil {
|
||||
pref.EmailTaskCompleted = *req.EmailTaskCompleted
|
||||
}
|
||||
@@ -245,6 +256,9 @@ func (h *AdminNotificationPrefsHandler) Update(c *gin.Context) {
|
||||
if req.WarrantyExpiringHour != nil {
|
||||
pref.WarrantyExpiringHour = req.WarrantyExpiringHour
|
||||
}
|
||||
if req.DailyDigestHour != nil {
|
||||
pref.DailyDigestHour = req.DailyDigestHour
|
||||
}
|
||||
|
||||
if err := h.db.Save(&pref).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update notification preference"})
|
||||
@@ -265,10 +279,12 @@ func (h *AdminNotificationPrefsHandler) Update(c *gin.Context) {
|
||||
TaskAssigned: pref.TaskAssigned,
|
||||
ResidenceShared: pref.ResidenceShared,
|
||||
WarrantyExpiring: pref.WarrantyExpiring,
|
||||
DailyDigest: pref.DailyDigest,
|
||||
EmailTaskCompleted: pref.EmailTaskCompleted,
|
||||
TaskDueSoonHour: pref.TaskDueSoonHour,
|
||||
TaskOverdueHour: pref.TaskOverdueHour,
|
||||
WarrantyExpiringHour: pref.WarrantyExpiringHour,
|
||||
DailyDigestHour: pref.DailyDigestHour,
|
||||
CreatedAt: pref.CreatedAt.Format("2006-01-02T15:04:05Z"),
|
||||
UpdatedAt: pref.UpdatedAt.Format("2006-01-02T15:04:05Z"),
|
||||
})
|
||||
@@ -327,10 +343,12 @@ func (h *AdminNotificationPrefsHandler) GetByUser(c *gin.Context) {
|
||||
TaskAssigned: pref.TaskAssigned,
|
||||
ResidenceShared: pref.ResidenceShared,
|
||||
WarrantyExpiring: pref.WarrantyExpiring,
|
||||
DailyDigest: pref.DailyDigest,
|
||||
EmailTaskCompleted: pref.EmailTaskCompleted,
|
||||
TaskDueSoonHour: pref.TaskDueSoonHour,
|
||||
TaskOverdueHour: pref.TaskOverdueHour,
|
||||
WarrantyExpiringHour: pref.WarrantyExpiringHour,
|
||||
DailyDigestHour: pref.DailyDigestHour,
|
||||
CreatedAt: pref.CreatedAt.Format("2006-01-02T15:04:05Z"),
|
||||
UpdatedAt: pref.UpdatedAt.Format("2006-01-02T15:04:05Z"),
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user