Sending the notification to team member when task is assigned
This commit is contained in:
parent
4655aa948b
commit
7b1238e7d6
@ -1,6 +1,6 @@
|
|||||||
using System.ComponentModel;
|
using Marco.Pms.Model.Utilities;
|
||||||
|
using System.ComponentModel;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using Marco.Pms.Model.Utilities;
|
|
||||||
|
|
||||||
namespace Marco.Pms.Model.Projects
|
namespace Marco.Pms.Model.Projects
|
||||||
{
|
{
|
||||||
|
@ -9,6 +9,7 @@ using Marco.Pms.Model.ViewModels.Activities;
|
|||||||
using Marco.Pms.Services.Helpers;
|
using Marco.Pms.Services.Helpers;
|
||||||
using Marco.Pms.Services.Hubs;
|
using Marco.Pms.Services.Hubs;
|
||||||
using Marco.Pms.Services.Service;
|
using Marco.Pms.Services.Service;
|
||||||
|
using Marco.Pms.Services.Service.ServiceInterfaces;
|
||||||
using MarcoBMS.Services.Helpers;
|
using MarcoBMS.Services.Helpers;
|
||||||
using MarcoBMS.Services.Service;
|
using MarcoBMS.Services.Service;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
@ -33,9 +34,10 @@ namespace MarcoBMS.Services.Controllers
|
|||||||
private readonly IHubContext<MarcoHub> _signalR;
|
private readonly IHubContext<MarcoHub> _signalR;
|
||||||
private readonly CacheUpdateHelper _cache;
|
private readonly CacheUpdateHelper _cache;
|
||||||
private readonly PermissionServices _permissionServices;
|
private readonly PermissionServices _permissionServices;
|
||||||
|
private readonly IFirebaseService _firebase;
|
||||||
|
|
||||||
public TaskController(ApplicationDbContext context, UserHelper userHelper, S3UploadService s3Service, ILoggingService logger, PermissionServices permissionServices,
|
public TaskController(ApplicationDbContext context, UserHelper userHelper, S3UploadService s3Service, ILoggingService logger, PermissionServices permissionServices,
|
||||||
IHubContext<MarcoHub> signalR, CacheUpdateHelper cache)
|
IHubContext<MarcoHub> signalR, CacheUpdateHelper cache, IFirebaseService firebase)
|
||||||
{
|
{
|
||||||
_context = context;
|
_context = context;
|
||||||
_userHelper = userHelper;
|
_userHelper = userHelper;
|
||||||
@ -44,6 +46,7 @@ namespace MarcoBMS.Services.Controllers
|
|||||||
_signalR = signalR;
|
_signalR = signalR;
|
||||||
_cache = cache;
|
_cache = cache;
|
||||||
_permissionServices = permissionServices;
|
_permissionServices = permissionServices;
|
||||||
|
_firebase = firebase;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Guid GetTenantId()
|
private Guid GetTenantId()
|
||||||
@ -66,28 +69,28 @@ namespace MarcoBMS.Services.Controllers
|
|||||||
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400));
|
return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve tenant and employee context
|
// Retrieve tenant and loggedInEmployee context
|
||||||
var tenantId = GetTenantId();
|
var tenantId = GetTenantId();
|
||||||
var employee = await _userHelper.GetCurrentEmployeeAsync();
|
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||||
|
|
||||||
// Check for permission to approve tasks
|
// Check for permission to approve tasks
|
||||||
var hasPermission = await _permissionServices.HasPermission(PermissionsMaster.AssignAndReportProgress, employee.Id);
|
var hasPermission = await _permissionServices.HasPermission(PermissionsMaster.AssignAndReportProgress, loggedInEmployee.Id);
|
||||||
if (!hasPermission)
|
if (!hasPermission)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("Employee {EmployeeId} attempted to assign Task without permission", employee.Id);
|
_logger.LogWarning("Employee {EmployeeId} attempted to assign Task without permission", loggedInEmployee.Id);
|
||||||
return StatusCode(403, ApiResponse<object>.ErrorResponse("You don't have access", "User not authorized to approve tasks", 403));
|
return StatusCode(403, ApiResponse<object>.ErrorResponse("You don't have access", "User not authorized to approve tasks", 403));
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.LogInfo("Employee {EmployeeId} is assigning a new task", employee.Id);
|
_logger.LogInfo("Employee {EmployeeId} is assigning a new task", loggedInEmployee.Id);
|
||||||
|
|
||||||
// Convert DTO to entity and save TaskAllocation
|
// Convert DTO to entity and save TaskAllocation
|
||||||
var taskAllocation = assignTask.ToTaskAllocationFromAssignTaskDto(employee.Id, tenantId);
|
var taskAllocation = assignTask.ToTaskAllocationFromAssignTaskDto(loggedInEmployee.Id, tenantId);
|
||||||
_context.TaskAllocations.Add(taskAllocation);
|
_context.TaskAllocations.Add(taskAllocation);
|
||||||
await _context.SaveChangesAsync();
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
await _cache.UpdatePlannedAndCompleteWorksInWorkItem(taskAllocation.WorkItemId, todaysAssigned: taskAllocation.PlannedTask);
|
await _cache.UpdatePlannedAndCompleteWorksInWorkItem(taskAllocation.WorkItemId, todaysAssigned: taskAllocation.PlannedTask);
|
||||||
|
|
||||||
_logger.LogInfo("Task {TaskId} assigned by Employee {EmployeeId}", taskAllocation.Id, employee.Id);
|
_logger.LogInfo("Task {TaskId} assigned by Employee {EmployeeId}", taskAllocation.Id, loggedInEmployee.Id);
|
||||||
|
|
||||||
var response = taskAllocation.ToAssignTaskVMFromTaskAllocation();
|
var response = taskAllocation.ToAssignTaskVMFromTaskAllocation();
|
||||||
|
|
||||||
@ -117,6 +120,18 @@ namespace MarcoBMS.Services.Controllers
|
|||||||
var team = employees.Select(e => e.ToBasicEmployeeVMFromEmployee()).ToList();
|
var team = employees.Select(e => e.ToBasicEmployeeVMFromEmployee()).ToList();
|
||||||
response.teamMembers = team;
|
response.teamMembers = team;
|
||||||
|
|
||||||
|
_ = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
// --- Push Notification Section ---
|
||||||
|
// This section attempts to send a test push notification to the user's device.
|
||||||
|
// It's designed to fail gracefully and handle invalid Firebase Cloud Messaging (FCM) tokens.
|
||||||
|
|
||||||
|
var name = $"{loggedInEmployee.FirstName} {loggedInEmployee.LastName}";
|
||||||
|
|
||||||
|
await _firebase.SendAssignTaskMessageAsync(taskAllocation.WorkItemId, name, employeeIds, tenantId);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
return Ok(ApiResponse<object>.SuccessResponse(response, "Task assigned successfully", 200));
|
return Ok(ApiResponse<object>.SuccessResponse(response, "Task assigned successfully", 200));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ namespace Marco.Pms.Services.Service
|
|||||||
|
|
||||||
await SendMessageToMultipleDevicesAsync(registrationTokens, notificationFirebase);
|
await SendMessageToMultipleDevicesAsync(registrationTokens, notificationFirebase);
|
||||||
}
|
}
|
||||||
public async Task SendAttendanceMessageAsync(Guid projectId, string Name, ATTENDANCE_MARK_TYPE markType, Guid tenantId)
|
public async Task SendAttendanceMessageAsync(Guid projectId, string name, ATTENDANCE_MARK_TYPE markType, Guid tenantId)
|
||||||
{
|
{
|
||||||
await using var _context = await _dbContextFactory.CreateDbContextAsync();
|
await using var _context = await _dbContextFactory.CreateDbContextAsync();
|
||||||
var projectTask = Task.Run(async () =>
|
var projectTask = Task.Run(async () =>
|
||||||
@ -86,42 +86,42 @@ namespace Marco.Pms.Services.Service
|
|||||||
notificationFirebase = new Notification
|
notificationFirebase = new Notification
|
||||||
{
|
{
|
||||||
Title = "Attendance Update",
|
Title = "Attendance Update",
|
||||||
Body = $" {Name} has checked in for project {project?.ProjectName ?? ""}."
|
Body = $" {name} has checked in for project {project?.ProjectName ?? ""}."
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
case ATTENDANCE_MARK_TYPE.CHECK_OUT:
|
case ATTENDANCE_MARK_TYPE.CHECK_OUT:
|
||||||
notificationFirebase = new Notification
|
notificationFirebase = new Notification
|
||||||
{
|
{
|
||||||
Title = "Attendance Update",
|
Title = "Attendance Update",
|
||||||
Body = $" {Name} has checked out for project {project?.ProjectName ?? ""}."
|
Body = $" {name} has checked out for project {project?.ProjectName ?? ""}."
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
case ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE:
|
case ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE:
|
||||||
notificationFirebase = new Notification
|
notificationFirebase = new Notification
|
||||||
{
|
{
|
||||||
Title = "Regularization Request",
|
Title = "Regularization Request",
|
||||||
Body = $" {Name} has submitted a regularization request for project {project?.ProjectName ?? ""}."
|
Body = $" {name} has submitted a regularization request for project {project?.ProjectName ?? ""}."
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
case ATTENDANCE_MARK_TYPE.REGULARIZE:
|
case ATTENDANCE_MARK_TYPE.REGULARIZE:
|
||||||
notificationFirebase = new Notification
|
notificationFirebase = new Notification
|
||||||
{
|
{
|
||||||
Title = " Regularization Approved",
|
Title = " Regularization Approved",
|
||||||
Body = $" {Name}'s regularization request for project {project?.ProjectName ?? ""} has been accepted."
|
Body = $" {name}'s regularization request for project {project?.ProjectName ?? ""} has been accepted."
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
case ATTENDANCE_MARK_TYPE.REGULARIZE_REJECT:
|
case ATTENDANCE_MARK_TYPE.REGULARIZE_REJECT:
|
||||||
notificationFirebase = new Notification
|
notificationFirebase = new Notification
|
||||||
{
|
{
|
||||||
Title = "Regularization Denied",
|
Title = "Regularization Denied",
|
||||||
Body = $" {Name}'s regularization request for project {project?.ProjectName ?? ""} has been rejected."
|
Body = $" {name}'s regularization request for project {project?.ProjectName ?? ""} has been rejected."
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
notificationFirebase = new Notification
|
notificationFirebase = new Notification
|
||||||
{
|
{
|
||||||
Title = "Attendance Update",
|
Title = "Attendance Update",
|
||||||
Body = $" {Name} has update his/her attendance for project {project?.ProjectName ?? ""}."
|
Body = $" {name} has update his/her attendance for project {project?.ProjectName ?? ""}."
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -140,6 +140,106 @@ namespace Marco.Pms.Services.Service
|
|||||||
|
|
||||||
await SendMessageToMultipleDevicesWithDataAsync(registrationTokens, notificationFirebase, data);
|
await SendMessageToMultipleDevicesWithDataAsync(registrationTokens, notificationFirebase, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task SendAssignTaskMessageAsync(Guid workItemId, string name, List<Guid> teamMembers, Guid tenantId)
|
||||||
|
{
|
||||||
|
using var scope = _serviceScopeFactory.CreateScope();
|
||||||
|
var _logger = scope.ServiceProvider.GetRequiredService<ILoggingService>();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await using var _context = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
|
||||||
|
var workItem = await _context.WorkItems
|
||||||
|
.Include(wi => wi.WorkArea)
|
||||||
|
.ThenInclude(wa => wa!.Floor)
|
||||||
|
.ThenInclude(f => f!.Building)
|
||||||
|
.FirstOrDefaultAsync(wi => wi.Id == workItemId && wi.WorkArea != null && wi.WorkArea.Floor != null && wi.WorkArea.Floor.Building != null);
|
||||||
|
|
||||||
|
if (workItem == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var projectId = workItem.WorkArea!.Floor!.Building!.ProjectId;
|
||||||
|
|
||||||
|
var projectTask = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
return await dbContext.ProjectAllocations
|
||||||
|
.Include(pa => pa.Project)
|
||||||
|
.Where(pa => pa.ProjectId == projectId && pa.IsActive && pa.Project != null)
|
||||||
|
.GroupBy(pa => pa.ProjectId)
|
||||||
|
.Select(g => new
|
||||||
|
{
|
||||||
|
ProjectName = g.Select(pa => pa.Project!.Name).FirstOrDefault(),
|
||||||
|
EmployeeIds = g.Select(pa => pa.EmployeeId).Distinct().ToList()
|
||||||
|
}).FirstOrDefaultAsync();
|
||||||
|
});
|
||||||
|
|
||||||
|
var viewTaskRoleTask = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
return await dbContext.RolePermissionMappings
|
||||||
|
.Where(rp => rp.FeaturePermissionId == PermissionsMaster.ViewTask)
|
||||||
|
.Select(rp => rp.ApplicationRoleId).ToListAsync();
|
||||||
|
});
|
||||||
|
|
||||||
|
await Task.WhenAll(projectTask, viewTaskRoleTask);
|
||||||
|
|
||||||
|
var viewTaskRoleIds = viewTaskRoleTask.Result;
|
||||||
|
var project = projectTask.Result;
|
||||||
|
|
||||||
|
var buildingName = workItem.WorkArea.Floor.Building.Name;
|
||||||
|
var FloorName = workItem.WorkArea.Floor.FloorName;
|
||||||
|
var AreaName = workItem.WorkArea.AreaName;
|
||||||
|
|
||||||
|
var location = $"{buildingName} > {FloorName} > {AreaName}";
|
||||||
|
|
||||||
|
List<Guid> projectAssignedEmployeeIds = project?.EmployeeIds ?? new List<Guid>();
|
||||||
|
|
||||||
|
var employeeIds = await _context.EmployeeRoleMappings
|
||||||
|
.Where(er => projectAssignedEmployeeIds.Contains(er.EmployeeId) && viewTaskRoleIds.Contains(er.RoleId))
|
||||||
|
.Select(er => er.EmployeeId)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
var data = new Dictionary<string, string>()
|
||||||
|
{
|
||||||
|
{ "Keyword", "Assign_Task" },
|
||||||
|
{ "ProjectId", projectId.ToString() }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// List of device registration tokens to send the message to
|
||||||
|
|
||||||
|
var registrationTokensForNotificationTask = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
var registrationTokensForNotification = await dbContext.FCMTokenMappings.Where(ft => teamMembers.Contains(ft.EmployeeId)).Select(ft => ft.FcmToken).ToListAsync();
|
||||||
|
var notificationFirebase = new Notification
|
||||||
|
{
|
||||||
|
Title = $"Task Assigned for {project?.ProjectName}",
|
||||||
|
Body = $"A task has been assigned to you by {name} at {location}"
|
||||||
|
};
|
||||||
|
|
||||||
|
await SendMessageToMultipleDevicesWithDataAsync(registrationTokensForNotification, notificationFirebase, data);
|
||||||
|
});
|
||||||
|
var registrationTokensForDataTask = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
var registrationTokensForData = await dbContext.FCMTokenMappings.Where(ft => employeeIds.Contains(ft.EmployeeId)).Select(ft => ft.FcmToken).ToListAsync();
|
||||||
|
|
||||||
|
await SendMessageToMultipleDevicesOnlyDataAsync(registrationTokensForData, data);
|
||||||
|
});
|
||||||
|
|
||||||
|
await Task.WhenAll(registrationTokensForNotificationTask, registrationTokensForDataTask);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Exception occured while get data for sending notification");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async Task SendMessageToMultipleDevicesWithDataAsync(List<string> registrationTokens, Notification notificationFirebase, Dictionary<string, string> data)
|
public async Task SendMessageToMultipleDevicesWithDataAsync(List<string> registrationTokens, Notification notificationFirebase, Dictionary<string, string> data)
|
||||||
{
|
{
|
||||||
using var scope = _serviceScopeFactory.CreateScope();
|
using var scope = _serviceScopeFactory.CreateScope();
|
||||||
@ -190,6 +290,56 @@ namespace Marco.Pms.Services.Service
|
|||||||
_logger.LogError(ex, "Exception Occured while sending notification to firebase");
|
_logger.LogError(ex, "Exception Occured while sending notification to firebase");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
public async Task SendMessageToMultipleDevicesOnlyDataAsync(List<string> registrationTokens, Dictionary<string, string> data)
|
||||||
|
{
|
||||||
|
using var scope = _serviceScopeFactory.CreateScope();
|
||||||
|
var _logger = scope.ServiceProvider.GetRequiredService<ILoggingService>();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var message = new MulticastMessage()
|
||||||
|
{
|
||||||
|
Tokens = registrationTokens,
|
||||||
|
Data = data
|
||||||
|
};
|
||||||
|
// Send the multicast message
|
||||||
|
var response = await FirebaseMessaging.DefaultInstance.SendEachForMulticastAsync(message);
|
||||||
|
|
||||||
|
_logger.LogInfo("{SuccessCount} messages were sent successfully.", response.SuccessCount);
|
||||||
|
|
||||||
|
if (response.FailureCount > 0)
|
||||||
|
{
|
||||||
|
var failedTokens = new List<string>();
|
||||||
|
for (int i = 0; i < response.Responses.Count; i++)
|
||||||
|
{
|
||||||
|
if (!response.Responses[i].IsSuccess)
|
||||||
|
{
|
||||||
|
failedTokens.Add(registrationTokens[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_logger.LogInfo("List of tokens that caused failures: " + string.Join(", ", failedTokens));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (FirebaseMessagingException ex)
|
||||||
|
{
|
||||||
|
// Log the specific Firebase error.
|
||||||
|
_logger.LogError(ex, "Error sending push notification");
|
||||||
|
|
||||||
|
// Check for specific error codes that indicate an invalid or unregistered token.
|
||||||
|
if (ex.MessagingErrorCode == MessagingErrorCode.Unregistered ||
|
||||||
|
ex.MessagingErrorCode == MessagingErrorCode.InvalidArgument)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("FCM token is invalid and should be deleted from the database");
|
||||||
|
|
||||||
|
// TODO: Implement the logic here to remove the invalid token from your database.
|
||||||
|
// Example: await YourTokenService.DeleteTokenAsync(loginDto.DeviceToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Exception Occured while sending notification to firebase");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
public async Task SendMessageToMultipleDevicesAsync(List<string> registrationTokens, Notification notificationFirebase)
|
public async Task SendMessageToMultipleDevicesAsync(List<string> registrationTokens, Notification notificationFirebase)
|
||||||
{
|
{
|
||||||
|
@ -6,5 +6,6 @@ namespace Marco.Pms.Services.Service.ServiceInterfaces
|
|||||||
{
|
{
|
||||||
Task SendLoginMessageAsync(string name);
|
Task SendLoginMessageAsync(string name);
|
||||||
Task SendAttendanceMessageAsync(Guid projectId, string Name, ATTENDANCE_MARK_TYPE markType, Guid tenantId);
|
Task SendAttendanceMessageAsync(Guid projectId, string Name, ATTENDANCE_MARK_TYPE markType, Guid tenantId);
|
||||||
|
Task SendAssignTaskMessageAsync(Guid workItemId, string name, List<Guid> teamMembers, Guid tenantId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user