459 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			459 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
using Marco.Pms.DataAccess.Data;
 | 
						|
using Marco.Pms.Model.Activities;
 | 
						|
using Marco.Pms.Model.Dtos.Attendance;
 | 
						|
using Marco.Pms.Model.Employees;
 | 
						|
using Marco.Pms.Model.Projects;
 | 
						|
using Marco.Pms.Model.Utilities;
 | 
						|
using Marco.Pms.Model.ViewModels.DashBoard;
 | 
						|
using Marco.Pms.Services.Service;
 | 
						|
using MarcoBMS.Services.Helpers;
 | 
						|
using MarcoBMS.Services.Service;
 | 
						|
using Microsoft.AspNetCore.Authorization;
 | 
						|
using Microsoft.AspNetCore.Mvc;
 | 
						|
using Microsoft.EntityFrameworkCore;
 | 
						|
 | 
						|
namespace Marco.Pms.Services.Controllers
 | 
						|
{
 | 
						|
    [Authorize]
 | 
						|
    [Route("api/[controller]")]
 | 
						|
    [ApiController]
 | 
						|
    public class DashboardController : ControllerBase
 | 
						|
    {
 | 
						|
        private readonly ApplicationDbContext _context;
 | 
						|
        private readonly UserHelper _userHelper;
 | 
						|
        private readonly ILoggingService _logger;
 | 
						|
        private readonly PermissionServices _permissionServices;
 | 
						|
        public DashboardController(ApplicationDbContext context, UserHelper userHelper, ILoggingService logger, PermissionServices permissionServices)
 | 
						|
        {
 | 
						|
            _context = context;
 | 
						|
            _userHelper = userHelper;
 | 
						|
            _logger = logger;
 | 
						|
            _permissionServices = permissionServices;
 | 
						|
        }
 | 
						|
        [HttpGet("progression")]
 | 
						|
        public async Task<IActionResult> GetGraph([FromQuery] double days, [FromQuery] string FromDate, [FromQuery] Guid? projectId)
 | 
						|
        {
 | 
						|
            var tenantId = _userHelper.GetTenantId();
 | 
						|
            var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
						|
 | 
						|
            DateTime fromDate = new DateTime();
 | 
						|
            DateTime toDate = new DateTime();
 | 
						|
            List<ProjectProgressionVM>? projectProgressionVMs = new List<ProjectProgressionVM>();
 | 
						|
            if (FromDate != null && DateTime.TryParse(FromDate, out fromDate) == false)
 | 
						|
            {
 | 
						|
                return BadRequest(ApiResponse<object>.ErrorResponse("Invalid starting date.", "Invalid starting date.", 400));
 | 
						|
 | 
						|
            }
 | 
						|
            var firstTask = await _context.TaskAllocations.Select(t => new { t.TenantId, t.AssignmentDate }).FirstOrDefaultAsync(t => t.TenantId == tenantId);
 | 
						|
            if (FromDate == null) fromDate = DateTime.UtcNow.Date;
 | 
						|
            if (firstTask == null) firstTask = new { TenantId = tenantId, AssignmentDate = DateTime.UtcNow };
 | 
						|
 | 
						|
 | 
						|
            if (days >= 0)
 | 
						|
            {
 | 
						|
                double negativeDays = 0 - days;
 | 
						|
                toDate = fromDate.AddDays(negativeDays);
 | 
						|
 | 
						|
                if (firstTask != null && (firstTask.AssignmentDate.Date >= toDate.Date))
 | 
						|
                {
 | 
						|
                    toDate = firstTask.AssignmentDate;
 | 
						|
                }
 | 
						|
                if (projectId == null)
 | 
						|
                {
 | 
						|
                    List<TaskAllocation> tasks = await _context.TaskAllocations.Where(t => t.AssignmentDate.Date <= fromDate.Date && t.AssignmentDate.Date >= toDate.Date && t.TenantId == tenantId).ToListAsync();
 | 
						|
 | 
						|
                    double flagDays = 0;
 | 
						|
                    while (negativeDays < flagDays)
 | 
						|
                    {
 | 
						|
                        ProjectProgressionVM ProjectProgressionVM = new ProjectProgressionVM();
 | 
						|
                        ProjectProgressionVM.ProjectId = projectId != null ? projectId.Value : Guid.Empty;
 | 
						|
                        ProjectProgressionVM.ProjectName = "";
 | 
						|
                        var date = fromDate.AddDays(flagDays);
 | 
						|
                        if (date >= (firstTask != null ? firstTask.AssignmentDate.Date : null))
 | 
						|
                        {
 | 
						|
                            var todayTasks = tasks.Where(t => t.AssignmentDate.Date == date.Date).ToList();
 | 
						|
                            double plannedTaks = 0;
 | 
						|
                            double completedTasks = 0;
 | 
						|
                            ProjectProgressionVM.Date = date;
 | 
						|
 | 
						|
                            foreach (var task in todayTasks)
 | 
						|
                            {
 | 
						|
                                plannedTaks += task.PlannedTask;
 | 
						|
                                completedTasks += task.CompletedTask;
 | 
						|
                            }
 | 
						|
                            ProjectProgressionVM.PlannedTask = plannedTaks;
 | 
						|
                            ProjectProgressionVM.CompletedTask = completedTasks;
 | 
						|
 | 
						|
                            projectProgressionVMs.Add(ProjectProgressionVM);
 | 
						|
                        }
 | 
						|
                        flagDays -= 1;
 | 
						|
                    }
 | 
						|
                    _logger.LogInfo("Project Progression report for all projects fetched successfully by employee {EmployeeId}", LoggedInEmployee.Id);
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    var project = await _context.Projects.FirstOrDefaultAsync(p => p.Id == projectId);
 | 
						|
                    List<Building> buildings = await _context.Buildings.Where(b => b.ProjectId == projectId && b.TenantId == tenantId).ToListAsync();
 | 
						|
                    List<Guid> idList = buildings.Select(b => b.Id).ToList();
 | 
						|
 | 
						|
                    List<Floor> floors = await _context.Floor.Where(f => idList.Contains(f.BuildingId) && f.TenantId == tenantId).ToListAsync();
 | 
						|
                    idList = floors.Select(f => f.Id).ToList();
 | 
						|
 | 
						|
                    List<WorkArea> workAreas = await _context.WorkAreas.Where(a => idList.Contains(a.FloorId) && a.TenantId == tenantId).ToListAsync();
 | 
						|
                    idList = workAreas.Select(a => a.Id).ToList();
 | 
						|
 | 
						|
                    List<WorkItem> workItems = await _context.WorkItems.Where(i => idList.Contains(i.WorkAreaId) && i.TenantId == tenantId).ToListAsync();
 | 
						|
                    idList = workItems.Select(i => i.Id).ToList();
 | 
						|
 | 
						|
                    List<TaskAllocation> tasks = await _context.TaskAllocations.Where(t => idList.Contains(t.WorkItemId) && t.AssignmentDate.Date <= fromDate.Date && t.AssignmentDate.Date >= toDate.Date && t.TenantId == tenantId).ToListAsync();
 | 
						|
                    if (project != null)
 | 
						|
                    {
 | 
						|
                        double flagDays = 0;
 | 
						|
                        while (negativeDays < flagDays)
 | 
						|
                        {
 | 
						|
                            ProjectProgressionVM projectProgressionVM = new ProjectProgressionVM();
 | 
						|
                            projectProgressionVM.ProjectId = projectId.Value;
 | 
						|
                            projectProgressionVM.ProjectName = project.Name;
 | 
						|
                            var date = fromDate.AddDays(flagDays);
 | 
						|
                            if (date >= (firstTask != null ? firstTask.AssignmentDate.Date : null))
 | 
						|
                            {
 | 
						|
                                var todayTasks = tasks.Where(t => t.AssignmentDate.Date == date.Date).ToList();
 | 
						|
                                double plannedTaks = 0;
 | 
						|
                                double completedTasks = 0;
 | 
						|
                                projectProgressionVM.Date = date;
 | 
						|
 | 
						|
                                foreach (var task in todayTasks)
 | 
						|
                                {
 | 
						|
                                    plannedTaks += task.PlannedTask;
 | 
						|
                                    completedTasks += task.CompletedTask;
 | 
						|
                                }
 | 
						|
                                projectProgressionVM.PlannedTask = plannedTaks;
 | 
						|
                                projectProgressionVM.CompletedTask = completedTasks;
 | 
						|
 | 
						|
                                projectProgressionVMs.Add(projectProgressionVM);
 | 
						|
                            }
 | 
						|
 | 
						|
                            flagDays -= 1;
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                    _logger.LogInfo("Project Progression  for project {ProjectId} fetched successfully by employee {EmployeeId}", projectId, LoggedInEmployee.Id);
 | 
						|
                }
 | 
						|
            }
 | 
						|
            return Ok(ApiResponse<object>.SuccessResponse(projectProgressionVMs, "Success", 200));
 | 
						|
        }
 | 
						|
 | 
						|
        [HttpGet("projects")]
 | 
						|
        public async Task<IActionResult> GetProjectCount()
 | 
						|
        {
 | 
						|
            var tenantId = _userHelper.GetTenantId();
 | 
						|
            var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
						|
 | 
						|
            var projects = await _context.Projects.Where(p => p.TenantId == tenantId).ToListAsync();
 | 
						|
            var projectStatus = await _context.StatusMasters.Where(s => s.Status == "Active" || s.Status == "In Progress").ToListAsync();
 | 
						|
            var projectStatusIds = projectStatus.Select(s => s.Id).ToList();
 | 
						|
            var ongoingProjects = projects.Where(p => projectStatusIds.Contains(p.ProjectStatusId)).ToList();
 | 
						|
 | 
						|
            ProjectDashboardVM projectDashboardVM = new ProjectDashboardVM
 | 
						|
            {
 | 
						|
                TotalProjects = projects.Count(),
 | 
						|
                OngoingProjects = ongoingProjects.Count()
 | 
						|
            };
 | 
						|
            _logger.LogInfo("Number of total ongoing projects fetched by employee {EmployeeId}", LoggedInEmployee.Id);
 | 
						|
            return Ok(ApiResponse<object>.SuccessResponse(projectDashboardVM, "Success", 200));
 | 
						|
        }
 | 
						|
 | 
						|
        [HttpGet("teams")]
 | 
						|
        public async Task<IActionResult> GetTotalEmployees()
 | 
						|
        {
 | 
						|
            var tenantId = _userHelper.GetTenantId();
 | 
						|
            var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
						|
            var date = DateTime.UtcNow.Date;
 | 
						|
 | 
						|
            var Employees = await _context.Employees.Where(e => e.TenantId == tenantId && e.IsActive == true).Select(e => e.Id).ToListAsync();
 | 
						|
 | 
						|
            var checkedInEmployee = await _context.Attendes.Where(e => e.InTime != null ? e.InTime.Value.Date == date : false).Select(e => e.EmployeeID).ToListAsync();
 | 
						|
 | 
						|
            TeamDashboardVM teamDashboardVM = new TeamDashboardVM
 | 
						|
            {
 | 
						|
                TotalEmployees = Employees.Count(),
 | 
						|
                InToday = checkedInEmployee.Distinct().Count()
 | 
						|
            };
 | 
						|
            _logger.LogInfo("Today's total checked in employees fetched by employee {EmployeeId}", LoggedInEmployee.Id);
 | 
						|
            return Ok(ApiResponse<object>.SuccessResponse(teamDashboardVM, "Success", 200));
 | 
						|
        }
 | 
						|
 | 
						|
        [HttpGet("tasks")]
 | 
						|
        public async Task<IActionResult> GetTotalTasks()
 | 
						|
        {
 | 
						|
            var tenantId = _userHelper.GetTenantId();
 | 
						|
            var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
						|
            var Tasks = await _context.WorkItems.Where(t => t.TenantId == tenantId).Select(t => new { PlannedWork = t.PlannedWork, CompletedWork = t.CompletedWork }).ToListAsync();
 | 
						|
            TasksDashboardVM tasksDashboardVM = new TasksDashboardVM
 | 
						|
            {
 | 
						|
                TotalTasks = 0,
 | 
						|
                CompletedTasks = 0
 | 
						|
            };
 | 
						|
            foreach (var task in Tasks)
 | 
						|
            {
 | 
						|
                tasksDashboardVM.TotalTasks += task.PlannedWork;
 | 
						|
                tasksDashboardVM.CompletedTasks += task.CompletedWork;
 | 
						|
            }
 | 
						|
            _logger.LogInfo("Total targeted tasks and total completed tasks fetched by employee {EmployeeId}", LoggedInEmployee.Id);
 | 
						|
            return Ok(ApiResponse<object>.SuccessResponse(tasksDashboardVM, "Success", 200));
 | 
						|
        }
 | 
						|
 | 
						|
        [HttpGet("pending-attendance")]
 | 
						|
        public async Task<IActionResult> GetPendingAttendance()
 | 
						|
        {
 | 
						|
            Guid tenantId = _userHelper.GetTenantId();
 | 
						|
            var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
						|
 | 
						|
            var attendance = await _context.Attendes.Where(a => a.EmployeeID == LoggedInEmployee.Id && a.TenantId == tenantId).ToListAsync();
 | 
						|
            if (attendance.Any())
 | 
						|
            {
 | 
						|
                var pendingRegularization = attendance.Where(a => a.Activity == ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE).ToList().Count;
 | 
						|
                var pendingCheckOut = attendance.Where(a => a.OutTime == null).ToList().Count;
 | 
						|
                var response = new
 | 
						|
                {
 | 
						|
                    PendingRegularization = pendingRegularization,
 | 
						|
                    PendingCheckOut = pendingCheckOut
 | 
						|
                };
 | 
						|
                _logger.LogInfo("Number of pending regularization and pending check-out are fetched successfully for employee {EmployeeId}", LoggedInEmployee.Id);
 | 
						|
                return Ok(ApiResponse<object>.SuccessResponse(response, "Pending regularization and pending check-out are fetched successfully", 200));
 | 
						|
            }
 | 
						|
            _logger.LogError("No attendance entry was found for employee {EmployeeId}", LoggedInEmployee.Id);
 | 
						|
            return NotFound(ApiResponse<object>.ErrorResponse("No attendance entry was found for this employee", "No attendance entry was found for this employee", 404));
 | 
						|
        }
 | 
						|
 | 
						|
        [HttpGet("project-attendance/{projectId}")]
 | 
						|
        public async Task<IActionResult> GetProjectAttendance(Guid projectId, [FromQuery] string? date)
 | 
						|
        {
 | 
						|
            Guid tenantId = _userHelper.GetTenantId();
 | 
						|
            var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
						|
 | 
						|
            DateTime currentDate = DateTime.UtcNow;
 | 
						|
            List<ProjectProgressionVM>? projectProgressionVMs = new List<ProjectProgressionVM>();
 | 
						|
            if (date != null && DateTime.TryParse(date, out currentDate) == false)
 | 
						|
            {
 | 
						|
                _logger.LogError($"user send invalid date");
 | 
						|
                return BadRequest(ApiResponse<object>.ErrorResponse("Invalid date.", "Invalid date.", 400));
 | 
						|
 | 
						|
            }
 | 
						|
            Project? project = await _context.Projects.FirstOrDefaultAsync(p => p.Id == projectId);
 | 
						|
            if (project == null)
 | 
						|
            {
 | 
						|
                _logger.LogError("Employee {EmployeeId} was attempted to get project attendance for date {Date}, but project not found in database", LoggedInEmployee.Id, currentDate);
 | 
						|
                return NotFound(ApiResponse<object>.ErrorResponse("Project not found", "Project not found", 404));
 | 
						|
            }
 | 
						|
            List<ProjectAllocation>? projectAllocation = await _context.ProjectAllocations.Where(p => p.ProjectId == projectId && p.IsActive && p.TenantId == tenantId).ToListAsync();
 | 
						|
            var employeeIds = projectAllocation.Select(p => p.EmployeeId).Distinct().ToList();
 | 
						|
            List<Employee>? employees = await _context.Employees.Where(e => employeeIds.Contains(e.Id)).ToListAsync();
 | 
						|
 | 
						|
            var attendances = await _context.Attendes.Where(a => employeeIds.Contains(a.EmployeeID) && a.ProjectID == projectId && a.InTime.HasValue && a.InTime.Value.Date == currentDate.Date).ToListAsync();
 | 
						|
            List<EmployeeAttendanceVM> employeeAttendanceVMs = new List<EmployeeAttendanceVM>();
 | 
						|
            foreach (var attendance in attendances)
 | 
						|
            {
 | 
						|
 | 
						|
                Employee? employee = employees.FirstOrDefault(e => e.Id == attendance.EmployeeID);
 | 
						|
                if (employee != null)
 | 
						|
                {
 | 
						|
                    EmployeeAttendanceVM employeeAttendanceVM = new EmployeeAttendanceVM
 | 
						|
                    {
 | 
						|
                        FirstName = employee.FirstName,
 | 
						|
                        LastName = employee.LastName,
 | 
						|
                        MiddleName = employee.MiddleName,
 | 
						|
                        Comment = attendance.Comment,
 | 
						|
                        InTime = attendance.InTime,
 | 
						|
                        OutTime = attendance.OutTime
 | 
						|
                    };
 | 
						|
 | 
						|
                    employeeAttendanceVMs.Add(employeeAttendanceVM);
 | 
						|
                }
 | 
						|
            }
 | 
						|
            ProjectAttendanceVM projectAttendanceVM = new ProjectAttendanceVM();
 | 
						|
            projectAttendanceVM.AttendanceTable = employeeAttendanceVMs;
 | 
						|
            projectAttendanceVM.CheckedInEmployee = attendances.Count;
 | 
						|
            projectAttendanceVM.AssignedEmployee = employeeIds.Count;
 | 
						|
 | 
						|
            _logger.LogInfo($"Attendance record for project {projectId} for date {currentDate.Date} by employee {LoggedInEmployee.Id}");
 | 
						|
            return Ok(ApiResponse<object>.SuccessResponse(projectAttendanceVM, $"Attendance record for project {project.Name} for date {currentDate.Date}", 200));
 | 
						|
        }
 | 
						|
 | 
						|
        [HttpGet("activities/{projectId}")]
 | 
						|
        public async Task<IActionResult> GetActivities(Guid projectId, [FromQuery] string? date)
 | 
						|
        {
 | 
						|
            Guid tenantId = _userHelper.GetTenantId();
 | 
						|
            var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
						|
 | 
						|
            DateTime currentDate = DateTime.UtcNow;
 | 
						|
            if (date != null && DateTime.TryParse(date, out currentDate) == false)
 | 
						|
            {
 | 
						|
                _logger.LogError($"user send invalid date");
 | 
						|
                return BadRequest(ApiResponse<object>.ErrorResponse("Invalid date.", "Invalid date.", 400));
 | 
						|
 | 
						|
            }
 | 
						|
            Project? project = await _context.Projects.FirstOrDefaultAsync(p => p.Id == projectId);
 | 
						|
            if (project == null)
 | 
						|
            {
 | 
						|
                _logger.LogError("Employee {EmployeeId} was attempted to get activities performed for date {Date}, but project not found in database", LoggedInEmployee.Id, currentDate);
 | 
						|
                return NotFound(ApiResponse<object>.ErrorResponse("Project not found", "Project not found", 404));
 | 
						|
            }
 | 
						|
 | 
						|
            var buildings = await _context.Buildings.Where(b => b.ProjectId == project.Id).ToListAsync();
 | 
						|
            var buildingIds = buildings.Select(b => b.Id).Distinct().ToList();
 | 
						|
 | 
						|
            var floors = await _context.Floor.Where(f => buildingIds.Contains(f.BuildingId)).ToListAsync();
 | 
						|
            var floorIds = floors.Select(f => f.Id).Distinct().ToList();
 | 
						|
 | 
						|
            var areas = await _context.WorkAreas.Where(a => floorIds.Contains(a.FloorId)).ToListAsync();
 | 
						|
            var areaIds = areas.Select(a => a.Id).Distinct().ToList();
 | 
						|
 | 
						|
            var workItems = await _context.WorkItems.Include(i => i.ActivityMaster).Where(i => areaIds.Contains(i.WorkAreaId)).ToListAsync();
 | 
						|
            var itemIds = workItems.Select(i => i.Id).Distinct().ToList();
 | 
						|
 | 
						|
            var tasks = await _context.TaskAllocations.Where(t => itemIds.Contains(t.WorkItemId) && t.AssignmentDate.Date == currentDate.Date).ToListAsync();
 | 
						|
            double totalPlannedTask = 0;
 | 
						|
            double totalCompletedTask = 0;
 | 
						|
            List<PerformedActivites> performedActivites = new List<PerformedActivites>();
 | 
						|
 | 
						|
            foreach (var task in tasks)
 | 
						|
            {
 | 
						|
                totalPlannedTask += task.PlannedTask;
 | 
						|
                totalCompletedTask += task.CompletedTask;
 | 
						|
 | 
						|
                WorkItem workItem = workItems.FirstOrDefault(i => i.Id == task.WorkItemId) ?? new WorkItem();
 | 
						|
                string activityName = (workItem.ActivityMaster != null ? workItem.ActivityMaster.ActivityName : "") ?? "";
 | 
						|
 | 
						|
                WorkArea workArea = areas.FirstOrDefault(a => a.Id == workItem.WorkAreaId) ?? new WorkArea();
 | 
						|
                string areaName = workArea.AreaName ?? "";
 | 
						|
 | 
						|
                Floor floor = floors.FirstOrDefault(f => f.Id == workArea.FloorId) ?? new Floor();
 | 
						|
                string floorName = floor.FloorName ?? "";
 | 
						|
 | 
						|
                Building building = buildings.FirstOrDefault(b => b.Id == floor.BuildingId) ?? new Building();
 | 
						|
                string buildingName = building.Name ?? "";
 | 
						|
 | 
						|
                PerformedActivites performedTask = new PerformedActivites
 | 
						|
                {
 | 
						|
                    ActivityName = activityName,
 | 
						|
                    BuldingName = buildingName,
 | 
						|
                    FloorName = floorName,
 | 
						|
                    WorkAreaName = areaName,
 | 
						|
                    AssignedToday = task.PlannedTask,
 | 
						|
                    CompletedToday = task.CompletedTask,
 | 
						|
                };
 | 
						|
                performedActivites.Add(performedTask);
 | 
						|
            }
 | 
						|
            var pendingReport = tasks.Where(t => t.ReportedDate == null).ToList().Count;
 | 
						|
 | 
						|
            ActivityReport report = new ActivityReport
 | 
						|
            {
 | 
						|
                PerformedActivites = performedActivites,
 | 
						|
                TotalCompletedWork = totalCompletedTask,
 | 
						|
                TotalPlannedWork = totalPlannedTask,
 | 
						|
                ReportPending = pendingReport,
 | 
						|
                TodaysAssigned = tasks.Count
 | 
						|
            };
 | 
						|
            _logger.LogInfo($"Record of performed activities for project {projectId} for date {currentDate.Date} by employee {LoggedInEmployee.Id}");
 | 
						|
            return Ok(ApiResponse<object>.SuccessResponse(report, $"Record of performed activities for project {project.Name} for date {currentDate.Date}", 200));
 | 
						|
        }
 | 
						|
 | 
						|
        [HttpGet("attendance-overview/{projectId}")]
 | 
						|
        public async Task<IActionResult> GetAttendanceOverView(Guid projectId, [FromQuery] string days)
 | 
						|
        {
 | 
						|
            _logger.LogInfo("GetAttendanceOverView called for ProjectId: {ProjectId}, Days: {Days}", projectId, days);
 | 
						|
 | 
						|
            // Step 1: Validate project existence
 | 
						|
            var project = await _context.Projects.AsNoTracking().FirstOrDefaultAsync(p => p.Id == projectId);
 | 
						|
            if (project == null)
 | 
						|
            {
 | 
						|
                _logger.LogWarning("Project not found for ProjectId: {ProjectId}", projectId);
 | 
						|
                return BadRequest(ApiResponse<object>.ErrorResponse("Project not found", "Project not found", 400));
 | 
						|
            }
 | 
						|
 | 
						|
            // Step 2: Permission check
 | 
						|
            var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
 | 
						|
            bool hasAssigned = await _permissionServices.HasProjectPermission(loggedInEmployee, projectId.ToString());
 | 
						|
 | 
						|
            if (!hasAssigned)
 | 
						|
            {
 | 
						|
                _logger.LogWarning("Unauthorized access attempt. EmployeeId: {EmployeeId}, ProjectId: {ProjectId}", loggedInEmployee.Id, projectId);
 | 
						|
                return StatusCode(403, ApiResponse<object>.ErrorResponse(
 | 
						|
                    "You don't have permission to access this feature",
 | 
						|
                    "You don't have permission to access this feature", 403));
 | 
						|
            }
 | 
						|
 | 
						|
            // Step 3: Validate and parse days input
 | 
						|
            if (!int.TryParse(days, out int dayCount) || dayCount <= 0)
 | 
						|
            {
 | 
						|
                _logger.LogWarning("Invalid days input received: {Days}", days);
 | 
						|
                return BadRequest(ApiResponse<object>.ErrorResponse("Invalid number of days", "Days must be a positive integer", 400));
 | 
						|
            }
 | 
						|
 | 
						|
            // Step 4: Define date range
 | 
						|
            DateTime today = DateTime.UtcNow.Date;
 | 
						|
            DateTime startDate = today.AddDays(-dayCount);
 | 
						|
 | 
						|
            // Step 5: Load project allocations and related job roles
 | 
						|
            var allocations = await _context.ProjectAllocations
 | 
						|
                .Where(pa => pa.ProjectId == projectId)
 | 
						|
                .ToListAsync();
 | 
						|
 | 
						|
            if (!allocations.Any())
 | 
						|
            {
 | 
						|
                _logger.LogInfo("No employee allocations found for project: {ProjectId}", projectId);
 | 
						|
                return Ok(ApiResponse<object>.SuccessResponse(new List<AttendanceOverviewVM>(), "No allocations found", 200));
 | 
						|
            }
 | 
						|
 | 
						|
            var jobRoleIds = allocations.Select(pa => pa.JobRoleId).Distinct().ToList();
 | 
						|
 | 
						|
            var jobRoles = await _context.JobRoles
 | 
						|
                .Where(jr => jobRoleIds.Contains(jr.Id))
 | 
						|
                .ToListAsync();
 | 
						|
 | 
						|
            // Step 6: Load attendance records for given date range
 | 
						|
            var attendances = await _context.Attendes
 | 
						|
                .Where(a =>
 | 
						|
                    a.ProjectID == projectId &&
 | 
						|
                    a.InTime.HasValue &&
 | 
						|
                    a.InTime.Value.Date >= startDate &&
 | 
						|
                    a.InTime.Value.Date <= today)
 | 
						|
                .ToListAsync();
 | 
						|
 | 
						|
            var overviewList = new List<AttendanceOverviewVM>();
 | 
						|
 | 
						|
            // Step 7: Process attendance per date per role
 | 
						|
            for (DateTime date = today; date > startDate; date = date.AddDays(-1))
 | 
						|
            {
 | 
						|
                foreach (var jobRole in jobRoles)
 | 
						|
                {
 | 
						|
                    var employeeIds = allocations
 | 
						|
                        .Where(pa => pa.JobRoleId == jobRole.Id)
 | 
						|
                        .Select(pa => pa.EmployeeId)
 | 
						|
                        .ToList();
 | 
						|
 | 
						|
                    int presentCount = attendances
 | 
						|
                        .Count(a => employeeIds.Contains(a.EmployeeID) && a.InTime!.Value.Date == date);
 | 
						|
 | 
						|
                    overviewList.Add(new AttendanceOverviewVM
 | 
						|
                    {
 | 
						|
                        Role = jobRole.Name,
 | 
						|
                        Date = date.ToString("yyyy-MM-dd"),
 | 
						|
                        Present = presentCount
 | 
						|
                    });
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            // Step 8: Order result for consistent presentation
 | 
						|
            var sortedResult = overviewList
 | 
						|
                .OrderByDescending(r => r.Date)
 | 
						|
                .ThenByDescending(r => r.Present)
 | 
						|
                .ToList();
 | 
						|
 | 
						|
            _logger.LogInfo("Attendance overview fetched. ProjectId: {ProjectId}, Records: {Count}", projectId, sortedResult.Count);
 | 
						|
 | 
						|
            return Ok(ApiResponse<object>.SuccessResponse(sortedResult, $"{sortedResult.Count} records fetched for attendance overview", 200));
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 |