diff --git a/JobFlow.API/Controllers/AssignmentController.cs b/JobFlow.API/Controllers/AssignmentController.cs index 5ac1e35..cb359c0 100644 --- a/JobFlow.API/Controllers/AssignmentController.cs +++ b/JobFlow.API/Controllers/AssignmentController.cs @@ -122,5 +122,18 @@ public async Task UpdateNotes(Guid id, [FromBody] UpdateAssignmen return Ok(result.Value); } + // Notify the client that the worker is en route + [HttpPost("{id:guid}/en-route")] + public async Task NotifyEnRoute(Guid id) + { + var organizationId = HttpContext.GetOrganizationId(); + + var result = await _assignmentService.NotifyEnRouteAsync(organizationId, id); + if (result.IsFailure) + return BadRequest(result.Error); + + return Ok(new { success = true }); + } + } diff --git a/JobFlow.Business/Notifications/Builders/INotificationMessageBuilder.cs b/JobFlow.Business/Notifications/Builders/INotificationMessageBuilder.cs index 5fbc440..fcde948 100644 --- a/JobFlow.Business/Notifications/Builders/INotificationMessageBuilder.cs +++ b/JobFlow.Business/Notifications/Builders/INotificationMessageBuilder.cs @@ -26,6 +26,7 @@ NotificationMessage BuildClientJobRescheduled( NotificationMessage BuildClientJobTrackingEta(OrganizationClient client, Job job, int etaMinutes); NotificationMessage BuildClientJobTrackingArrival(OrganizationClient client, Job job); + NotificationMessage BuildClientJobTrackingEnRoute(OrganizationClient client, Job job); NotificationMessage BuildEmployeeInvite(EmployeeInvite invite); diff --git a/JobFlow.Business/Notifications/Builders/NotificationMessageBuilder.cs b/JobFlow.Business/Notifications/Builders/NotificationMessageBuilder.cs index 38af0a3..9496aee 100644 --- a/JobFlow.Business/Notifications/Builders/NotificationMessageBuilder.cs +++ b/JobFlow.Business/Notifications/Builders/NotificationMessageBuilder.cs @@ -210,6 +210,21 @@ public NotificationMessage BuildClientJobTrackingArrival(OrganizationClient clie }; } + public NotificationMessage BuildClientJobTrackingEnRoute(OrganizationClient client, Job job) + { + return new NotificationMessage + { + Email = client.EmailAddress, + Phone = client.PhoneNumber, + Name = client.FirstName, + Subject = $"Your worker is on the way for {job.Title}", + Body = + $"Hello {client.ClientFullName()},\n\nYour JobFlow worker is on the way for your job: {job.Title}.", + Sms = $"Your JobFlow worker is on the way for {job.Title}. ", + TemplateId = EmailTemplate.Default + }; + } + public NotificationMessage BuildEmployeeInvite(EmployeeInvite invite) { var link = $"{baseUrl}/i/{invite.ShortCode}"; diff --git a/JobFlow.Business/Notifications/NotificationService.cs b/JobFlow.Business/Notifications/NotificationService.cs index 6021a32..1951dfa 100644 --- a/JobFlow.Business/Notifications/NotificationService.cs +++ b/JobFlow.Business/Notifications/NotificationService.cs @@ -132,6 +132,12 @@ public async Task SendClientJobTrackingArrivalNotificationAsync(OrganizationClie await SendNotificationAsync(message); } + public async Task SendClientJobTrackingEnRouteNotificationAsync(OrganizationClient client, Job job) + { + var message = _builder.BuildClientJobTrackingEnRoute(client, job); + await SendNotificationAsync(message); + } + public async Task SendEmployeeInviteNotificationAsync(EmployeeInvite invite) { var message = _builder.BuildEmployeeInvite(invite); diff --git a/JobFlow.Business/Services/AssignmentService.cs b/JobFlow.Business/Services/AssignmentService.cs index 8cadd73..39b278f 100644 --- a/JobFlow.Business/Services/AssignmentService.cs +++ b/JobFlow.Business/Services/AssignmentService.cs @@ -346,6 +346,26 @@ public async Task> GetAssignmentByIdAsync( return Result.Success(await MapToDtoAsync(organizationId, assignment)); } + public async Task NotifyEnRouteAsync(Guid organizationId, Guid assignmentId) + { + var assignment = await _assignments.Query() + .Include(a => a.Job) + .ThenInclude(j => j.OrganizationClient) + .FirstOrDefaultAsync(a => + a.Id == assignmentId && + a.Job.OrganizationClient.OrganizationId == organizationId); + + if (assignment == null) + return Result.Failure(AssignmentErrors.NotFound); + + var job = assignment.Job; + if (job?.OrganizationClient == null) + return Result.Failure(Error.NotFound("Client.NotFound", "The client for this assignment could not be found.")); + + await _notificationService.SendClientJobTrackingEnRouteNotificationAsync(job.OrganizationClient, job); + return Result.Success(); + } + private async Task MapToDtoAsync(Guid organizationId, Assignment assignment) { var labelMapResult = await _workflowSettings.GetJobLifecycleLabelMapAsync(organizationId); @@ -397,6 +417,16 @@ private AssignmentDto MapToDto(Assignment assignment, Dictionary>> GetAssignmentsAsync(Guid organizationId, DateTime start, DateTime end); Task> GetAssignmentByIdAsync(Guid organizationId, Guid assignmentId); + + Task NotifyEnRouteAsync(Guid organizationId, Guid assignmentId); } \ No newline at end of file diff --git a/JobFlow.Business/Services/ServiceInterfaces/INotificationService.cs b/JobFlow.Business/Services/ServiceInterfaces/INotificationService.cs index 0b49373..998f174 100644 --- a/JobFlow.Business/Services/ServiceInterfaces/INotificationService.cs +++ b/JobFlow.Business/Services/ServiceInterfaces/INotificationService.cs @@ -26,6 +26,7 @@ Task SendClientJobRescheduledNotificationAsync( Task SendClientPaymentReceivedNotificationAsync(OrganizationClient client, Invoice invoice); Task SendClientJobTrackingEtaNotificationAsync(OrganizationClient client, Job job, int etaMinutes); Task SendClientJobTrackingArrivalNotificationAsync(OrganizationClient client, Job job); + Task SendClientJobTrackingEnRouteNotificationAsync(OrganizationClient client, Job job); Task SendClientEstimateSentNotificationAsync(OrganizationClient client, Estimate estimate); Task SendClientEstimateFollowUpNotificationAsync(OrganizationClient client, Estimate estimate, string message); Task SendOrganizationEstimateRevisionRequestedNotificationAsync(Organization organization, OrganizationClient client, Estimate estimate, string revisionMessage); diff --git a/JobFlow.Tests/FollowUpAutomationServiceTests.cs b/JobFlow.Tests/FollowUpAutomationServiceTests.cs index dadb0fd..e507996 100644 --- a/JobFlow.Tests/FollowUpAutomationServiceTests.cs +++ b/JobFlow.Tests/FollowUpAutomationServiceTests.cs @@ -272,6 +272,7 @@ private sealed class NoOpNotificationService : INotificationService public Task SendClientPaymentReceivedNotificationAsync(OrganizationClient client, Invoice invoice) => Task.CompletedTask; public Task SendClientJobTrackingEtaNotificationAsync(OrganizationClient client, Job job, int etaMinutes) => Task.CompletedTask; public Task SendClientJobTrackingArrivalNotificationAsync(OrganizationClient client, Job job) => Task.CompletedTask; + public Task SendClientJobTrackingEnRouteNotificationAsync(OrganizationClient client, Job job) => Task.CompletedTask; public Task SendClientEstimateSentNotificationAsync(OrganizationClient client, Estimate estimate) => Task.CompletedTask; public Task SendClientEstimateFollowUpNotificationAsync(OrganizationClient client, Estimate estimate, string message) => Task.CompletedTask; public Task SendOrganizationEstimateRevisionRequestedNotificationAsync(Organization organization, OrganizationClient client, Estimate estimate, string revisionMessage) => Task.CompletedTask;