From d7fb057e64fcc77e57c29fda5b24a6050b19acc6 Mon Sep 17 00:00:00 2001 From: Harkamal Randhawa Date: Thu, 5 Mar 2026 08:44:49 -0700 Subject: [PATCH] Match Postman API contract for desktop app - Change bulk delete from POST /bulk-delete to DELETE with body - Add search parameter support (q) to all list endpoints - Add customer registration endpoint - Add stores listing endpoint - Add analytics dashboard endpoint (admin only) - Update appointment availability to include storeId - Add CUSTOMER role to User entity - Implement search across all repositories --- .../controller/AdoptionController.java | 8 +- .../controller/AnalyticsController.java | 26 ++++ .../controller/AppointmentController.java | 20 ++- .../backend/controller/AuthController.java | 40 ++++- .../controller/CategoryController.java | 2 +- .../controller/InventoryController.java | 8 +- .../backend/controller/PetController.java | 2 +- .../backend/controller/ProductController.java | 2 +- .../controller/ProductSupplierController.java | 8 +- .../controller/PurchaseOrderController.java | 6 +- .../backend/controller/SaleController.java | 6 +- .../backend/controller/ServiceController.java | 2 +- .../backend/controller/StoreController.java | 26 ++++ .../controller/SupplierController.java | 2 +- .../backend/controller/UserController.java | 8 +- .../dto/analytics/DashboardResponse.java | 10 +- .../backend/dto/auth/RegisterRequest.java | 91 +++++++++++ .../backend/dto/store/StoreResponse.java | 76 ++++++++++ .../java/com/petshop/backend/entity/User.java | 2 +- .../repository/AdoptionRepository.java | 9 ++ .../repository/AppointmentRepository.java | 11 ++ .../repository/InventoryRepository.java | 7 + .../repository/ProductSupplierRepository.java | 9 ++ .../repository/PurchaseOrderRepository.java | 9 ++ .../backend/repository/SaleRepository.java | 10 ++ .../backend/repository/StoreRepository.java | 9 ++ .../backend/repository/UserRepository.java | 10 ++ .../backend/security/SecurityConfig.java | 2 +- .../backend/service/AdoptionService.java | 10 +- .../backend/service/AnalyticsService.java | 141 ++++++++++++++++++ .../backend/service/AppointmentService.java | 37 ++++- .../backend/service/InventoryService.java | 10 +- .../service/ProductSupplierService.java | 10 +- .../backend/service/PurchaseOrderService.java | 10 +- .../petshop/backend/service/SaleService.java | 10 +- .../petshop/backend/service/StoreService.java | 37 +++++ .../petshop/backend/service/UserService.java | 10 +- 37 files changed, 650 insertions(+), 46 deletions(-) create mode 100644 src/main/java/com/petshop/backend/controller/AnalyticsController.java create mode 100644 src/main/java/com/petshop/backend/controller/StoreController.java create mode 100644 src/main/java/com/petshop/backend/dto/auth/RegisterRequest.java create mode 100644 src/main/java/com/petshop/backend/dto/store/StoreResponse.java create mode 100644 src/main/java/com/petshop/backend/service/AnalyticsService.java create mode 100644 src/main/java/com/petshop/backend/service/StoreService.java diff --git a/src/main/java/com/petshop/backend/controller/AdoptionController.java b/src/main/java/com/petshop/backend/controller/AdoptionController.java index 8fbc4db3..30da7d5e 100644 --- a/src/main/java/com/petshop/backend/controller/AdoptionController.java +++ b/src/main/java/com/petshop/backend/controller/AdoptionController.java @@ -22,8 +22,10 @@ public class AdoptionController { } @GetMapping - public ResponseEntity> getAllAdoptions(Pageable pageable) { - return ResponseEntity.ok(adoptionService.getAllAdoptions(pageable)); + public ResponseEntity> getAllAdoptions( + @RequestParam(required = false) String q, + Pageable pageable) { + return ResponseEntity.ok(adoptionService.getAllAdoptions(q, pageable)); } @GetMapping("/{id}") @@ -49,7 +51,7 @@ public class AdoptionController { return ResponseEntity.noContent().build(); } - @PostMapping("/bulk-delete") + @DeleteMapping public ResponseEntity bulkDeleteAdoptions(@Valid @RequestBody BulkDeleteRequest request) { adoptionService.bulkDeleteAdoptions(request); return ResponseEntity.noContent().build(); diff --git a/src/main/java/com/petshop/backend/controller/AnalyticsController.java b/src/main/java/com/petshop/backend/controller/AnalyticsController.java new file mode 100644 index 00000000..6c7b9b9e --- /dev/null +++ b/src/main/java/com/petshop/backend/controller/AnalyticsController.java @@ -0,0 +1,26 @@ +package com.petshop.backend.controller; + +import com.petshop.backend.dto.analytics.DashboardResponse; +import com.petshop.backend.service.AnalyticsService; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/api/v1/analytics") +@PreAuthorize("hasRole('ADMIN')") +public class AnalyticsController { + + private final AnalyticsService analyticsService; + + public AnalyticsController(AnalyticsService analyticsService) { + this.analyticsService = analyticsService; + } + + @GetMapping("/dashboard") + public ResponseEntity getDashboard( + @RequestParam(defaultValue = "30") int days, + @RequestParam(defaultValue = "10") int top) { + return ResponseEntity.ok(analyticsService.getDashboardData(days, top)); + } +} diff --git a/src/main/java/com/petshop/backend/controller/AppointmentController.java b/src/main/java/com/petshop/backend/controller/AppointmentController.java index a08416b5..6c9f8fa4 100644 --- a/src/main/java/com/petshop/backend/controller/AppointmentController.java +++ b/src/main/java/com/petshop/backend/controller/AppointmentController.java @@ -11,6 +11,9 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import java.time.LocalDate; +import java.util.List; + @RestController @RequestMapping("/api/v1/appointments") public class AppointmentController { @@ -22,8 +25,10 @@ public class AppointmentController { } @GetMapping - public ResponseEntity> getAllAppointments(Pageable pageable) { - return ResponseEntity.ok(appointmentService.getAllAppointments(pageable)); + public ResponseEntity> getAllAppointments( + @RequestParam(required = false) String q, + Pageable pageable) { + return ResponseEntity.ok(appointmentService.getAllAppointments(q, pageable)); } @GetMapping("/{id}") @@ -49,9 +54,18 @@ public class AppointmentController { return ResponseEntity.noContent().build(); } - @PostMapping("/bulk-delete") + @DeleteMapping public ResponseEntity bulkDeleteAppointments(@Valid @RequestBody BulkDeleteRequest request) { appointmentService.bulkDeleteAppointments(request); return ResponseEntity.noContent().build(); } + + @GetMapping("/availability") + public ResponseEntity> checkAvailability( + @RequestParam Long storeId, + @RequestParam Long serviceId, + @RequestParam String date) { + LocalDate appointmentDate = LocalDate.parse(date); + return ResponseEntity.ok(appointmentService.checkAvailability(storeId, serviceId, appointmentDate)); + } } diff --git a/src/main/java/com/petshop/backend/controller/AuthController.java b/src/main/java/com/petshop/backend/controller/AuthController.java index 970d45c9..491c4833 100644 --- a/src/main/java/com/petshop/backend/controller/AuthController.java +++ b/src/main/java/com/petshop/backend/controller/AuthController.java @@ -2,6 +2,7 @@ package com.petshop.backend.controller; import com.petshop.backend.dto.auth.LoginRequest; import com.petshop.backend.dto.auth.LoginResponse; +import com.petshop.backend.dto.auth.RegisterRequest; import com.petshop.backend.dto.auth.UserInfoResponse; import com.petshop.backend.entity.User; import com.petshop.backend.repository.UserRepository; @@ -16,6 +17,7 @@ import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.web.bind.annotation.*; import java.util.HashMap; @@ -28,11 +30,47 @@ public class AuthController { private final AuthenticationManager authenticationManager; private final UserRepository userRepository; private final JwtUtil jwtUtil; + private final PasswordEncoder passwordEncoder; - public AuthController(AuthenticationManager authenticationManager, UserRepository userRepository, JwtUtil jwtUtil) { + public AuthController(AuthenticationManager authenticationManager, UserRepository userRepository, JwtUtil jwtUtil, PasswordEncoder passwordEncoder) { this.authenticationManager = authenticationManager; this.userRepository = userRepository; this.jwtUtil = jwtUtil; + this.passwordEncoder = passwordEncoder; + } + + @PostMapping("/register") + public ResponseEntity register(@Valid @RequestBody RegisterRequest request) { + if (userRepository.findByUsername(request.getEmail()).isPresent()) { + Map error = new HashMap<>(); + error.put("message", "Email already registered"); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error); + } + + User user = new User(); + user.setUsername(request.getEmail()); + user.setEmail(request.getEmail()); + user.setPassword(passwordEncoder.encode(request.getPassword())); + user.setFullName(request.getFirstName() + " " + request.getLastName()); + user.setRole(User.Role.CUSTOMER); + user.setActive(true); + + user = userRepository.save(user); + + UserDetails userDetails = new org.springframework.security.core.userdetails.User( + user.getUsername(), + user.getPassword(), + java.util.Collections.emptyList() + ); + + String token = jwtUtil.generateToken(userDetails); + + return ResponseEntity.status(HttpStatus.CREATED).body(new LoginResponse( + token, + user.getUsername(), + user.getFullName(), + user.getRole().name() + )); } @PostMapping("/login") diff --git a/src/main/java/com/petshop/backend/controller/CategoryController.java b/src/main/java/com/petshop/backend/controller/CategoryController.java index f03055b4..ddd7b934 100644 --- a/src/main/java/com/petshop/backend/controller/CategoryController.java +++ b/src/main/java/com/petshop/backend/controller/CategoryController.java @@ -51,7 +51,7 @@ public class CategoryController { return ResponseEntity.noContent().build(); } - @PostMapping("/bulk-delete") + @DeleteMapping public ResponseEntity bulkDeleteCategories(@Valid @RequestBody BulkDeleteRequest request) { categoryService.bulkDeleteCategories(request); return ResponseEntity.noContent().build(); diff --git a/src/main/java/com/petshop/backend/controller/InventoryController.java b/src/main/java/com/petshop/backend/controller/InventoryController.java index 44db2471..35ccbff9 100644 --- a/src/main/java/com/petshop/backend/controller/InventoryController.java +++ b/src/main/java/com/petshop/backend/controller/InventoryController.java @@ -24,8 +24,10 @@ public class InventoryController { } @GetMapping - public ResponseEntity> getAllInventory(Pageable pageable) { - return ResponseEntity.ok(inventoryService.getAllInventory(pageable)); + public ResponseEntity> getAllInventory( + @RequestParam(required = false) String q, + Pageable pageable) { + return ResponseEntity.ok(inventoryService.getAllInventory(q, pageable)); } @GetMapping("/{id}") @@ -51,7 +53,7 @@ public class InventoryController { return ResponseEntity.noContent().build(); } - @PostMapping("/bulk-delete") + @DeleteMapping public ResponseEntity bulkDeleteInventory(@Valid @RequestBody BulkDeleteRequest request) { inventoryService.bulkDeleteInventory(request); return ResponseEntity.noContent().build(); diff --git a/src/main/java/com/petshop/backend/controller/PetController.java b/src/main/java/com/petshop/backend/controller/PetController.java index 70c68e8f..7ae7ca64 100644 --- a/src/main/java/com/petshop/backend/controller/PetController.java +++ b/src/main/java/com/petshop/backend/controller/PetController.java @@ -51,7 +51,7 @@ public class PetController { return ResponseEntity.noContent().build(); } - @PostMapping("/bulk-delete") + @DeleteMapping public ResponseEntity bulkDeletePets(@Valid @RequestBody BulkDeleteRequest request) { petService.bulkDeletePets(request); return ResponseEntity.noContent().build(); diff --git a/src/main/java/com/petshop/backend/controller/ProductController.java b/src/main/java/com/petshop/backend/controller/ProductController.java index 12999b70..ada0e4dc 100644 --- a/src/main/java/com/petshop/backend/controller/ProductController.java +++ b/src/main/java/com/petshop/backend/controller/ProductController.java @@ -51,7 +51,7 @@ public class ProductController { return ResponseEntity.noContent().build(); } - @PostMapping("/bulk-delete") + @DeleteMapping public ResponseEntity bulkDeleteProducts(@Valid @RequestBody BulkDeleteRequest request) { productService.bulkDeleteProducts(request); return ResponseEntity.noContent().build(); diff --git a/src/main/java/com/petshop/backend/controller/ProductSupplierController.java b/src/main/java/com/petshop/backend/controller/ProductSupplierController.java index ea56c4ad..e6d78e28 100644 --- a/src/main/java/com/petshop/backend/controller/ProductSupplierController.java +++ b/src/main/java/com/petshop/backend/controller/ProductSupplierController.java @@ -24,8 +24,10 @@ public class ProductSupplierController { } @GetMapping - public ResponseEntity> getAllProductSuppliers(Pageable pageable) { - return ResponseEntity.ok(productSupplierService.getAllProductSuppliers(pageable)); + public ResponseEntity> getAllProductSuppliers( + @RequestParam(required = false) String q, + Pageable pageable) { + return ResponseEntity.ok(productSupplierService.getAllProductSuppliers(q, pageable)); } @GetMapping("/{productId}/{supplierId}") @@ -56,7 +58,7 @@ public class ProductSupplierController { return ResponseEntity.noContent().build(); } - @PostMapping("/bulk-delete") + @DeleteMapping public ResponseEntity bulkDeleteProductSuppliers(@Valid @RequestBody BulkDeleteProductSupplierRequest request) { productSupplierService.bulkDeleteProductSuppliers(request); return ResponseEntity.noContent().build(); diff --git a/src/main/java/com/petshop/backend/controller/PurchaseOrderController.java b/src/main/java/com/petshop/backend/controller/PurchaseOrderController.java index 5a3c214c..369f6995 100644 --- a/src/main/java/com/petshop/backend/controller/PurchaseOrderController.java +++ b/src/main/java/com/petshop/backend/controller/PurchaseOrderController.java @@ -20,8 +20,10 @@ public class PurchaseOrderController { } @GetMapping - public ResponseEntity> getAllPurchaseOrders(Pageable pageable) { - return ResponseEntity.ok(purchaseOrderService.getAllPurchaseOrders(pageable)); + public ResponseEntity> getAllPurchaseOrders( + @RequestParam(required = false) String q, + Pageable pageable) { + return ResponseEntity.ok(purchaseOrderService.getAllPurchaseOrders(q, pageable)); } @GetMapping("/{id}") diff --git a/src/main/java/com/petshop/backend/controller/SaleController.java b/src/main/java/com/petshop/backend/controller/SaleController.java index 0618c317..aae791fe 100644 --- a/src/main/java/com/petshop/backend/controller/SaleController.java +++ b/src/main/java/com/petshop/backend/controller/SaleController.java @@ -21,8 +21,10 @@ public class SaleController { } @GetMapping - public ResponseEntity> getAllSales(Pageable pageable) { - return ResponseEntity.ok(saleService.getAllSales(pageable)); + public ResponseEntity> getAllSales( + @RequestParam(required = false) String q, + Pageable pageable) { + return ResponseEntity.ok(saleService.getAllSales(q, pageable)); } @GetMapping("/{id}") diff --git a/src/main/java/com/petshop/backend/controller/ServiceController.java b/src/main/java/com/petshop/backend/controller/ServiceController.java index f9167a0b..53bb5343 100644 --- a/src/main/java/com/petshop/backend/controller/ServiceController.java +++ b/src/main/java/com/petshop/backend/controller/ServiceController.java @@ -51,7 +51,7 @@ public class ServiceController { return ResponseEntity.noContent().build(); } - @PostMapping("/bulk-delete") + @DeleteMapping public ResponseEntity bulkDeleteServices(@Valid @RequestBody BulkDeleteRequest request) { serviceService.bulkDeleteServices(request); return ResponseEntity.noContent().build(); diff --git a/src/main/java/com/petshop/backend/controller/StoreController.java b/src/main/java/com/petshop/backend/controller/StoreController.java new file mode 100644 index 00000000..4fa716be --- /dev/null +++ b/src/main/java/com/petshop/backend/controller/StoreController.java @@ -0,0 +1,26 @@ +package com.petshop.backend.controller; + +import com.petshop.backend.dto.store.StoreResponse; +import com.petshop.backend.service.StoreService; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/api/v1/stores") +public class StoreController { + + private final StoreService storeService; + + public StoreController(StoreService storeService) { + this.storeService = storeService; + } + + @GetMapping + public ResponseEntity> getAllStores( + @RequestParam(required = false) String q, + Pageable pageable) { + return ResponseEntity.ok(storeService.getAllStores(q, pageable)); + } +} diff --git a/src/main/java/com/petshop/backend/controller/SupplierController.java b/src/main/java/com/petshop/backend/controller/SupplierController.java index 255f01dc..eb5c065d 100644 --- a/src/main/java/com/petshop/backend/controller/SupplierController.java +++ b/src/main/java/com/petshop/backend/controller/SupplierController.java @@ -53,7 +53,7 @@ public class SupplierController { return ResponseEntity.noContent().build(); } - @PostMapping("/bulk-delete") + @DeleteMapping public ResponseEntity bulkDeleteSuppliers(@Valid @RequestBody BulkDeleteRequest request) { supplierService.bulkDeleteSuppliers(request); return ResponseEntity.noContent().build(); diff --git a/src/main/java/com/petshop/backend/controller/UserController.java b/src/main/java/com/petshop/backend/controller/UserController.java index a43f01f8..b1ece730 100644 --- a/src/main/java/com/petshop/backend/controller/UserController.java +++ b/src/main/java/com/petshop/backend/controller/UserController.java @@ -24,8 +24,10 @@ public class UserController { } @GetMapping - public ResponseEntity> getAllUsers(Pageable pageable) { - return ResponseEntity.ok(userService.getAllUsers(pageable)); + public ResponseEntity> getAllUsers( + @RequestParam(required = false) String q, + Pageable pageable) { + return ResponseEntity.ok(userService.getAllUsers(q, pageable)); } @GetMapping("/{id}") @@ -51,7 +53,7 @@ public class UserController { return ResponseEntity.noContent().build(); } - @PostMapping("/bulk-delete") + @DeleteMapping public ResponseEntity bulkDeleteUsers(@Valid @RequestBody BulkDeleteRequest request) { userService.bulkDeleteUsers(request); return ResponseEntity.noContent().build(); diff --git a/src/main/java/com/petshop/backend/dto/analytics/DashboardResponse.java b/src/main/java/com/petshop/backend/dto/analytics/DashboardResponse.java index 263d3fae..c884d24c 100644 --- a/src/main/java/com/petshop/backend/dto/analytics/DashboardResponse.java +++ b/src/main/java/com/petshop/backend/dto/analytics/DashboardResponse.java @@ -74,9 +74,8 @@ public class DashboardResponse { ", dailySales=" + dailySales + '}'; } -} -class SalesSummary { + public static class SalesSummary { private BigDecimal totalRevenue; private Long totalSales; private BigDecimal totalRefunds; @@ -148,7 +147,7 @@ class SalesSummary { } } -class InventorySummary { + public static class InventorySummary { private Long totalProducts; private Long lowStockProducts; private Long outOfStockProducts; @@ -209,7 +208,7 @@ class InventorySummary { } } -class TopProduct { + public static class TopProduct { private Long productId; private String productName; private Long quantitySold; @@ -281,7 +280,7 @@ class TopProduct { } } -class DailySales { + public static class DailySales { private String date; private BigDecimal revenue; private Long salesCount; @@ -341,3 +340,4 @@ class DailySales { '}'; } } +} diff --git a/src/main/java/com/petshop/backend/dto/auth/RegisterRequest.java b/src/main/java/com/petshop/backend/dto/auth/RegisterRequest.java new file mode 100644 index 00000000..ae3f5a56 --- /dev/null +++ b/src/main/java/com/petshop/backend/dto/auth/RegisterRequest.java @@ -0,0 +1,91 @@ +package com.petshop.backend.dto.auth; + +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import java.util.Objects; + +public class RegisterRequest { + @NotBlank(message = "First name is required") + private String firstName; + + @NotBlank(message = "Last name is required") + private String lastName; + + @NotBlank(message = "Email is required") + @Email(message = "Email must be valid") + private String email; + + @NotBlank(message = "Phone is required") + private String phone; + + @NotBlank(message = "Password is required") + private String password; + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + RegisterRequest that = (RegisterRequest) o; + return Objects.equals(firstName, that.firstName) && + Objects.equals(lastName, that.lastName) && + Objects.equals(email, that.email) && + Objects.equals(phone, that.phone) && + Objects.equals(password, that.password); + } + + @Override + public int hashCode() { + return Objects.hash(firstName, lastName, email, phone, password); + } + + @Override + public String toString() { + return "RegisterRequest{" + + "firstName='" + firstName + '\'' + + ", lastName='" + lastName + '\'' + + ", email='" + email + '\'' + + ", phone='" + phone + '\'' + + ", password='[PROTECTED]'" + + '}'; + } +} diff --git a/src/main/java/com/petshop/backend/dto/store/StoreResponse.java b/src/main/java/com/petshop/backend/dto/store/StoreResponse.java new file mode 100644 index 00000000..6af1e702 --- /dev/null +++ b/src/main/java/com/petshop/backend/dto/store/StoreResponse.java @@ -0,0 +1,76 @@ +package com.petshop.backend.dto.store; + +import java.time.LocalDateTime; +import java.util.Objects; + +public class StoreResponse { + private Long id; + private String storeName; + private String storeLocation; + private LocalDateTime createdAt; + + public StoreResponse() { + } + + public StoreResponse(Long id, String storeName, String storeLocation, LocalDateTime createdAt) { + this.id = id; + this.storeName = storeName; + this.storeLocation = storeLocation; + this.createdAt = createdAt; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getStoreName() { + return storeName; + } + + public void setStoreName(String storeName) { + this.storeName = storeName; + } + + public String getStoreLocation() { + return storeLocation; + } + + public void setStoreLocation(String storeLocation) { + this.storeLocation = storeLocation; + } + + public LocalDateTime getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(LocalDateTime createdAt) { + this.createdAt = createdAt; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + StoreResponse that = (StoreResponse) o; + return Objects.equals(id, that.id) && Objects.equals(storeName, that.storeName) && Objects.equals(storeLocation, that.storeLocation) && Objects.equals(createdAt, that.createdAt); + } + + @Override + public int hashCode() { + return Objects.hash(id, storeName, storeLocation, createdAt); + } + + @Override + public String toString() { + return "StoreResponse{" + + "id=" + id + + ", storeName='" + storeName + '\'' + + ", storeLocation='" + storeLocation + '\'' + + ", createdAt=" + createdAt + + '}'; + } +} diff --git a/src/main/java/com/petshop/backend/entity/User.java b/src/main/java/com/petshop/backend/entity/User.java index 7d6901a8..729e782a 100644 --- a/src/main/java/com/petshop/backend/entity/User.java +++ b/src/main/java/com/petshop/backend/entity/User.java @@ -43,7 +43,7 @@ public class User { private LocalDateTime updatedAt; public enum Role { - STAFF, ADMIN + STAFF, ADMIN, CUSTOMER } public User() { diff --git a/src/main/java/com/petshop/backend/repository/AdoptionRepository.java b/src/main/java/com/petshop/backend/repository/AdoptionRepository.java index 8bf64f74..92bf2cb2 100644 --- a/src/main/java/com/petshop/backend/repository/AdoptionRepository.java +++ b/src/main/java/com/petshop/backend/repository/AdoptionRepository.java @@ -1,9 +1,18 @@ package com.petshop.backend.repository; import com.petshop.backend.entity.Adoption; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; @Repository public interface AdoptionRepository extends JpaRepository { + + @Query("SELECT a FROM Adoption a WHERE " + + "LOWER(a.customer.customerName) LIKE LOWER(CONCAT('%', :q, '%')) OR " + + "LOWER(a.pet.petName) LIKE LOWER(CONCAT('%', :q, '%'))") + Page searchAdoptions(@Param("q") String query, Pageable pageable); } diff --git a/src/main/java/com/petshop/backend/repository/AppointmentRepository.java b/src/main/java/com/petshop/backend/repository/AppointmentRepository.java index 5d773e8c..4040d08c 100644 --- a/src/main/java/com/petshop/backend/repository/AppointmentRepository.java +++ b/src/main/java/com/petshop/backend/repository/AppointmentRepository.java @@ -1,6 +1,8 @@ package com.petshop.backend.repository; import com.petshop.backend.entity.Appointment; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; @@ -15,4 +17,13 @@ public interface AppointmentRepository extends JpaRepository @Query("SELECT a FROM Appointment a WHERE a.appointmentDate = :date AND a.appointmentTime = :time") List findByDateAndTime(@Param("date") LocalDate date, @Param("time") LocalTime time); + + @Query("SELECT a FROM Appointment a WHERE a.service.id = :serviceId AND a.appointmentDate = :date AND a.status != 'Cancelled'") + List findByServiceAndDate(@Param("serviceId") Long serviceId, @Param("date") LocalDate date); + + @Query("SELECT DISTINCT a FROM Appointment a LEFT JOIN a.pets p WHERE " + + "LOWER(a.customer.customerName) LIKE LOWER(CONCAT('%', :q, '%')) OR " + + "LOWER(a.service.serviceName) LIKE LOWER(CONCAT('%', :q, '%')) OR " + + "LOWER(p.petName) LIKE LOWER(CONCAT('%', :q, '%'))") + Page searchAppointments(@Param("q") String query, Pageable pageable); } diff --git a/src/main/java/com/petshop/backend/repository/InventoryRepository.java b/src/main/java/com/petshop/backend/repository/InventoryRepository.java index 2d0e0f9c..e7e4d673 100644 --- a/src/main/java/com/petshop/backend/repository/InventoryRepository.java +++ b/src/main/java/com/petshop/backend/repository/InventoryRepository.java @@ -1,6 +1,8 @@ package com.petshop.backend.repository; import com.petshop.backend.entity.Inventory; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; @@ -13,4 +15,9 @@ public interface InventoryRepository extends JpaRepository { @Query("SELECT i FROM Inventory i WHERE i.product.id = :productId AND i.store.id = :storeId") Optional findByProductIdAndStoreId(@Param("productId") Long productId, @Param("storeId") Long storeId); + + @Query("SELECT i FROM Inventory i WHERE " + + "LOWER(i.product.productName) LIKE LOWER(CONCAT('%', :q, '%')) OR " + + "LOWER(i.store.storeName) LIKE LOWER(CONCAT('%', :q, '%'))") + Page searchInventory(@Param("q") String query, Pageable pageable); } diff --git a/src/main/java/com/petshop/backend/repository/ProductSupplierRepository.java b/src/main/java/com/petshop/backend/repository/ProductSupplierRepository.java index f8e96185..8b5e8720 100644 --- a/src/main/java/com/petshop/backend/repository/ProductSupplierRepository.java +++ b/src/main/java/com/petshop/backend/repository/ProductSupplierRepository.java @@ -1,9 +1,18 @@ package com.petshop.backend.repository; import com.petshop.backend.entity.ProductSupplier; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; @Repository public interface ProductSupplierRepository extends JpaRepository { + + @Query("SELECT ps FROM ProductSupplier ps WHERE " + + "LOWER(ps.product.productName) LIKE LOWER(CONCAT('%', :q, '%')) OR " + + "LOWER(ps.supplier.supplierName) LIKE LOWER(CONCAT('%', :q, '%'))") + Page searchProductSuppliers(@Param("q") String query, Pageable pageable); } diff --git a/src/main/java/com/petshop/backend/repository/PurchaseOrderRepository.java b/src/main/java/com/petshop/backend/repository/PurchaseOrderRepository.java index ed3251ac..e6087bef 100644 --- a/src/main/java/com/petshop/backend/repository/PurchaseOrderRepository.java +++ b/src/main/java/com/petshop/backend/repository/PurchaseOrderRepository.java @@ -1,9 +1,18 @@ package com.petshop.backend.repository; import com.petshop.backend.entity.PurchaseOrder; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; @Repository public interface PurchaseOrderRepository extends JpaRepository { + + @Query("SELECT po FROM PurchaseOrder po WHERE " + + "LOWER(po.supplier.supplierName) LIKE LOWER(CONCAT('%', :q, '%')) OR " + + "LOWER(po.notes) LIKE LOWER(CONCAT('%', :q, '%'))") + Page searchPurchaseOrders(@Param("q") String query, Pageable pageable); } diff --git a/src/main/java/com/petshop/backend/repository/SaleRepository.java b/src/main/java/com/petshop/backend/repository/SaleRepository.java index f58f0660..9d5392a5 100644 --- a/src/main/java/com/petshop/backend/repository/SaleRepository.java +++ b/src/main/java/com/petshop/backend/repository/SaleRepository.java @@ -1,9 +1,19 @@ package com.petshop.backend.repository; import com.petshop.backend.entity.Sale; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; @Repository public interface SaleRepository extends JpaRepository { + + @Query("SELECT s FROM Sale s WHERE " + + "LOWER(s.customer.customerName) LIKE LOWER(CONCAT('%', :q, '%')) OR " + + "LOWER(s.employee.fullName) LIKE LOWER(CONCAT('%', :q, '%')) OR " + + "LOWER(s.store.storeName) LIKE LOWER(CONCAT('%', :q, '%'))") + Page searchSales(@Param("q") String query, Pageable pageable); } diff --git a/src/main/java/com/petshop/backend/repository/StoreRepository.java b/src/main/java/com/petshop/backend/repository/StoreRepository.java index 4f067633..0c855389 100644 --- a/src/main/java/com/petshop/backend/repository/StoreRepository.java +++ b/src/main/java/com/petshop/backend/repository/StoreRepository.java @@ -1,9 +1,18 @@ package com.petshop.backend.repository; import com.petshop.backend.entity.Store; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; @Repository public interface StoreRepository extends JpaRepository { + + @Query("SELECT s FROM Store s WHERE " + + "LOWER(s.storeName) LIKE LOWER(CONCAT('%', :q, '%')) OR " + + "LOWER(s.storeLocation) LIKE LOWER(CONCAT('%', :q, '%'))") + Page searchStores(@Param("q") String query, Pageable pageable); } diff --git a/src/main/java/com/petshop/backend/repository/UserRepository.java b/src/main/java/com/petshop/backend/repository/UserRepository.java index 95b79227..486d8f1a 100644 --- a/src/main/java/com/petshop/backend/repository/UserRepository.java +++ b/src/main/java/com/petshop/backend/repository/UserRepository.java @@ -1,7 +1,11 @@ package com.petshop.backend.repository; import com.petshop.backend.entity.User; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import java.util.Optional; @@ -10,4 +14,10 @@ import java.util.Optional; public interface UserRepository extends JpaRepository { Optional findByUsername(String username); boolean existsByUsername(String username); + + @Query("SELECT u FROM User u WHERE " + + "LOWER(u.username) LIKE LOWER(CONCAT('%', :q, '%')) OR " + + "LOWER(u.fullName) LIKE LOWER(CONCAT('%', :q, '%')) OR " + + "LOWER(u.email) LIKE LOWER(CONCAT('%', :q, '%'))") + Page searchUsers(@Param("q") String query, Pageable pageable); } diff --git a/src/main/java/com/petshop/backend/security/SecurityConfig.java b/src/main/java/com/petshop/backend/security/SecurityConfig.java index dadf9d93..e1ce42c4 100644 --- a/src/main/java/com/petshop/backend/security/SecurityConfig.java +++ b/src/main/java/com/petshop/backend/security/SecurityConfig.java @@ -36,7 +36,7 @@ public class SecurityConfig { http .csrf(AbstractHttpConfigurer::disable) .authorizeHttpRequests(auth -> auth - .requestMatchers("/api/v1/auth/login").permitAll() + .requestMatchers("/api/v1/auth/login", "/api/v1/auth/register").permitAll() .requestMatchers("/swagger-ui/**", "/v3/api-docs/**", "/swagger-ui.html").permitAll() .requestMatchers(HttpMethod.GET, "/api/v1/dropdowns/suppliers").hasRole("ADMIN") .requestMatchers("/api/v1/inventory/**").hasRole("ADMIN") diff --git a/src/main/java/com/petshop/backend/service/AdoptionService.java b/src/main/java/com/petshop/backend/service/AdoptionService.java index 021e058a..d5ecc890 100644 --- a/src/main/java/com/petshop/backend/service/AdoptionService.java +++ b/src/main/java/com/petshop/backend/service/AdoptionService.java @@ -28,8 +28,14 @@ public class AdoptionService { this.customerRepository = customerRepository; } - public Page getAllAdoptions(Pageable pageable) { - return adoptionRepository.findAll(pageable).map(this::mapToResponse); + public Page getAllAdoptions(String query, Pageable pageable) { + Page adoptions; + if (query != null && !query.trim().isEmpty()) { + adoptions = adoptionRepository.searchAdoptions(query, pageable); + } else { + adoptions = adoptionRepository.findAll(pageable); + } + return adoptions.map(this::mapToResponse); } public AdoptionResponse getAdoptionById(Long id) { diff --git a/src/main/java/com/petshop/backend/service/AnalyticsService.java b/src/main/java/com/petshop/backend/service/AnalyticsService.java new file mode 100644 index 00000000..47e27d17 --- /dev/null +++ b/src/main/java/com/petshop/backend/service/AnalyticsService.java @@ -0,0 +1,141 @@ +package com.petshop.backend.service; + +import com.petshop.backend.dto.analytics.DashboardResponse; +import com.petshop.backend.entity.Inventory; +import com.petshop.backend.entity.Product; +import com.petshop.backend.entity.Refund; +import com.petshop.backend.entity.Sale; +import com.petshop.backend.repository.InventoryRepository; +import com.petshop.backend.repository.ProductRepository; +import com.petshop.backend.repository.RefundRepository; +import com.petshop.backend.repository.SaleRepository; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.stream.Collectors; + +@Service +public class AnalyticsService { + + private final SaleRepository saleRepository; + private final RefundRepository refundRepository; + private final InventoryRepository inventoryRepository; + private final ProductRepository productRepository; + + public AnalyticsService(SaleRepository saleRepository, RefundRepository refundRepository, + InventoryRepository inventoryRepository, ProductRepository productRepository) { + this.saleRepository = saleRepository; + this.refundRepository = refundRepository; + this.inventoryRepository = inventoryRepository; + this.productRepository = productRepository; + } + + public DashboardResponse getDashboardData(int days, int top) { + LocalDateTime startDate = LocalDateTime.now().minusDays(days); + + List sales = saleRepository.findAll().stream() + .filter(sale -> sale.getSaleDate().isAfter(startDate)) + .collect(Collectors.toList()); + + List refunds = refundRepository.findAll().stream() + .filter(refund -> refund.getRefundDate().isAfter(startDate)) + .collect(Collectors.toList()); + + DashboardResponse.SalesSummary salesSummary = calculateSalesSummary(sales, refunds); + DashboardResponse.InventorySummary inventorySummary = calculateInventorySummary(); + List topProducts = calculateTopProducts(sales, top); + List dailySales = calculateDailySales(sales, days); + + return new DashboardResponse(salesSummary, inventorySummary, topProducts, dailySales); + } + + private DashboardResponse.SalesSummary calculateSalesSummary(List sales, List refunds) { + BigDecimal totalRevenue = sales.stream() + .map(Sale::getTotal) + .reduce(BigDecimal.ZERO, BigDecimal::add); + + Long totalSales = (long) sales.size(); + + BigDecimal totalRefunds = refunds.stream() + .map(Refund::getRefundAmount) + .reduce(BigDecimal.ZERO, BigDecimal::add); + + Long totalRefundCount = (long) refunds.size(); + + return new DashboardResponse.SalesSummary(totalRevenue, totalSales, totalRefunds, totalRefundCount); + } + + private DashboardResponse.InventorySummary calculateInventorySummary() { + List allInventory = inventoryRepository.findAll(); + + Long totalProducts = productRepository.count(); + + Long lowStockProducts = allInventory.stream() + .filter(inv -> inv.getQuantity() > 0 && inv.getQuantity() <= inv.getReorderLevel()) + .map(inv -> inv.getProduct().getId()) + .distinct() + .count(); + + Long outOfStockProducts = allInventory.stream() + .filter(inv -> inv.getQuantity() == 0) + .map(inv -> inv.getProduct().getId()) + .distinct() + .count(); + + return new DashboardResponse.InventorySummary(totalProducts, lowStockProducts, outOfStockProducts); + } + + private List calculateTopProducts(List sales, int top) { + Map productSalesMap = new HashMap<>(); + + for (Sale sale : sales) { + for (var item : sale.getItems()) { + Long productId = item.getProduct().getId(); + String productName = item.getProduct().getProductName(); + Long quantitySold = Long.valueOf(item.getQuantity()); + BigDecimal revenue = item.getSubtotal(); + + productSalesMap.compute(productId, (key, existing) -> { + if (existing == null) { + return new DashboardResponse.TopProduct(productId, productName, quantitySold, revenue); + } else { + existing.setQuantitySold(existing.getQuantitySold() + quantitySold); + existing.setRevenue(existing.getRevenue().add(revenue)); + return existing; + } + }); + } + } + + return productSalesMap.values().stream() + .sorted((p1, p2) -> p2.getRevenue().compareTo(p1.getRevenue())) + .limit(top) + .collect(Collectors.toList()); + } + + private List calculateDailySales(List sales, int days) { + Map dailySalesMap = new LinkedHashMap<>(); + + LocalDate startDate = LocalDate.now().minusDays(days - 1); + for (int i = 0; i < days; i++) { + LocalDate date = startDate.plusDays(i); + String dateStr = date.format(DateTimeFormatter.ISO_LOCAL_DATE); + dailySalesMap.put(date, new DashboardResponse.DailySales(dateStr, BigDecimal.ZERO, 0L)); + } + + for (Sale sale : sales) { + LocalDate saleDate = sale.getSaleDate().toLocalDate(); + if (dailySalesMap.containsKey(saleDate)) { + DashboardResponse.DailySales dailySale = dailySalesMap.get(saleDate); + dailySale.setRevenue(dailySale.getRevenue().add(sale.getTotal())); + dailySale.setSalesCount(dailySale.getSalesCount() + 1); + } + } + + return new ArrayList<>(dailySalesMap.values()); + } +} diff --git a/src/main/java/com/petshop/backend/service/AppointmentService.java b/src/main/java/com/petshop/backend/service/AppointmentService.java index 5244c5f1..8cc52b2c 100644 --- a/src/main/java/com/petshop/backend/service/AppointmentService.java +++ b/src/main/java/com/petshop/backend/service/AppointmentService.java @@ -16,6 +16,9 @@ import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.time.LocalDate; +import java.time.LocalTime; +import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -36,8 +39,14 @@ public class AppointmentService { this.petRepository = petRepository; } - public Page getAllAppointments(Pageable pageable) { - return appointmentRepository.findAll(pageable).map(this::mapToResponse); + public Page getAllAppointments(String query, Pageable pageable) { + Page appointments; + if (query != null && !query.trim().isEmpty()) { + appointments = appointmentRepository.searchAppointments(query, pageable); + } else { + appointments = appointmentRepository.findAll(pageable); + } + return appointments.map(this::mapToResponse); } public AppointmentResponse getAppointmentById(Long id) { @@ -107,6 +116,30 @@ public class AppointmentService { appointmentRepository.deleteAllById(request.getIds()); } + public List checkAvailability(Long storeId, Long serviceId, LocalDate date) { + com.petshop.backend.entity.Service service = serviceRepository.findById(serviceId) + .orElseThrow(() -> new ResourceNotFoundException("Service not found with id: " + serviceId)); + + List existingAppointments = appointmentRepository.findByServiceAndDate(serviceId, date); + Set bookedTimes = existingAppointments.stream() + .map(Appointment::getAppointmentTime) + .collect(Collectors.toSet()); + + List availableSlots = new ArrayList<>(); + LocalTime startTime = LocalTime.of(9, 0); + LocalTime endTime = LocalTime.of(17, 0); + + LocalTime currentTime = startTime; + while (currentTime.isBefore(endTime)) { + if (!bookedTimes.contains(currentTime)) { + availableSlots.add(currentTime.toString()); + } + currentTime = currentTime.plusMinutes(30); + } + + return availableSlots; + } + private Set fetchPets(List petIds) { Set pets = new HashSet<>(); for (Long petId : petIds) { diff --git a/src/main/java/com/petshop/backend/service/InventoryService.java b/src/main/java/com/petshop/backend/service/InventoryService.java index 4aa368a2..78509765 100644 --- a/src/main/java/com/petshop/backend/service/InventoryService.java +++ b/src/main/java/com/petshop/backend/service/InventoryService.java @@ -30,8 +30,14 @@ public class InventoryService { this.storeRepository = storeRepository; } - public Page getAllInventory(Pageable pageable) { - return inventoryRepository.findAll(pageable).map(this::mapToResponse); + public Page getAllInventory(String query, Pageable pageable) { + Page inventory; + if (query != null && !query.trim().isEmpty()) { + inventory = inventoryRepository.searchInventory(query, pageable); + } else { + inventory = inventoryRepository.findAll(pageable); + } + return inventory.map(this::mapToResponse); } public InventoryResponse getInventoryById(Long id) { diff --git a/src/main/java/com/petshop/backend/service/ProductSupplierService.java b/src/main/java/com/petshop/backend/service/ProductSupplierService.java index 3b87ef61..2e60b3e4 100644 --- a/src/main/java/com/petshop/backend/service/ProductSupplierService.java +++ b/src/main/java/com/petshop/backend/service/ProductSupplierService.java @@ -28,8 +28,14 @@ public class ProductSupplierService { this.supplierRepository = supplierRepository; } - public Page getAllProductSuppliers(Pageable pageable) { - return productSupplierRepository.findAll(pageable).map(this::mapToResponse); + public Page getAllProductSuppliers(String query, Pageable pageable) { + Page productSuppliers; + if (query != null && !query.trim().isEmpty()) { + productSuppliers = productSupplierRepository.searchProductSuppliers(query, pageable); + } else { + productSuppliers = productSupplierRepository.findAll(pageable); + } + return productSuppliers.map(this::mapToResponse); } public ProductSupplierResponse getProductSupplierById(Long productId, Long supplierId) { diff --git a/src/main/java/com/petshop/backend/service/PurchaseOrderService.java b/src/main/java/com/petshop/backend/service/PurchaseOrderService.java index d87b2f6f..efda04d6 100644 --- a/src/main/java/com/petshop/backend/service/PurchaseOrderService.java +++ b/src/main/java/com/petshop/backend/service/PurchaseOrderService.java @@ -22,8 +22,14 @@ public class PurchaseOrderService { this.purchaseOrderRepository = purchaseOrderRepository; } - public Page getAllPurchaseOrders(Pageable pageable) { - return purchaseOrderRepository.findAll(pageable).map(this::mapToResponse); + public Page getAllPurchaseOrders(String query, Pageable pageable) { + Page purchaseOrders; + if (query != null && !query.trim().isEmpty()) { + purchaseOrders = purchaseOrderRepository.searchPurchaseOrders(query, pageable); + } else { + purchaseOrders = purchaseOrderRepository.findAll(pageable); + } + return purchaseOrders.map(this::mapToResponse); } public PurchaseOrderResponse getPurchaseOrderById(Long id) { diff --git a/src/main/java/com/petshop/backend/service/SaleService.java b/src/main/java/com/petshop/backend/service/SaleService.java index b5db6959..698a6c66 100644 --- a/src/main/java/com/petshop/backend/service/SaleService.java +++ b/src/main/java/com/petshop/backend/service/SaleService.java @@ -36,8 +36,14 @@ public class SaleService { this.userRepository = userRepository; } - public Page getAllSales(Pageable pageable) { - return saleRepository.findAll(pageable).map(this::mapToResponse); + public Page getAllSales(String query, Pageable pageable) { + Page sales; + if (query != null && !query.trim().isEmpty()) { + sales = saleRepository.searchSales(query, pageable); + } else { + sales = saleRepository.findAll(pageable); + } + return sales.map(this::mapToResponse); } public SaleResponse getSaleById(Long id) { diff --git a/src/main/java/com/petshop/backend/service/StoreService.java b/src/main/java/com/petshop/backend/service/StoreService.java new file mode 100644 index 00000000..79ab3c47 --- /dev/null +++ b/src/main/java/com/petshop/backend/service/StoreService.java @@ -0,0 +1,37 @@ +package com.petshop.backend.service; + +import com.petshop.backend.dto.store.StoreResponse; +import com.petshop.backend.entity.Store; +import com.petshop.backend.repository.StoreRepository; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; + +@Service +public class StoreService { + + private final StoreRepository storeRepository; + + public StoreService(StoreRepository storeRepository) { + this.storeRepository = storeRepository; + } + + public Page getAllStores(String query, Pageable pageable) { + Page stores; + if (query != null && !query.trim().isEmpty()) { + stores = storeRepository.searchStores(query, pageable); + } else { + stores = storeRepository.findAll(pageable); + } + return stores.map(this::mapToResponse); + } + + private StoreResponse mapToResponse(Store store) { + return new StoreResponse( + store.getId(), + store.getStoreName(), + store.getStoreLocation(), + store.getCreatedAt() + ); + } +} diff --git a/src/main/java/com/petshop/backend/service/UserService.java b/src/main/java/com/petshop/backend/service/UserService.java index 1183ea2f..e1317aa7 100644 --- a/src/main/java/com/petshop/backend/service/UserService.java +++ b/src/main/java/com/petshop/backend/service/UserService.java @@ -23,8 +23,14 @@ public class UserService { this.passwordEncoder = passwordEncoder; } - public Page getAllUsers(Pageable pageable) { - return userRepository.findAll(pageable).map(this::mapToResponse); + public Page getAllUsers(String query, Pageable pageable) { + Page users; + if (query != null && !query.trim().isEmpty()) { + users = userRepository.searchUsers(query, pageable); + } else { + users = userRepository.findAll(pageable); + } + return users.map(this::mapToResponse); } public UserResponse getUserById(Long id) {