diff --git a/Marco.Pms.Services/Controllers/AttendanceController.cs b/Marco.Pms.Services/Controllers/AttendanceController.cs index 3675dc6..7545959 100644 --- a/Marco.Pms.Services/Controllers/AttendanceController.cs +++ b/Marco.Pms.Services/Controllers/AttendanceController.cs @@ -1,5 +1,4 @@ -using FirebaseAdmin.Messaging; -using Marco.Pms.DataAccess.Data; +using Marco.Pms.DataAccess.Data; using Marco.Pms.Model.AttendanceModule; using Marco.Pms.Model.Dtos.Attendance; using Marco.Pms.Model.Employees; @@ -36,10 +35,11 @@ namespace MarcoBMS.Services.Controllers private readonly PermissionServices _permission; private readonly ILoggingService _logger; private readonly IHubContext _signalR; - + private readonly IFirebaseService _firebase; public AttendanceController( - ApplicationDbContext context, EmployeeHelper employeeHelper, IProjectServices projectServices, UserHelper userHelper, S3UploadService s3Service, ILoggingService logger, PermissionServices permission, IHubContext signalR) + ApplicationDbContext context, EmployeeHelper employeeHelper, IProjectServices projectServices, UserHelper userHelper, + S3UploadService s3Service, ILoggingService logger, PermissionServices permission, IHubContext signalR, IFirebaseService firebase) { _context = context; _employeeHelper = employeeHelper; @@ -49,6 +49,7 @@ namespace MarcoBMS.Services.Controllers _logger = logger; _permission = permission; _signalR = signalR; + _firebase = firebase; } private Guid GetTenantId() @@ -82,8 +83,8 @@ namespace MarcoBMS.Services.Controllers return Ok(ApiResponse.SuccessResponse(attendanceLogVMs, System.String.Format("{0} Attendance records fetched successfully", lstAttendance.Count), 200)); } - [HttpGet("log/employee/{employeeId}")] + [HttpGet("log/employee/{employeeId}")] public async Task GetAttendanceLogByEmployeeId(Guid employeeId, [FromQuery] string? dateFrom = null, [FromQuery] string? dateTo = null) { Guid TenantId = GetTenantId(); @@ -259,7 +260,6 @@ namespace MarcoBMS.Services.Controllers /// YYYY-MM-dd /// [HttpGet("project/team")] - public async Task EmployeeAttendanceByProject([FromQuery] Guid projectId, [FromQuery] bool IncludeInActive, [FromQuery] string? date = null) { Guid TenantId = GetTenantId(); @@ -364,7 +364,6 @@ namespace MarcoBMS.Services.Controllers } [HttpGet("regularize")] - public async Task GetRequestRegularizeAttendance([FromQuery] Guid projectId, [FromQuery] bool IncludeInActive) { Guid TenantId = GetTenantId(); @@ -578,6 +577,19 @@ namespace MarcoBMS.Services.Controllers var notification = new { LoggedInUserId = currentEmployee.Id, Keyword = "Attendance", Activity = sendActivity, ProjectId = attendance.ProjectID, Response = vm }; await _signalR.Clients.All.SendAsync("NotificationEventHandler", notification); _logger.LogInfo("Attendance for employee {FirstName} {LastName} has been marked", employee.FirstName ?? string.Empty, employee.LastName ?? string.Empty); + + _ = 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 = $"{vm.FirstName} {vm.LastName}"; + + await _firebase.SendAttendanceMessageAsync(attendance.ProjectID, name, recordAttendanceDot.Action, TenantId); + + }); + return Ok(ApiResponse.SuccessResponse(vm, "Attendance marked successfully.", 200)); } _logger.LogInfo("Attendance for employee {FirstName} {LastName} has been marked", employee.FirstName ?? string.Empty, employee.LastName ?? string.Empty); @@ -775,20 +787,19 @@ namespace MarcoBMS.Services.Controllers await _signalR.Clients.All.SendAsync("NotificationEventHandler", notification); - _logger.LogInfo("Attendance recorded for employee: {FullName}", $"{employee.FirstName} {employee.LastName}"); - - var message = new Message() + _ = Task.Run(async () => { - Token = recordAttendanceDot.DeviceToken, - Notification = new Notification - { - Title = "Hello from .NET", - Body = "This is a test message" - } - }; - string response = await FirebaseMessaging.DefaultInstance.SendAsync(message); - _logger.LogInfo("Firebase push notification messageId: {MessageId}", response); + // --- 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 = $"{vm.FirstName} {vm.LastName}"; + + await _firebase.SendAttendanceMessageAsync(attendance.ProjectID, name, recordAttendanceDot.Action, tenantId); + + }); + + _logger.LogInfo("Attendance recorded for employee: {FullName}", $"{employee.FirstName} {employee.LastName}"); return Ok(ApiResponse.SuccessResponse(vm, "Attendance marked successfully.", 200)); } catch (Exception ex) diff --git a/Marco.Pms.Services/Controllers/AuthController.cs b/Marco.Pms.Services/Controllers/AuthController.cs index 748dba3..a222f38 100644 --- a/Marco.Pms.Services/Controllers/AuthController.cs +++ b/Marco.Pms.Services/Controllers/AuthController.cs @@ -1,4 +1,5 @@ -using Marco.Pms.DataAccess.Data; +using FirebaseAdmin.Messaging; +using Marco.Pms.DataAccess.Data; using Marco.Pms.Model.Authentication; using Marco.Pms.Model.Dtos.Authentication; using Marco.Pms.Model.Dtos.Util; @@ -256,7 +257,14 @@ namespace MarcoBMS.Services.Controllers // --- 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. - await _firebase.SendMessageToMultipleDevicesAsync(); + + var notification = new Notification + { + Title = "Testing from API", + Body = "This messages comes from FireBase Services" + }; + + await _firebase.SendLoginMessageAsync(notification); }); return Ok(ApiResponse.SuccessResponse(responseData, "User logged in successfully.", 200)); diff --git a/Marco.Pms.Services/Service/FirebaseService.cs b/Marco.Pms.Services/Service/FirebaseService.cs index b45a8f7..ad00e5f 100644 --- a/Marco.Pms.Services/Service/FirebaseService.cs +++ b/Marco.Pms.Services/Service/FirebaseService.cs @@ -1,5 +1,7 @@ using FirebaseAdmin.Messaging; using Marco.Pms.DataAccess.Data; +using Marco.Pms.Model.Dtos.Attendance; +using Marco.Pms.Model.Entitlements; using Marco.Pms.Services.Service.ServiceInterfaces; using MarcoBMS.Services.Service; using Microsoft.EntityFrameworkCore; @@ -28,30 +30,99 @@ namespace Marco.Pms.Services.Service string response = await FirebaseMessaging.DefaultInstance.SendAsync(message); } - public async Task SendMessageToMultipleDevicesAsync() + public async Task SendLoginMessageAsync(Notification notificationFirebase) { await using var _context = await _dbContextFactory.CreateDbContextAsync(); // List of device registration tokens to send the message to var registrationTokens = await _context.FCMTokenMappings.Select(ft => ft.FcmToken).ToListAsync(); - //var registrationTokens = new List - //{ - // "YOUR_REGISTRATION_TOKEN_1", - // "YOUR_REGISTRATION_TOKEN_2", - // // add up to 500 tokens - //}; - var message = new MulticastMessage() + await SendMessageToMultipleDevicesAsync(registrationTokens, notificationFirebase); + } + public async Task SendAttendanceMessageAsync(Guid projectId, string Name, ATTENDANCE_MARK_TYPE markType, Guid tenantId) + { + await using var _context = await _dbContextFactory.CreateDbContextAsync(); + + var projectTask = Task.Run(async () => { - Tokens = registrationTokens, - Notification = new Notification + 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 { - Title = "Testing from API", - Body = "This messages comes from FireBase Services" - } + ProjectName = g.Select(pa => pa.Project!.Name).FirstOrDefault(), + EmployeeIds = g.Select(pa => pa.EmployeeId).Distinct().ToList() + }).FirstOrDefaultAsync(); + }); + + var roleTask = Task.Run(async () => + { + await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); + return await dbContext.RolePermissionMappings + .Where(rp => rp.FeaturePermissionId == PermissionsMaster.TeamAttendance) + .Select(rp => rp.ApplicationRoleId).ToListAsync(); + }); + + await Task.WhenAll(projectTask, roleTask); + + var applicationRoleIds = roleTask.Result; + var project = projectTask.Result; + + List projectAssignedEmployeeIds = project?.EmployeeIds ?? new List(); + + var employeeIds = await _context.EmployeeRoleMappings + .Where(er => projectAssignedEmployeeIds.Contains(er.EmployeeId) && applicationRoleIds.Contains(er.RoleId)) + .Select(er => er.RoleId) + .ToListAsync(); + + string body; + switch (markType) + { + case ATTENDANCE_MARK_TYPE.CHECK_IN: + body = $"{Name} Checked In for project {project?.ProjectName ?? ""}"; + break; + case ATTENDANCE_MARK_TYPE.CHECK_OUT: + body = $"{Name} Checked Out for project {project?.ProjectName ?? ""}"; + break; + case ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE: + body = $"{Name} Requested Regularization for project {project?.ProjectName ?? ""}"; + break; + case ATTENDANCE_MARK_TYPE.REGULARIZE: + body = $"Regularization Accepted of {Name} for Project {project?.ProjectName ?? ""}"; + break; + case ATTENDANCE_MARK_TYPE.REGULARIZE_REJECT: + body = $"Regularization Rejected of {Name} for Project {project?.ProjectName ?? ""}"; + break; + default: + body = string.Empty; + break; + } + + var notificationFirebase = new Notification + { + Title = "Attendance Marked", + Body = body }; + + // List of device registration tokens to send the message to + var registrationTokens = await _context.FCMTokenMappings + //.Where(ft => employeeIds.Contains(ft.EmployeeId) && ft.TenantId == tenantId) + .Select(ft => ft.FcmToken).ToListAsync(); + + await SendMessageToMultipleDevicesAsync(registrationTokens, notificationFirebase); + } + public async Task SendMessageToMultipleDevicesAsync(List registrationTokens, Notification notificationFirebase) + { + try { + var message = new MulticastMessage() + { + Tokens = registrationTokens, + Notification = notificationFirebase + }; // Send the multicast message var response = await FirebaseMessaging.DefaultInstance.SendEachForMulticastAsync(message); _logger.LogInfo("{SuccessCount} messages were sent successfully.", response.SuccessCount); diff --git a/Marco.Pms.Services/Service/ServiceInterfaces/IFirebaseService.cs b/Marco.Pms.Services/Service/ServiceInterfaces/IFirebaseService.cs index 1d5867d..66d8532 100644 --- a/Marco.Pms.Services/Service/ServiceInterfaces/IFirebaseService.cs +++ b/Marco.Pms.Services/Service/ServiceInterfaces/IFirebaseService.cs @@ -1,7 +1,11 @@ -namespace Marco.Pms.Services.Service.ServiceInterfaces +using FirebaseAdmin.Messaging; +using Marco.Pms.Model.Dtos.Attendance; + +namespace Marco.Pms.Services.Service.ServiceInterfaces { public interface IFirebaseService { - Task SendMessageToMultipleDevicesAsync(); + Task SendLoginMessageAsync(Notification notificationFirebase); + Task SendAttendanceMessageAsync(Guid projectId, string Name, ATTENDANCE_MARK_TYPE markType, Guid tenantId); } }