diff --git a/internal/router/router.go b/internal/router/router.go index 0166445..cd9b06d 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -75,8 +75,9 @@ func SetupRouter(deps *Dependencies) *gin.Engine { documentService := services.NewDocumentService(documentRepo, residenceRepo) notificationService := services.NewNotificationService(notificationRepo, gorushClient) - // Wire up notification service to task service (for task completion notifications) + // Wire up notification and email services to task service (for task completion notifications) taskService.SetNotificationService(notificationService) + taskService.SetEmailService(deps.EmailService) subscriptionService := services.NewSubscriptionService(subscriptionRepo, residenceRepo, taskRepo, contractorRepo, documentRepo) // Initialize middleware diff --git a/internal/services/email_service.go b/internal/services/email_service.go index 24f55d0..ffd6e0f 100644 --- a/internal/services/email_service.go +++ b/internal/services/email_service.go @@ -280,6 +280,68 @@ The MyCrib Team return s.SendEmail(to, subject, htmlBody, textBody) } +// SendTaskCompletedEmail sends an email notification when a task is completed +func (s *EmailService) SendTaskCompletedEmail(to, recipientName, taskTitle, completedByName, residenceName string) error { + subject := fmt.Sprintf("MyCrib - Task Completed: %s", taskTitle) + + name := recipientName + if name == "" { + name = "there" + } + + htmlBody := fmt.Sprintf(` + + + + + + + +
+
+

Task Completed!

+
+

Hi %s,

+

A task has been completed at %s:

+
+

%s

+

Completed by: %s
Completed on: %s

+
+

Best regards,
The MyCrib Team

+ +
+ + +`, name, residenceName, taskTitle, completedByName, time.Now().UTC().Format("January 2, 2006 at 3:04 PM"), time.Now().Year()) + + textBody := fmt.Sprintf(` +Task Completed! + +Hi %s, + +A task has been completed at %s: + +Task: %s +Completed by: %s +Completed on: %s + +Best regards, +The MyCrib Team +`, name, residenceName, taskTitle, completedByName, time.Now().UTC().Format("January 2, 2006 at 3:04 PM")) + + return s.SendEmail(to, subject, htmlBody, textBody) +} + // EmailTemplate represents an email template type EmailTemplate struct { name string diff --git a/internal/services/task_service.go b/internal/services/task_service.go index a3d7723..2a7828b 100644 --- a/internal/services/task_service.go +++ b/internal/services/task_service.go @@ -29,6 +29,7 @@ type TaskService struct { taskRepo *repositories.TaskRepository residenceRepo *repositories.ResidenceRepository notificationService *NotificationService + emailService *EmailService } // NewTaskService creates a new task service @@ -44,6 +45,11 @@ func (s *TaskService) SetNotificationService(ns *NotificationService) { s.notificationService = ns } +// SetEmailService sets the email service +func (s *TaskService) SetEmailService(es *EmailService) { + s.emailService = es +} + // === Task CRUD === // GetTask gets a task by ID with access check @@ -485,11 +491,6 @@ func (s *TaskService) CreateCompletion(req *requests.CreateTaskCompletionRequest // sendTaskCompletedNotification sends notifications when a task is completed func (s *TaskService) sendTaskCompletedNotification(task *models.Task, completion *models.TaskCompletion) { - if s.notificationService == nil { - log.Debug().Msg("Notification service not configured, skipping task completion notification") - return - } - // Get all users with access to this residence users, err := s.residenceRepo.GetResidenceUsers(task.ResidenceID) if err != nil { @@ -497,6 +498,13 @@ func (s *TaskService) sendTaskCompletedNotification(task *models.Task, completio return } + // Get residence name + residence, err := s.residenceRepo.FindByIDSimple(task.ResidenceID) + residenceName := "your property" + if err == nil && residence != nil { + residenceName = residence.Name + } + completedByName := "Someone" if completion.CompletedBy.ID > 0 { completedByName = completion.CompletedBy.GetFullName() @@ -517,19 +525,39 @@ func (s *TaskService) sendTaskCompletedNotification(task *models.Task, completio continue // Don't notify the person who completed it } - go func(userID uint) { - ctx := context.Background() - if err := s.notificationService.CreateAndSendNotification( - ctx, - userID, - models.NotificationTaskCompleted, - title, - body, - data, - ); err != nil { - log.Error().Err(err).Uint("user_id", userID).Uint("task_id", task.ID).Msg("Failed to send task completion notification") - } - }(user.ID) + // Send push notification + if s.notificationService != nil { + go func(userID uint) { + ctx := context.Background() + if err := s.notificationService.CreateAndSendNotification( + ctx, + userID, + models.NotificationTaskCompleted, + title, + body, + data, + ); err != nil { + log.Error().Err(err).Uint("user_id", userID).Uint("task_id", task.ID).Msg("Failed to send task completion push notification") + } + }(user.ID) + } + + // Send email notification + 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) + } } }