From 037a53fe0cc7f77f6054fb86c0eef227a4f517a1 Mon Sep 17 00:00:00 2001 From: "B. G. M. Prabuddhi" Date: Sun, 27 Jul 2025 03:36:15 +0530 Subject: [PATCH 1/3] Create search and filter endpoint for service centers --- .../Controllers/ServiceCentersController.cs | 53 +++++++++++++++++++ Vpassbackend/DTOs/PaginatedResult.cs | 9 ++++ Vpassbackend/DTOs/ServiceCenterSearchDTO.cs | 10 ++++ 3 files changed, 72 insertions(+) create mode 100644 Vpassbackend/DTOs/PaginatedResult.cs create mode 100644 Vpassbackend/DTOs/ServiceCenterSearchDTO.cs diff --git a/Vpassbackend/Controllers/ServiceCentersController.cs b/Vpassbackend/Controllers/ServiceCentersController.cs index 2f1715c..d5891f8 100644 --- a/Vpassbackend/Controllers/ServiceCentersController.cs +++ b/Vpassbackend/Controllers/ServiceCentersController.cs @@ -6,6 +6,7 @@ using Vpassbackend.Models; using Vpassbackend.Services; using System; +using System.Linq; namespace Vpassbackend.Controllers @@ -531,5 +532,57 @@ private bool ServiceCenterServiceExists(int id) { return _context.ServiceCenterServices.Any(e => e.ServiceCenterServiceId == id); } + + // POST endpoint to support search and filtering of service centers + [HttpPost("search")] + public async Task>> SearchServiceCenters([FromBody] ServiceCenterSearchDTO filter) + { + var query = _context.ServiceCenters.AsQueryable(); + + // Apply keyword search (matches name, email, telephone, or address) + if (!string.IsNullOrWhiteSpace(filter.SearchTerm)) + { + var keyword = filter.SearchTerm.ToLower(); + query = query.Where(sc => + (sc.Station_name != null && sc.Station_name.ToLower().Contains(keyword)) || + (sc.Email != null && sc.Email.ToLower().Contains(keyword)) || + (sc.Telephone != null && sc.Telephone.ToLower().Contains(keyword)) || + (sc.Address != null && sc.Address.ToLower().Contains(keyword))); + } + + // Optional status filter (e.g., active/inactive) + if (!string.IsNullOrWhiteSpace(filter.Status)) + { + query = query.Where(sc => sc.Station_status != null && + sc.Station_status.ToLower() == filter.Status.ToLower()); + } + + var total = await query.CountAsync(); // Total count before pagination + + // Apply pagination and project to DTO + var data = await query + .OrderBy(sc => sc.Station_id) + .Skip((filter.PageNumber - 1) * filter.PageSize) + .Take(filter.PageSize) + .Select(sc => new ServiceCenterDTO + { + Station_id = sc.Station_id, + OwnerName = sc.OwnerName, + VATNumber = sc.VATNumber, + RegisterationNumber = sc.RegisterationNumber, + Station_name = sc.Station_name, + Email = sc.Email, + Telephone = sc.Telephone, + Address = sc.Address, + Station_status = sc.Station_status + }) + .ToListAsync(); + + return new PaginatedResult + { + Data = data, + TotalCount = total + }; + } } } diff --git a/Vpassbackend/DTOs/PaginatedResult.cs b/Vpassbackend/DTOs/PaginatedResult.cs new file mode 100644 index 0000000..2d4244e --- /dev/null +++ b/Vpassbackend/DTOs/PaginatedResult.cs @@ -0,0 +1,9 @@ +namespace Vpassbackend.DTOs +{ + // Generic wrapper to return paginated results for any entity type + public class PaginatedResult + { + public List Data { get; set; } = new(); // Current page data + public int TotalCount { get; set; } // Total items across all pages + } +} diff --git a/Vpassbackend/DTOs/ServiceCenterSearchDTO.cs b/Vpassbackend/DTOs/ServiceCenterSearchDTO.cs new file mode 100644 index 0000000..d616222 --- /dev/null +++ b/Vpassbackend/DTOs/ServiceCenterSearchDTO.cs @@ -0,0 +1,10 @@ +namespace Vpassbackend.DTOs +{ + public class ServiceCenterSearchDTO + { + public string? SearchTerm { get; set; } // General keyword search (name, email, etc.) + public string? Status { get; set; } // "active", "inactive" + public int PageNumber { get; set; } = 1; // For pagination + public int PageSize { get; set; } = 10; // For pagination + } +} From d2a91d38da581dab881b8c8b3a33cb3c7c01963f Mon Sep 17 00:00:00 2001 From: "B. G. M. Prabuddhi" Date: Sun, 27 Jul 2025 11:37:43 +0530 Subject: [PATCH 2/3] Create search and filter endpoint and DTO for customer and vehicle --- .../Controllers/CustomersController.cs | 49 ++++++++++++++++++ .../Controllers/ServiceCentersController.cs | 2 +- .../Controllers/VehiclesController.cs | 51 +++++++++++++++++++ Vpassbackend/DTOs/CustomerListItemDTO.cs | 14 +++++ Vpassbackend/DTOs/CustomerVehicleDTO.cs | 14 +++++ ...ServiceCenterSearchDTO.cs => SearchDTO.cs} | 2 +- 6 files changed, 130 insertions(+), 2 deletions(-) create mode 100644 Vpassbackend/DTOs/CustomerListItemDTO.cs create mode 100644 Vpassbackend/DTOs/CustomerVehicleDTO.cs rename Vpassbackend/DTOs/{ServiceCenterSearchDTO.cs => SearchDTO.cs} (89%) diff --git a/Vpassbackend/Controllers/CustomersController.cs b/Vpassbackend/Controllers/CustomersController.cs index fdb3d34..7908f6c 100644 --- a/Vpassbackend/Controllers/CustomersController.cs +++ b/Vpassbackend/Controllers/CustomersController.cs @@ -220,5 +220,54 @@ public async Task DeleteVehicle(int customerId, int vehicleId) await _context.SaveChangesAsync(); return Ok("Vehicle deleted successfully."); } + + // POST: api/Customers/search + [Authorize(Roles = "SuperAdmin,Admin,ServiceCenterAdmin,Cashier")] + [HttpPost("search")] + public async Task SearchCustomers([FromBody] SearchDTO filter) + { + var query = _context.Customers.AsQueryable(); + + if (!string.IsNullOrEmpty(filter.SearchTerm)) + { + var term = filter.SearchTerm.ToLower(); + query = query.Where(c => + c.FirstName.ToLower().Contains(term) || + c.LastName.ToLower().Contains(term) || + c.Email.ToLower().Contains(term)); + } + + if (!string.IsNullOrEmpty(filter.Status)) + { + query = filter.Status == "Active" + ? query.Where(c => c.IsEmailVerified) + : query.Where(c => !c.IsEmailVerified); + } + + var totalCount = await query.CountAsync(); + + var data = await query + .OrderByDescending(c => c.CustomerId) + .Skip((filter.PageNumber - 1) * filter.PageSize) + .Take(filter.PageSize) + .Select(c => new CustomerListItemDTO + { + CustomerId = c.CustomerId, + FirstName = c.FirstName, + LastName = c.LastName, + Email = c.Email, + PhoneNumber = c.PhoneNumber, + Address = c.Address, + LoyaltyPoints = c.LoyaltyPoints, + NIC = c.NIC + }) + .ToListAsync(); + + return Ok(new PaginatedResult + { + Data = data, + TotalCount = totalCount + }); + } } } diff --git a/Vpassbackend/Controllers/ServiceCentersController.cs b/Vpassbackend/Controllers/ServiceCentersController.cs index d5891f8..8476926 100644 --- a/Vpassbackend/Controllers/ServiceCentersController.cs +++ b/Vpassbackend/Controllers/ServiceCentersController.cs @@ -535,7 +535,7 @@ private bool ServiceCenterServiceExists(int id) // POST endpoint to support search and filtering of service centers [HttpPost("search")] - public async Task>> SearchServiceCenters([FromBody] ServiceCenterSearchDTO filter) + public async Task>> SearchServiceCenters([FromBody] SearchDTO filter) { var query = _context.ServiceCenters.AsQueryable(); diff --git a/Vpassbackend/Controllers/VehiclesController.cs b/Vpassbackend/Controllers/VehiclesController.cs index 54c5633..f1d95a3 100644 --- a/Vpassbackend/Controllers/VehiclesController.cs +++ b/Vpassbackend/Controllers/VehiclesController.cs @@ -412,5 +412,56 @@ private bool ServiceHistoryExists(int serviceHistoryId, int vehicleId) { return _context.VehicleServiceHistories.Any(e => e.ServiceHistoryId == serviceHistoryId && e.VehicleId == vehicleId); } + + [HttpPost("search")] + public async Task>> SearchVehicles([FromBody] SearchDTO filter) + { + var query = _context.Vehicles.Include(v => v.Customer).AsQueryable(); + + if (!string.IsNullOrEmpty(filter.SearchTerm)) + { + var term = filter.SearchTerm.ToLower(); + query = query.Where(v => + v.Brand.ToLower().Contains(term) || + v.Model.ToLower().Contains(term) || + v.Customer.FirstName.ToLower().Contains(term) || + v.Customer.LastName.ToLower().Contains(term)); + } + + // Example filter on status - e.g. active/inactive + if (!string.IsNullOrEmpty(filter.Status)) + { + // Assuming there's an `IsActive` property on Vehicle or Customer + if (filter.Status == "Active") + query = query.Where(v => v.Year.HasValue && v.Year.Value >= DateTime.Now.Year - 10); + else + query = query.Where(v => !v.Year.HasValue || v.Year.Value < DateTime.Now.Year - 10); + } + + var totalCount = await query.CountAsync(); + + var items = await query + .OrderBy(v => v.VehicleId) + .Skip((filter.PageNumber - 1) * filter.PageSize) + .Take(filter.PageSize) + .Select(v => new CustomerVehicleDTO + { + VehicleId = v.VehicleId, + CustomerId = v.CustomerId, + Client = v.Customer.FirstName + " " + v.Customer.LastName, + Brand = v.Brand, + Model = v.Model, + Fuel = v.Fuel, + Year = v.Year, + RegistrationNumber = v.RegistrationNumber + }) + .ToListAsync(); + + return Ok(new PaginatedResult + { + Data = items, + TotalCount = totalCount + }); + } } } diff --git a/Vpassbackend/DTOs/CustomerListItemDTO.cs b/Vpassbackend/DTOs/CustomerListItemDTO.cs new file mode 100644 index 0000000..fe938d7 --- /dev/null +++ b/Vpassbackend/DTOs/CustomerListItemDTO.cs @@ -0,0 +1,14 @@ +namespace Vpassbackend.DTOs +{ + public class CustomerListItemDTO + { + public int CustomerId { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } + public string Email { get; set; } + public string PhoneNumber { get; set; } + public string Address { get; set; } + public int LoyaltyPoints { get; set; } + public string NIC { get; set; } + } +} diff --git a/Vpassbackend/DTOs/CustomerVehicleDTO.cs b/Vpassbackend/DTOs/CustomerVehicleDTO.cs new file mode 100644 index 0000000..152ba35 --- /dev/null +++ b/Vpassbackend/DTOs/CustomerVehicleDTO.cs @@ -0,0 +1,14 @@ +namespace Vpassbackend.DTOs +{ + public class CustomerVehicleDTO + { + public int VehicleId { get; set; } + public int CustomerId { get; set; } + public string Client { get; set; } + public string Brand { get; set; } + public string Model { get; set; } + public string Fuel { get; set; } + public int? Year { get; set; } + public string RegistrationNumber { get; set; } + } +} diff --git a/Vpassbackend/DTOs/ServiceCenterSearchDTO.cs b/Vpassbackend/DTOs/SearchDTO.cs similarity index 89% rename from Vpassbackend/DTOs/ServiceCenterSearchDTO.cs rename to Vpassbackend/DTOs/SearchDTO.cs index d616222..e37e2b8 100644 --- a/Vpassbackend/DTOs/ServiceCenterSearchDTO.cs +++ b/Vpassbackend/DTOs/SearchDTO.cs @@ -1,6 +1,6 @@ namespace Vpassbackend.DTOs { - public class ServiceCenterSearchDTO + public class SearchDTO { public string? SearchTerm { get; set; } // General keyword search (name, email, etc.) public string? Status { get; set; } // "active", "inactive" From 3e27cf9a99d182cad6e2eda2e07ac2ca1c786269 Mon Sep 17 00:00:00 2001 From: "B. G. M. Prabuddhi" Date: Sun, 27 Jul 2025 12:04:45 +0530 Subject: [PATCH 3/3] Create DTO and Update AdminController for User search endpoint --- Vpassbackend/Controllers/AdminController.cs | 40 +++++++++++++++++++++ Vpassbackend/DTOs/UserSearchDTO.cs | 10 ++++++ 2 files changed, 50 insertions(+) create mode 100644 Vpassbackend/DTOs/UserSearchDTO.cs diff --git a/Vpassbackend/Controllers/AdminController.cs b/Vpassbackend/Controllers/AdminController.cs index abb3d37..34862cc 100644 --- a/Vpassbackend/Controllers/AdminController.cs +++ b/Vpassbackend/Controllers/AdminController.cs @@ -109,5 +109,45 @@ public async Task GetUserById(int id) return Ok(user); } + [HttpPost("search")] + public async Task>> SearchUsers([FromBody] UserSearchDTO filter) + { + var q = _context.Users.Include(u => u.UserRole).AsQueryable(); + + if (!string.IsNullOrEmpty(filter.SearchTerm)) + { + var term = filter.SearchTerm.ToLower(); + q = q.Where(u => + u.FirstName.ToLower().Contains(term) || + u.LastName.ToLower().Contains(term) || + u.Email.ToLower().Contains(term) || + u.UserRole.UserRoleName.ToLower().Contains(term)); + } + + if (!string.IsNullOrEmpty(filter.Role) && filter.Role != "All Users") + { + q = q.Where(u => u.UserRole.UserRoleName == filter.Role); + } + + var total = await q.CountAsync(); + + var items = await q + .OrderBy(u => u.UserId) + .Skip((filter.PageNumber - 1) * filter.PageSize) + .Take(filter.PageSize) + .Select(u => new UserDto + { + UserId = u.UserId, + Email = u.Email, + Role = u.UserRole.UserRoleName + }) + .ToListAsync(); + + return Ok(new PaginatedResult + { + Data = items, + TotalCount = total + }); + } } } diff --git a/Vpassbackend/DTOs/UserSearchDTO.cs b/Vpassbackend/DTOs/UserSearchDTO.cs new file mode 100644 index 0000000..33104e5 --- /dev/null +++ b/Vpassbackend/DTOs/UserSearchDTO.cs @@ -0,0 +1,10 @@ +namespace Vpassbackend.DTOs +{ + public class UserSearchDTO + { + public string? SearchTerm { get; set; } + public string? Role { get; set; } + public int PageNumber { get; set; } = 1; + public int PageSize { get; set; } = 10; + } +}