From 672046d02e117f9b41b70fc625e1af0ab73ef678 Mon Sep 17 00:00:00 2001 From: "ashutosh.nehete" Date: Fri, 5 Sep 2025 17:48:25 +0530 Subject: [PATCH] Added the contact note filter in get all contact notes API --- .../Utilities/ContactNoteFilter.cs | 8 ++ .../Controllers/DirectoryController.cs | 4 +- .../Service/DirectoryService.cs | 74 +++++++++++++++++-- .../ServiceInterfaces/IDirectoryService.cs | 2 +- 4 files changed, 79 insertions(+), 9 deletions(-) create mode 100644 Marco.Pms.Model/Utilities/ContactNoteFilter.cs diff --git a/Marco.Pms.Model/Utilities/ContactNoteFilter.cs b/Marco.Pms.Model/Utilities/ContactNoteFilter.cs new file mode 100644 index 0000000..7db8115 --- /dev/null +++ b/Marco.Pms.Model/Utilities/ContactNoteFilter.cs @@ -0,0 +1,8 @@ +namespace Marco.Pms.Model.Utilities +{ + public class ContactNoteFilter + { + public List? CreatedByIds { get; set; } + public List? Organizations { get; set; } + } +} diff --git a/Marco.Pms.Services/Controllers/DirectoryController.cs b/Marco.Pms.Services/Controllers/DirectoryController.cs index 431122e..b4b6cfa 100644 --- a/Marco.Pms.Services/Controllers/DirectoryController.cs +++ b/Marco.Pms.Services/Controllers/DirectoryController.cs @@ -142,10 +142,10 @@ namespace Marco.Pms.Services.Controllers #region =================================================================== Contact Notes APIs =================================================================== [HttpGet("notes")] - public async Task GetListOFAllNotes([FromQuery] Guid? projectId, [FromQuery] int pageSize = 20, [FromQuery] int pageNumber = 1) + public async Task GetListOFAllNotes([FromQuery] Guid? projectId, [FromQuery] string? searchString, [FromQuery] string? filter, [FromQuery] int pageSize = 20, [FromQuery] int pageNumber = 1) { var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); - var response = await _directoryService.GetListOFAllNotesAsync(projectId, pageSize, pageNumber, tenantId, loggedInEmployee); + var response = await _directoryService.GetListOFAllNotesAsync(projectId, searchString, filter, pageSize, pageNumber, tenantId, loggedInEmployee); return StatusCode(response.StatusCode, response); } diff --git a/Marco.Pms.Services/Service/DirectoryService.cs b/Marco.Pms.Services/Service/DirectoryService.cs index 5fa811f..d522f66 100644 --- a/Marco.Pms.Services/Service/DirectoryService.cs +++ b/Marco.Pms.Services/Service/DirectoryService.cs @@ -118,7 +118,7 @@ namespace Marco.Pms.Services.Service } // --- Advanced Filtering from 'filter' parameter --- - ContactFilterDto? contactFilter = TryDeserializeFilter(filter); + ContactFilterDto? contactFilter = TryDeserializeContactFilter(filter); if (contactFilter?.BucketIds?.Any() ?? false) { // Note: Permission filtering is already applied. Here we further restrict by the user's filter choice. @@ -918,7 +918,6 @@ namespace Marco.Pms.Services.Service } } - #endregion #region =================================================================== Contact Post APIs =================================================================== @@ -1513,7 +1512,7 @@ namespace Marco.Pms.Services.Service #region =================================================================== Contact Notes APIs =================================================================== - public async Task> GetListOFAllNotesAsync(Guid? projectId, int pageSize, int pageNumber, Guid tenantId, Employee loggedInEmployee) + public async Task> GetListOFAllNotesAsync(Guid? projectId, string? searchString, string? filter, int pageSize, int pageNumber, Guid tenantId, Employee loggedInEmployee) { _logger.LogInfo("Initiating GetListOFAllNotesAsync. TenantId: {TenantId}, ProjectId: {ProjectId}, PageSize: {PageSize}, PageNumber: {PageNumber}, EmployeeId: {EmployeeId}", tenantId, projectId ?? Guid.Empty, pageSize, pageNumber, loggedInEmployee.Id); @@ -1610,6 +1609,31 @@ namespace Marco.Pms.Services.Service notesQuery = notesQuery.Where(cn => projectContactIds.Contains(cn.ContactId)); } + // --- Advanced Filtering from 'filter' parameter --- + ContactNoteFilter? contactNoteFilter = TryDeserializeContactNoteFilter(filter); + if (contactNoteFilter != null) + { + if (contactNoteFilter.CreatedByIds?.Any() ?? false) + { + notesQuery = notesQuery.Where(cn => contactNoteFilter.CreatedByIds.Contains(cn.CreatedById)); + } + if (contactNoteFilter.Organizations?.Any() ?? false) + { + notesQuery = notesQuery.Where(cn => cn.Contact != null && contactNoteFilter.Organizations.Contains(cn.Contact.Organization)); + } + } + + // --- Search Term Filtering --- + if (!string.IsNullOrWhiteSpace(searchString)) + { + var searchTermLower = searchString.ToLower(); + notesQuery = notesQuery.Where(c => + (c.Contact != null && c.Contact.Name.ToLower().Contains(searchTermLower)) || + (c.Contact != null && c.Contact.Organization != null && c.Contact.Organization.ToLower().Contains(searchTermLower)) || + c.Note.ToLower().Contains(searchTermLower) + ); + } + // Pagination safeguard pageSize = pageSize < 1 ? 25 : pageSize; pageNumber = pageNumber < 1 ? 1 : pageNumber; @@ -2735,7 +2759,7 @@ namespace Marco.Pms.Services.Service return result; } - private ContactFilterDto? TryDeserializeFilter(string? filter) + private ContactFilterDto? TryDeserializeContactFilter(string? filter) { if (string.IsNullOrWhiteSpace(filter)) { @@ -2752,7 +2776,7 @@ namespace Marco.Pms.Services.Service } catch (JsonException ex) { - _logger.LogError(ex, "[{MethodName}] Failed to directly deserialize filter. Attempting to unescape and re-parse. Filter: {Filter}", nameof(TryDeserializeFilter), filter); + _logger.LogError(ex, "[{MethodName}] Failed to directly deserialize filter. Attempting to unescape and re-parse. Filter: {Filter}", nameof(TryDeserializeContactFilter), filter); // If direct deserialization fails, it might be an escaped string (common with tools like Postman or some mobile clients). try @@ -2767,7 +2791,45 @@ namespace Marco.Pms.Services.Service catch (JsonException ex1) { // If both attempts fail, log the final error and return null. - _logger.LogError(ex1, "[{MethodName}] All attempts to deserialize the filter failed. Filter will be ignored. Filter: {Filter}", nameof(TryDeserializeFilter), filter); + _logger.LogError(ex1, "[{MethodName}] All attempts to deserialize the filter failed. Filter will be ignored. Filter: {Filter}", nameof(TryDeserializeContactFilter), filter); + return null; + } + } + return expenseFilter; + } + private ContactNoteFilter? TryDeserializeContactNoteFilter(string? filter) + { + if (string.IsNullOrWhiteSpace(filter)) + { + return null; + } + + var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; + ContactNoteFilter? expenseFilter = null; + + try + { + // First, try to deserialize directly. This is the expected case (e.g., from a web client). + expenseFilter = JsonSerializer.Deserialize(filter, options); + } + catch (JsonException ex) + { + _logger.LogError(ex, "[{MethodName}] Failed to directly deserialize filter. Attempting to unescape and re-parse. Filter: {Filter}", nameof(TryDeserializeContactNoteFilter), filter); + + // If direct deserialization fails, it might be an escaped string (common with tools like Postman or some mobile clients). + try + { + // Unescape the string first, then deserialize the result. + string unescapedJsonString = JsonSerializer.Deserialize(filter, options) ?? ""; + if (!string.IsNullOrWhiteSpace(unescapedJsonString)) + { + expenseFilter = JsonSerializer.Deserialize(unescapedJsonString, options); + } + } + catch (JsonException ex1) + { + // If both attempts fail, log the final error and return null. + _logger.LogError(ex1, "[{MethodName}] All attempts to deserialize the filter failed. Filter will be ignored. Filter: {Filter}", nameof(TryDeserializeContactNoteFilter), filter); return null; } } diff --git a/Marco.Pms.Services/Service/ServiceInterfaces/IDirectoryService.cs b/Marco.Pms.Services/Service/ServiceInterfaces/IDirectoryService.cs index 49db0c7..d499d1e 100644 --- a/Marco.Pms.Services/Service/ServiceInterfaces/IDirectoryService.cs +++ b/Marco.Pms.Services/Service/ServiceInterfaces/IDirectoryService.cs @@ -21,7 +21,7 @@ namespace Marco.Pms.Services.Service.ServiceInterfaces - Task> GetListOFAllNotesAsync(Guid? projectId, int pageSize, int pageNumber, Guid tenantId, Employee loggedInEmployee); + Task> GetListOFAllNotesAsync(Guid? projectId, string? searchString, string? filter, int pageSize, int pageNumber, Guid tenantId, Employee loggedInEmployee); Task> GetNoteListByContactIdAsync(Guid id, bool active, Guid tenantId, Employee loggedInEmployee); Task> GetContactNotesFilterObjectAsync(Guid tenantId, Employee loggedInEmployee); Task> CreateContactNoteAsync(CreateContactNoteDto noteDto, Guid tenantId, Employee loggedInEmployee);