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

@@ -190,6 +190,9 @@ func (s *NotificationService) UpdatePreferences(userID uint, req *UpdatePreferen
if req.WarrantyExpiring != nil {
prefs.WarrantyExpiring = *req.WarrantyExpiring
}
if req.EmailTaskCompleted != nil {
prefs.EmailTaskCompleted = *req.EmailTaskCompleted
}
if err := s.notificationRepo.UpdatePreferences(prefs); err != nil {
return nil, err
@@ -358,17 +361,21 @@ type NotificationPreferencesResponse struct {
TaskAssigned bool `json:"task_assigned"`
ResidenceShared bool `json:"residence_shared"`
WarrantyExpiring bool `json:"warranty_expiring"`
// Email preferences
EmailTaskCompleted bool `json:"email_task_completed"`
}
// NewNotificationPreferencesResponse creates a NotificationPreferencesResponse
func NewNotificationPreferencesResponse(p *models.NotificationPreference) *NotificationPreferencesResponse {
return &NotificationPreferencesResponse{
TaskDueSoon: p.TaskDueSoon,
TaskOverdue: p.TaskOverdue,
TaskCompleted: p.TaskCompleted,
TaskAssigned: p.TaskAssigned,
ResidenceShared: p.ResidenceShared,
WarrantyExpiring: p.WarrantyExpiring,
TaskDueSoon: p.TaskDueSoon,
TaskOverdue: p.TaskOverdue,
TaskCompleted: p.TaskCompleted,
TaskAssigned: p.TaskAssigned,
ResidenceShared: p.ResidenceShared,
WarrantyExpiring: p.WarrantyExpiring,
EmailTaskCompleted: p.EmailTaskCompleted,
}
}
@@ -380,6 +387,9 @@ type UpdatePreferencesRequest struct {
TaskAssigned *bool `json:"task_assigned"`
ResidenceShared *bool `json:"residence_shared"`
WarrantyExpiring *bool `json:"warranty_expiring"`
// Email preferences
EmailTaskCompleted *bool `json:"email_task_completed"`
}
// DeviceResponse represents a device in API response

View File

@@ -570,20 +570,25 @@ func (s *TaskService) sendTaskCompletedNotification(task *models.Task, completio
}
// Send email notification (to everyone INCLUDING the person who completed it)
if s.emailService != nil && user.Email != "" {
go func(u models.User) {
if err := s.emailService.SendTaskCompletedEmail(
u.Email,
u.GetFullName(),
task.Title,
completedByName,
residenceName,
); err != nil {
log.Error().Err(err).Str("email", u.Email).Uint("task_id", task.ID).Msg("Failed to send task completion email")
} else {
log.Info().Str("email", u.Email).Uint("task_id", task.ID).Msg("Task completion email sent")
}
}(user)
// Check user's email notification preferences first
if s.emailService != nil && user.Email != "" && s.notificationService != nil {
prefs, err := s.notificationService.GetPreferences(user.ID)
if err != nil || (prefs != nil && prefs.EmailTaskCompleted) {
// Send email if we couldn't get prefs (fail-open) or if email notifications are enabled
go func(u models.User) {
if err := s.emailService.SendTaskCompletedEmail(
u.Email,
u.GetFullName(),
task.Title,
completedByName,
residenceName,
); err != nil {
log.Error().Err(err).Str("email", u.Email).Uint("task_id", task.ID).Msg("Failed to send task completion email")
} else {
log.Info().Str("email", u.Email).Uint("task_id", task.ID).Msg("Task completion email sent")
}
}(user)
}
}
}
}