From a9f1a99e6fee7d51c9d810d7e6ef94936cc9539e Mon Sep 17 00:00:00 2001 From: "ashutosh.nehete" Date: Thu, 13 Nov 2025 14:49:56 +0530 Subject: [PATCH] Added an API to get list of comments using job ID (if possible) --- .../Controllers/ServiceProjectController.cs | 10 ++ .../ServiceInterfaces/IServiceProject.cs | 1 + .../Service/ServiceProjectService.cs | 91 +++++++++++++++++++ 3 files changed, 102 insertions(+) diff --git a/Marco.Pms.Services/Controllers/ServiceProjectController.cs b/Marco.Pms.Services/Controllers/ServiceProjectController.cs index 1935b32..f05775c 100644 --- a/Marco.Pms.Services/Controllers/ServiceProjectController.cs +++ b/Marco.Pms.Services/Controllers/ServiceProjectController.cs @@ -102,6 +102,7 @@ namespace Marco.Pms.Services.Controllers return StatusCode(response.StatusCode, response); } + [HttpGet("job/details/{id}")] public async Task GetJobTicketDetails(Guid id) { @@ -111,6 +112,15 @@ namespace Marco.Pms.Services.Controllers return StatusCode(response.StatusCode, response); } + [HttpGet("job/comment/list")] + public async Task GetCommentListByJobTicket([FromQuery] Guid? jobTicketId, [FromQuery] int pageNumber = 1, [FromQuery] int pageSize = 20) + { + Employee loggedInEmploee = await _userHelper.GetCurrentEmployeeAsync(); + var response = await _serviceProject.GetCommentListByJobTicketAsync(jobTicketId, pageNumber, pageSize, loggedInEmploee, tenantId); + + return StatusCode(response.StatusCode, response); + } + [HttpPost("job/create")] public async Task CreateJobTicket(CreateJobTicketDto model) { diff --git a/Marco.Pms.Services/Service/ServiceInterfaces/IServiceProject.cs b/Marco.Pms.Services/Service/ServiceInterfaces/IServiceProject.cs index 7ebb8f6..b0628be 100644 --- a/Marco.Pms.Services/Service/ServiceInterfaces/IServiceProject.cs +++ b/Marco.Pms.Services/Service/ServiceInterfaces/IServiceProject.cs @@ -19,6 +19,7 @@ namespace Marco.Pms.Services.Service.ServiceInterfaces Task> GetJobTicketsListAsync(Guid? projectId, int pageNumber, int pageSize, bool isActive, Employee loggedInEmployee, Guid tenantId); Task> GetJobTicketDetailsAsync(Guid id, Employee loggedInEmployee, Guid tenantId); Task> CreateJobTicketAsync(CreateJobTicketDto model, Employee loggedInEmployee, Guid tenantId); + Task> GetCommentListByJobTicketAsync(Guid? jobTicketId, int pageNumber, int pageSize, Employee loggedInEmployee, Guid tenantId); Task> AddCommentToJobTicketAsync(JobCommentDto model, Employee loggedInEmployee, Guid tenantId); #endregion } diff --git a/Marco.Pms.Services/Service/ServiceProjectService.cs b/Marco.Pms.Services/Service/ServiceProjectService.cs index c7f50ba..6127168 100644 --- a/Marco.Pms.Services/Service/ServiceProjectService.cs +++ b/Marco.Pms.Services/Service/ServiceProjectService.cs @@ -597,6 +597,11 @@ namespace Marco.Pms.Services.Service // Map comments, assignees, and tags to their respective viewmodels var commentVMs = _mapper.Map>(commentTask.Result); + commentVMs = commentVMs.Select(vm => + { + vm.JobTicket = _mapper.Map(jobTicket); + return vm; + }).ToList(); var assigneeVMs = _mapper.Map>(assigneeTask.Result); var tagVMs = _mapper.Map>(tagTask.Result); @@ -617,6 +622,91 @@ namespace Marco.Pms.Services.Service return ApiResponse.ErrorResponse("Internal Server Error", "Failed to retrieve job details. Please try again later.", 500); } } + /// + /// Retrieves a paginated list of comments for a specified job ticket within a tenant context. + /// + /// Optional job ticket identifier to filter comments. + /// Page number (1-based index) for pagination. + /// Page size for pagination. + /// Employee making the request (for logging). + /// Tenant context to scope data. + /// ApiResponse with paged comment view models or error details. + public async Task> GetCommentListByJobTicketAsync(Guid? jobTicketId, int pageNumber, int pageSize, Employee loggedInEmployee, Guid tenantId) + { + if (tenantId == Guid.Empty) + { + _logger.LogWarning("TenantId missing in comment list request by employee {EmployeeId}", loggedInEmployee.Id); + return ApiResponse.ErrorResponse("Access Denied", "Invalid tenant context.", 403); + } + + if (pageNumber < 1 || pageSize < 1) + { + _logger.LogInfo("Invalid pagination parameters in comment list request. PageNumber: {PageNumber}, PageSize: {PageSize}", pageNumber, pageSize); + return ApiResponse.ErrorResponse("Bad Request", "Page number and size must be greater than zero.", 400); + } + + try + { + _logger.LogInfo("Fetching comment list for jobTicketId {JobTicketId} by employee {EmployeeId} in tenant {TenantId}", + jobTicketId ?? Guid.Empty, loggedInEmployee.Id, tenantId); + + var commentQuery = _context.JobComments + .Include(jc => jc.JobTicket).ThenInclude(jt => jt!.Status) + .Include(jc => jc.CreatedBy).ThenInclude(e => e!.JobRole) + .Where(jc => + jc.TenantId == tenantId && + jc.JobTicket != null && + jc.CreatedBy != null && + jc.CreatedBy.JobRole != null); + + // Validate and filter by job ticket if specified + if (jobTicketId.HasValue) + { + var jobTicketExists = await _context.JobTickets.AnyAsync(jt => + jt.Id == jobTicketId && jt.TenantId == tenantId); + + if (!jobTicketExists) + { + _logger.LogWarning("Job ticket {JobTicketId} not found in tenant {TenantId} for comment listing", jobTicketId, tenantId); + return ApiResponse.ErrorResponse("Job not found", "Job ticket not found.", 404); + } + + commentQuery = commentQuery.Where(jc => jc.JobTicketId == jobTicketId.Value); + } + + var totalEntities = await commentQuery.CountAsync(); + var totalPages = (int)Math.Ceiling((double)totalEntities / pageSize); + + var comments = await commentQuery + .OrderByDescending(jc => jc.CreatedAt) + .Skip((pageNumber - 1) * pageSize) + .Take(pageSize) + .ToListAsync(); + + var commentVMs = _mapper.Map>(comments); + + var response = new + { + CurrentPage = pageNumber, + TotalPages = totalPages, + TotalEntities = totalEntities, + Data = commentVMs, + }; + + _logger.LogInfo("{Count} comments fetched successfully for jobTicketId {JobTicketId} by employee {EmployeeId}", + commentVMs.Count, jobTicketId ?? Guid.Empty, loggedInEmployee.Id); + + return ApiResponse.SuccessResponse(response, $"{commentVMs.Count} record(s) fetched successfully.", 200); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error fetching comments for jobTicketId {JobTicketId} by employee {EmployeeId} in tenant {TenantId}", + jobTicketId ?? Guid.Empty, loggedInEmployee.Id, tenantId); + + return ApiResponse.ErrorResponse("Internal Server Error", "Failed to fetch comments. Please try again later.", 500); + } + } + /// /// Creates a new job ticket with optional assignees and tags within a transactional scope. @@ -796,6 +886,7 @@ namespace Marco.Pms.Services.Service return ApiResponse.ErrorResponse("Internal Server Error", "An unexpected error occurred.", 500); } } + /// /// Adds a new comment to an existing job ticket within the tenant context. ///