Add email notification preference for task completion

- Add EmailTaskCompleted field to NotificationPreference model
- Update notification repository to include email preference in queries
- Check email preference before sending task completion emails
- Add email preference toggle to admin panel notification-prefs page
- Update API types for email preference support

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Trey t
2025-12-04 20:04:24 -06:00
parent 0825fd9a12
commit 5a6fbe0a36
7 changed files with 125 additions and 78 deletions

View File

@@ -33,8 +33,12 @@ type NotificationPrefResponse struct {
TaskAssigned bool `json:"task_assigned"`
ResidenceShared bool `json:"residence_shared"`
WarrantyExpiring bool `json:"warranty_expiring"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
// Email preferences
EmailTaskCompleted bool `json:"email_task_completed"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
}
// List handles GET /api/admin/notification-prefs
@@ -98,18 +102,19 @@ func (h *AdminNotificationPrefsHandler) List(c *gin.Context) {
for i, pref := range prefs {
user := userMap[pref.UserID]
responses[i] = NotificationPrefResponse{
ID: pref.ID,
UserID: pref.UserID,
Username: user.Username,
Email: user.Email,
TaskDueSoon: pref.TaskDueSoon,
TaskOverdue: pref.TaskOverdue,
TaskCompleted: pref.TaskCompleted,
TaskAssigned: pref.TaskAssigned,
ResidenceShared: pref.ResidenceShared,
WarrantyExpiring: pref.WarrantyExpiring,
CreatedAt: pref.CreatedAt.Format("2006-01-02T15:04:05Z"),
UpdatedAt: pref.UpdatedAt.Format("2006-01-02T15:04:05Z"),
ID: pref.ID,
UserID: pref.UserID,
Username: user.Username,
Email: user.Email,
TaskDueSoon: pref.TaskDueSoon,
TaskOverdue: pref.TaskOverdue,
TaskCompleted: pref.TaskCompleted,
TaskAssigned: pref.TaskAssigned,
ResidenceShared: pref.ResidenceShared,
WarrantyExpiring: pref.WarrantyExpiring,
EmailTaskCompleted: pref.EmailTaskCompleted,
CreatedAt: pref.CreatedAt.Format("2006-01-02T15:04:05Z"),
UpdatedAt: pref.UpdatedAt.Format("2006-01-02T15:04:05Z"),
}
}
@@ -138,18 +143,19 @@ func (h *AdminNotificationPrefsHandler) Get(c *gin.Context) {
h.db.First(&user, pref.UserID)
c.JSON(http.StatusOK, NotificationPrefResponse{
ID: pref.ID,
UserID: pref.UserID,
Username: user.Username,
Email: user.Email,
TaskDueSoon: pref.TaskDueSoon,
TaskOverdue: pref.TaskOverdue,
TaskCompleted: pref.TaskCompleted,
TaskAssigned: pref.TaskAssigned,
ResidenceShared: pref.ResidenceShared,
WarrantyExpiring: pref.WarrantyExpiring,
CreatedAt: pref.CreatedAt.Format("2006-01-02T15:04:05Z"),
UpdatedAt: pref.UpdatedAt.Format("2006-01-02T15:04:05Z"),
ID: pref.ID,
UserID: pref.UserID,
Username: user.Username,
Email: user.Email,
TaskDueSoon: pref.TaskDueSoon,
TaskOverdue: pref.TaskOverdue,
TaskCompleted: pref.TaskCompleted,
TaskAssigned: pref.TaskAssigned,
ResidenceShared: pref.ResidenceShared,
WarrantyExpiring: pref.WarrantyExpiring,
EmailTaskCompleted: pref.EmailTaskCompleted,
CreatedAt: pref.CreatedAt.Format("2006-01-02T15:04:05Z"),
UpdatedAt: pref.UpdatedAt.Format("2006-01-02T15:04:05Z"),
})
}
@@ -161,6 +167,9 @@ type UpdateNotificationPrefRequest struct {
TaskAssigned *bool `json:"task_assigned"`
ResidenceShared *bool `json:"residence_shared"`
WarrantyExpiring *bool `json:"warranty_expiring"`
// Email preferences
EmailTaskCompleted *bool `json:"email_task_completed"`
}
// Update handles PUT /api/admin/notification-prefs/:id
@@ -206,6 +215,9 @@ func (h *AdminNotificationPrefsHandler) Update(c *gin.Context) {
if req.WarrantyExpiring != nil {
pref.WarrantyExpiring = *req.WarrantyExpiring
}
if req.EmailTaskCompleted != nil {
pref.EmailTaskCompleted = *req.EmailTaskCompleted
}
if err := h.db.Save(&pref).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update notification preference"})
@@ -216,18 +228,19 @@ func (h *AdminNotificationPrefsHandler) Update(c *gin.Context) {
h.db.First(&user, pref.UserID)
c.JSON(http.StatusOK, NotificationPrefResponse{
ID: pref.ID,
UserID: pref.UserID,
Username: user.Username,
Email: user.Email,
TaskDueSoon: pref.TaskDueSoon,
TaskOverdue: pref.TaskOverdue,
TaskCompleted: pref.TaskCompleted,
TaskAssigned: pref.TaskAssigned,
ResidenceShared: pref.ResidenceShared,
WarrantyExpiring: pref.WarrantyExpiring,
CreatedAt: pref.CreatedAt.Format("2006-01-02T15:04:05Z"),
UpdatedAt: pref.UpdatedAt.Format("2006-01-02T15:04:05Z"),
ID: pref.ID,
UserID: pref.UserID,
Username: user.Username,
Email: user.Email,
TaskDueSoon: pref.TaskDueSoon,
TaskOverdue: pref.TaskOverdue,
TaskCompleted: pref.TaskCompleted,
TaskAssigned: pref.TaskAssigned,
ResidenceShared: pref.ResidenceShared,
WarrantyExpiring: pref.WarrantyExpiring,
EmailTaskCompleted: pref.EmailTaskCompleted,
CreatedAt: pref.CreatedAt.Format("2006-01-02T15:04:05Z"),
UpdatedAt: pref.UpdatedAt.Format("2006-01-02T15:04:05Z"),
})
}
@@ -274,17 +287,18 @@ func (h *AdminNotificationPrefsHandler) GetByUser(c *gin.Context) {
h.db.First(&user, pref.UserID)
c.JSON(http.StatusOK, NotificationPrefResponse{
ID: pref.ID,
UserID: pref.UserID,
Username: user.Username,
Email: user.Email,
TaskDueSoon: pref.TaskDueSoon,
TaskOverdue: pref.TaskOverdue,
TaskCompleted: pref.TaskCompleted,
TaskAssigned: pref.TaskAssigned,
ResidenceShared: pref.ResidenceShared,
WarrantyExpiring: pref.WarrantyExpiring,
CreatedAt: pref.CreatedAt.Format("2006-01-02T15:04:05Z"),
UpdatedAt: pref.UpdatedAt.Format("2006-01-02T15:04:05Z"),
ID: pref.ID,
UserID: pref.UserID,
Username: user.Username,
Email: user.Email,
TaskDueSoon: pref.TaskDueSoon,
TaskOverdue: pref.TaskOverdue,
TaskCompleted: pref.TaskCompleted,
TaskAssigned: pref.TaskAssigned,
ResidenceShared: pref.ResidenceShared,
WarrantyExpiring: pref.WarrantyExpiring,
EmailTaskCompleted: pref.EmailTaskCompleted,
CreatedAt: pref.CreatedAt.Format("2006-01-02T15:04:05Z"),
UpdatedAt: pref.UpdatedAt.Format("2006-01-02T15:04:05Z"),
})
}