Merge pull request #56 from RecentRunner/expand-pets-products-data
Expand catalog
This commit was merged in pull request #56.
This commit is contained in:
@@ -33,6 +33,7 @@ public class DevStackApplication {
|
|||||||
docker.ensureDockerAvailable();
|
docker.ensureDockerAvailable();
|
||||||
docker.startDatabase();
|
docker.startDatabase();
|
||||||
context = new SpringApplicationBuilder(BackendApplication.class)
|
context = new SpringApplicationBuilder(BackendApplication.class)
|
||||||
|
.profiles("local")
|
||||||
.initializers(new FlywayContextInitializer())
|
.initializers(new FlywayContextInitializer())
|
||||||
.run(args);
|
.run(args);
|
||||||
context.addApplicationListener(event -> {
|
context.addApplicationListener(event -> {
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package com.petshop.backend.config;
|
||||||
|
|
||||||
|
import com.petshop.backend.repository.PetRepository;
|
||||||
|
import com.petshop.backend.repository.ProductRepository;
|
||||||
|
import org.springframework.boot.CommandLineRunner;
|
||||||
|
import org.springframework.context.annotation.Profile;
|
||||||
|
import org.springframework.core.io.ClassPathResource;
|
||||||
|
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.sql.DataSource;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@Profile("local")
|
||||||
|
public class LocalCatalogSeedInitializer implements CommandLineRunner {
|
||||||
|
|
||||||
|
private final DataSource dataSource;
|
||||||
|
private final PetRepository petRepository;
|
||||||
|
private final ProductRepository productRepository;
|
||||||
|
|
||||||
|
public LocalCatalogSeedInitializer(DataSource dataSource, PetRepository petRepository, ProductRepository productRepository) {
|
||||||
|
this.dataSource = dataSource;
|
||||||
|
this.petRepository = petRepository;
|
||||||
|
this.productRepository = productRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(String... args) {
|
||||||
|
if (petRepository.count() > 6 || productRepository.count() > 6) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResourceDatabasePopulator populator = new ResourceDatabasePopulator(false, false, "UTF-8",
|
||||||
|
new ClassPathResource("dev/expand_pet_product_seed.sql"));
|
||||||
|
populator.execute(dataSource);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,8 +25,9 @@ public class CategoryController {
|
|||||||
@GetMapping
|
@GetMapping
|
||||||
public ResponseEntity<Page<CategoryResponse>> getAllCategories(
|
public ResponseEntity<Page<CategoryResponse>> getAllCategories(
|
||||||
@RequestParam(required = false) String q,
|
@RequestParam(required = false) String q,
|
||||||
|
@RequestParam(required = false) String type,
|
||||||
Pageable pageable) {
|
Pageable pageable) {
|
||||||
return ResponseEntity.ok(categoryService.getAllCategories(q, pageable));
|
return ResponseEntity.ok(categoryService.getAllCategories(q, type, pageable));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/{id}")
|
@GetMapping("/{id}")
|
||||||
|
|||||||
@@ -82,6 +82,29 @@ public class DropdownController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/product-categories")
|
||||||
|
public ResponseEntity<List<DropdownOption>> getProductCategories() {
|
||||||
|
return ResponseEntity.ok(
|
||||||
|
categoryRepository.findAll().stream()
|
||||||
|
.filter(c -> "product".equalsIgnoreCase(c.getCategoryType()))
|
||||||
|
.map(c -> new DropdownOption(c.getCategoryId(), c.getCategoryName()))
|
||||||
|
.collect(Collectors.toList())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/pet-species")
|
||||||
|
public ResponseEntity<List<DropdownOption>> getPetSpecies() {
|
||||||
|
return ResponseEntity.ok(
|
||||||
|
petRepository.findAll().stream()
|
||||||
|
.map(p -> p.getPetSpecies())
|
||||||
|
.filter(species -> species != null && !species.isBlank())
|
||||||
|
.distinct()
|
||||||
|
.sorted(String.CASE_INSENSITIVE_ORDER)
|
||||||
|
.map(species -> new DropdownOption(null, species))
|
||||||
|
.collect(Collectors.toList())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping("/stores")
|
@GetMapping("/stores")
|
||||||
public ResponseEntity<List<DropdownOption>> getStores() {
|
public ResponseEntity<List<DropdownOption>> getStores() {
|
||||||
return ResponseEntity.ok(
|
return ResponseEntity.ok(
|
||||||
|
|||||||
@@ -25,8 +25,10 @@ public class PetController {
|
|||||||
@GetMapping
|
@GetMapping
|
||||||
public ResponseEntity<Page<PetResponse>> getAllPets(
|
public ResponseEntity<Page<PetResponse>> getAllPets(
|
||||||
@RequestParam(required = false) String q,
|
@RequestParam(required = false) String q,
|
||||||
|
@RequestParam(required = false) String species,
|
||||||
|
@RequestParam(required = false) String status,
|
||||||
Pageable pageable) {
|
Pageable pageable) {
|
||||||
return ResponseEntity.ok(petService.getAllPets(q, pageable));
|
return ResponseEntity.ok(petService.getAllPets(q, species, status, pageable));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/{id}")
|
@GetMapping("/{id}")
|
||||||
|
|||||||
@@ -25,8 +25,9 @@ public class ProductController {
|
|||||||
@GetMapping
|
@GetMapping
|
||||||
public ResponseEntity<Page<ProductResponse>> getAllProducts(
|
public ResponseEntity<Page<ProductResponse>> getAllProducts(
|
||||||
@RequestParam(required = false) String q,
|
@RequestParam(required = false) String q,
|
||||||
|
@RequestParam(required = false) Long categoryId,
|
||||||
Pageable pageable) {
|
Pageable pageable) {
|
||||||
return ResponseEntity.ok(productService.getAllProducts(q, pageable));
|
return ResponseEntity.ok(productService.getAllProducts(q, categoryId, pageable));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/{id}")
|
@GetMapping("/{id}")
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ public interface CategoryRepository extends JpaRepository<Category, Long> {
|
|||||||
Optional<Category> findByCategoryName(String categoryName);
|
Optional<Category> findByCategoryName(String categoryName);
|
||||||
|
|
||||||
@Query("SELECT c FROM Category c WHERE " +
|
@Query("SELECT c FROM Category c WHERE " +
|
||||||
"LOWER(c.categoryName) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
|
"(:q IS NULL OR LOWER(c.categoryName) LIKE LOWER(CONCAT('%', :q, '%')) OR LOWER(c.categoryType) LIKE LOWER(CONCAT('%', :q, '%'))) AND " +
|
||||||
"LOWER(c.categoryType) LIKE LOWER(CONCAT('%', :q, '%'))")
|
"(:type IS NULL OR LOWER(c.categoryType) = LOWER(:type))")
|
||||||
Page<Category> searchCategories(@Param("q") String query, Pageable pageable);
|
Page<Category> searchCategories(@Param("q") String query, @Param("type") String type, Pageable pageable);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ import org.springframework.stereotype.Repository;
|
|||||||
public interface PetRepository extends JpaRepository<Pet, Long> {
|
public interface PetRepository extends JpaRepository<Pet, Long> {
|
||||||
|
|
||||||
@Query("SELECT p FROM Pet p WHERE " +
|
@Query("SELECT p FROM Pet p WHERE " +
|
||||||
"LOWER(p.petName) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
|
"(:q IS NULL OR LOWER(p.petName) LIKE LOWER(CONCAT('%', :q, '%')) OR LOWER(p.petSpecies) LIKE LOWER(CONCAT('%', :q, '%')) OR LOWER(p.petBreed) LIKE LOWER(CONCAT('%', :q, '%'))) AND " +
|
||||||
"LOWER(p.petSpecies) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
|
"(:species IS NULL OR LOWER(p.petSpecies) = LOWER(:species)) AND " +
|
||||||
"LOWER(p.petBreed) LIKE LOWER(CONCAT('%', :q, '%'))")
|
"(:status IS NULL OR LOWER(p.petStatus) = LOWER(:status))")
|
||||||
Page<Pet> searchPets(@Param("q") String query, Pageable pageable);
|
Page<Pet> searchPets(@Param("q") String query, @Param("species") String species, @Param("status") String status, Pageable pageable);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import org.springframework.stereotype.Repository;
|
|||||||
public interface ProductRepository extends JpaRepository<Product, Long> {
|
public interface ProductRepository extends JpaRepository<Product, Long> {
|
||||||
|
|
||||||
@Query("SELECT p FROM Product p WHERE " +
|
@Query("SELECT p FROM Product p WHERE " +
|
||||||
"LOWER(p.prodName) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
|
"(:q IS NULL OR LOWER(p.prodName) LIKE LOWER(CONCAT('%', :q, '%')) OR LOWER(COALESCE(p.prodDesc, '')) LIKE LOWER(CONCAT('%', :q, '%'))) AND " +
|
||||||
"LOWER(p.prodDesc) LIKE LOWER(CONCAT('%', :q, '%'))")
|
"(:categoryId IS NULL OR p.category.categoryId = :categoryId)")
|
||||||
Page<Product> searchProducts(@Param("q") String query, Pageable pageable);
|
Page<Product> searchProducts(@Param("q") String query, @Param("categoryId") Long categoryId, Pageable pageable);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,14 +20,9 @@ public class CategoryService {
|
|||||||
this.categoryRepository = categoryRepository;
|
this.categoryRepository = categoryRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Page<CategoryResponse> getAllCategories(String query, Pageable pageable) {
|
public Page<CategoryResponse> getAllCategories(String query, String type, Pageable pageable) {
|
||||||
Page<Category> categories;
|
return categoryRepository.searchCategories(normalizeFilter(query), normalizeFilter(type), pageable)
|
||||||
if (query != null && !query.trim().isEmpty()) {
|
.map(this::mapToResponse);
|
||||||
categories = categoryRepository.searchCategories(query, pageable);
|
|
||||||
} else {
|
|
||||||
categories = categoryRepository.findAll(pageable);
|
|
||||||
}
|
|
||||||
return categories.map(this::mapToResponse);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public CategoryResponse getCategoryById(Long id) {
|
public CategoryResponse getCategoryById(Long id) {
|
||||||
@@ -80,4 +75,12 @@ public class CategoryService {
|
|||||||
category.getUpdatedAt()
|
category.getUpdatedAt()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String normalizeFilter(String value) {
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String trimmed = value.trim();
|
||||||
|
return trimmed.isEmpty() ? null : trimmed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,14 +33,9 @@ public class PetService {
|
|||||||
this.catalogImageStorageService = catalogImageStorageService;
|
this.catalogImageStorageService = catalogImageStorageService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Page<PetResponse> getAllPets(String query, Pageable pageable) {
|
public Page<PetResponse> getAllPets(String query, String species, String status, Pageable pageable) {
|
||||||
Page<Pet> pets;
|
return petRepository.searchPets(normalizeFilter(query), normalizeFilter(species), normalizeFilter(status), pageable)
|
||||||
if (query != null && !query.trim().isEmpty()) {
|
.map(this::mapToResponse);
|
||||||
pets = petRepository.searchPets(query, pageable);
|
|
||||||
} else {
|
|
||||||
pets = petRepository.findAll(pageable);
|
|
||||||
}
|
|
||||||
return pets.map(this::mapToResponse);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public PetResponse getPetById(Long id) {
|
public PetResponse getPetById(Long id) {
|
||||||
@@ -182,6 +177,14 @@ public class PetService {
|
|||||||
return status == null ? "" : status.trim();
|
return status == null ? "" : status.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String normalizeFilter(String value) {
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String trimmed = value.trim();
|
||||||
|
return trimmed.isEmpty() ? null : trimmed;
|
||||||
|
}
|
||||||
|
|
||||||
private PetResponse mapToResponse(Pet pet) {
|
private PetResponse mapToResponse(Pet pet) {
|
||||||
return new PetResponse(
|
return new PetResponse(
|
||||||
pet.getPetId(),
|
pet.getPetId(),
|
||||||
|
|||||||
@@ -32,14 +32,9 @@ public class ProductService {
|
|||||||
this.catalogImageStorageService = catalogImageStorageService;
|
this.catalogImageStorageService = catalogImageStorageService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Page<ProductResponse> getAllProducts(String query, Pageable pageable) {
|
public Page<ProductResponse> getAllProducts(String query, Long categoryId, Pageable pageable) {
|
||||||
Page<Product> products;
|
return productRepository.searchProducts(normalizeFilter(query), categoryId, pageable)
|
||||||
if (query != null && !query.trim().isEmpty()) {
|
.map(this::mapToResponse);
|
||||||
products = productRepository.searchProducts(query, pageable);
|
|
||||||
} else {
|
|
||||||
products = productRepository.findAll(pageable);
|
|
||||||
}
|
|
||||||
return products.map(this::mapToResponse);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProductResponse getProductById(Long id) {
|
public ProductResponse getProductById(Long id) {
|
||||||
@@ -168,4 +163,12 @@ public class ProductService {
|
|||||||
|
|
||||||
public record ImagePayload(Resource resource, MediaType mediaType) {
|
public record ImagePayload(Resource resource, MediaType mediaType) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String normalizeFilter(String value) {
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String trimmed = value.trim();
|
||||||
|
return trimmed.isEmpty() ? null : trimmed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
226
backend/src/main/resources/dev/expand_pet_product_seed.sql
Normal file
226
backend/src/main/resources/dev/expand_pet_product_seed.sql
Normal file
@@ -0,0 +1,226 @@
|
|||||||
|
-- Expand pet and product seed data
|
||||||
|
|
||||||
|
INSERT INTO pet (petName, petSpecies, petBreed, petAge, petStatus, petPrice)
|
||||||
|
VALUES
|
||||||
|
('Rocky', 'Dog', 'German Shepherd', 1, 'Available', 475.00),
|
||||||
|
('Daisy', 'Dog', 'Poodle', 2, 'Available', 512.00),
|
||||||
|
('Cooper', 'Dog', 'Bulldog', 3, 'Available', 560.00),
|
||||||
|
('Ruby', 'Dog', 'Boxer', 4, 'Available', 575.00),
|
||||||
|
('Tucker', 'Dog', 'Dachshund', 5, 'Available', 634.00),
|
||||||
|
('Rosie', 'Dog', 'Shih Tzu', 1, 'Available', 660.00),
|
||||||
|
('Bear', 'Dog', 'Rottweiler', 2, 'Available', 686.00),
|
||||||
|
('Maggie', 'Dog', 'Corgi', 3, 'Available', 745.00),
|
||||||
|
('Leo', 'Dog', 'Husky', 4, 'Available', 749.00),
|
||||||
|
('Penny', 'Dog', 'Border Collie', 5, 'Available', 808.00),
|
||||||
|
('Jax', 'Dog', 'German Shepherd', 1, 'Available', 823.00),
|
||||||
|
('Nala', 'Dog', 'Poodle', 2, 'Available', 871.00),
|
||||||
|
('Finn', 'Dog', 'Bulldog', 3, 'Available', 447.00),
|
||||||
|
('Sadie', 'Dog', 'Boxer', 4, 'Available', 495.00),
|
||||||
|
('Ace', 'Dog', 'Dachshund', 5, 'Available', 510.00),
|
||||||
|
('Zoe', 'Dog', 'Shih Tzu', 1, 'Available', 547.00),
|
||||||
|
('Ollie', 'Dog', 'Rottweiler', 2, 'Available', 606.00),
|
||||||
|
('Millie', 'Dog', 'Corgi', 3, 'Available', 654.00),
|
||||||
|
('Murphy', 'Dog', 'Husky', 4, 'Available', 691.00),
|
||||||
|
('Willow', 'Dog', 'Border Collie', 5, 'Available', 728.00),
|
||||||
|
('Bentley', 'Dog', 'German Shepherd', 1, 'Available', 776.00),
|
||||||
|
('Lily', 'Dog', 'Poodle', 2, 'Available', 780.00),
|
||||||
|
('Scout', 'Dog', 'Bulldog', 3, 'Available', 828.00),
|
||||||
|
('Gracie', 'Dog', 'Boxer', 4, 'Available', 876.00),
|
||||||
|
('Ranger', 'Dog', 'Dachshund', 5, 'Available', 452.00),
|
||||||
|
('Hazel', 'Dog', 'Shih Tzu', 1, 'Available', 478.00),
|
||||||
|
('Moose', 'Dog', 'Rottweiler', 2, 'Available', 515.00),
|
||||||
|
('Mia', 'Dog', 'Corgi', 3, 'Available', 530.00),
|
||||||
|
('Simba', 'Cat', 'Ragdoll', 1, 'Available', 295.00),
|
||||||
|
('Cleo', 'Cat', 'Bengal', 2, 'Available', 321.00),
|
||||||
|
('Oreo', 'Cat', 'British Shorthair', 3, 'Available', 358.00),
|
||||||
|
('Pepper', 'Cat', 'Sphynx', 4, 'Available', 417.00),
|
||||||
|
('Jasper', 'Cat', 'Scottish Fold', 5, 'Available', 454.00),
|
||||||
|
('Phoebe', 'Cat', 'Russian Blue', 1, 'Available', 491.00),
|
||||||
|
('Shadow', 'Cat', 'Abyssinian', 2, 'Available', 528.00),
|
||||||
|
('Mochi', 'Cat', 'Birman', 3, 'Available', 554.00),
|
||||||
|
('Louie', 'Cat', 'Ragdoll', 4, 'Available', 591.00),
|
||||||
|
('Ivy', 'Cat', 'Bengal', 5, 'Available', 606.00),
|
||||||
|
('Theo', 'Cat', 'British Shorthair', 1, 'Available', 654.00),
|
||||||
|
('Piper', 'Cat', 'Sphynx', 2, 'Available', 251.00),
|
||||||
|
('Nova', 'Cat', 'Scottish Fold', 3, 'Available', 277.00),
|
||||||
|
('Archie', 'Cat', 'Russian Blue', 4, 'Available', 336.00),
|
||||||
|
('Olive', 'Cat', 'Abyssinian', 5, 'Available', 362.00),
|
||||||
|
('Boots', 'Cat', 'Birman', 1, 'Available', 399.00),
|
||||||
|
('Maple', 'Cat', 'Ragdoll', 2, 'Available', 436.00),
|
||||||
|
('Gizmo', 'Cat', 'Bengal', 3, 'Available', 473.00),
|
||||||
|
('Nina', 'Cat', 'British Shorthair', 4, 'Available', 499.00),
|
||||||
|
('Salem', 'Cat', 'Sphynx', 5, 'Available', 547.00),
|
||||||
|
('Stella', 'Cat', 'Scottish Fold', 1, 'Available', 595.00),
|
||||||
|
('Kiki', 'Cat', 'Russian Blue', 2, 'Available', 610.00),
|
||||||
|
('Sunny', 'Cat', 'Abyssinian', 3, 'Available', 658.00),
|
||||||
|
('Mabel', 'Cat', 'Birman', 4, 'Available', 244.00),
|
||||||
|
('Coco', 'Bird', 'Cockatiel', 1, 'Available', 119.00),
|
||||||
|
('Sky', 'Bird', 'Parakeet', 2, 'Available', 145.00),
|
||||||
|
('Sunny', 'Bird', 'Canary', 3, 'Available', 204.00),
|
||||||
|
('Kiwi', 'Bird', 'Lovebird', 1, 'Available', 230.00),
|
||||||
|
('Pico', 'Bird', 'Finch', 2, 'Available', 81.00),
|
||||||
|
('Blue', 'Bird', 'Conure', 3, 'Available', 118.00),
|
||||||
|
('Rio', 'Bird', 'Cockatiel', 1, 'Available', 144.00),
|
||||||
|
('Angel', 'Bird', 'Parakeet', 2, 'Available', 203.00),
|
||||||
|
('Chirpy', 'Bird', 'Canary', 3, 'Available', 251.00),
|
||||||
|
('Peach', 'Bird', 'Lovebird', 1, 'Available', 91.00),
|
||||||
|
('Mango', 'Bird', 'Finch', 2, 'Available', 128.00),
|
||||||
|
('Pearl', 'Bird', 'Conure', 3, 'Available', 165.00),
|
||||||
|
('Bubbles', 'Fish', 'Goldfish', 1, 'Available', 30.00),
|
||||||
|
('Splash', 'Fish', 'Betta', 2, 'Available', 56.00),
|
||||||
|
('Coral', 'Fish', 'Guppy', 1, 'Available', 23.00),
|
||||||
|
('Neptune', 'Fish', 'Molly', 2, 'Available', 23.00),
|
||||||
|
('Marlin', 'Fish', 'Tetra', 1, 'Available', 49.00),
|
||||||
|
('Finley', 'Fish', 'Angelfish', 2, 'Available', 27.00),
|
||||||
|
('Pebble', 'Fish', 'Goldfish', 1, 'Available', 64.00),
|
||||||
|
('Wave', 'Fish', 'Betta', 2, 'Available', 20.00),
|
||||||
|
('Aqua', 'Fish', 'Guppy', 1, 'Available', 57.00),
|
||||||
|
('Flash', 'Fish', 'Molly', 2, 'Available', 46.00),
|
||||||
|
('Nemo', 'Fish', 'Tetra', 1, 'Available', 13.00),
|
||||||
|
('Pearl', 'Fish', 'Angelfish', 2, 'Available', 61.00),
|
||||||
|
('Thumper', 'Rabbit', 'Mini Lop', 1, 'Adopted', 147.00),
|
||||||
|
('Clover', 'Rabbit', 'Netherland Dwarf', 2, 'Adopted', 138.00),
|
||||||
|
('Biscuit', 'Rabbit', 'Lionhead', 3, 'Adopted', 177.00),
|
||||||
|
('Hazel', 'Rabbit', 'Rex', 1, 'Adopted', 91.00),
|
||||||
|
('Juniper', 'Rabbit', 'Mini Lop', 2, 'Adopted', 83.00),
|
||||||
|
('Poppy', 'Rabbit', 'Netherland Dwarf', 3, 'Adopted', 111.00),
|
||||||
|
('Snowball', 'Rabbit', 'Lionhead', 1, 'Adopted', 172.00),
|
||||||
|
('Maple', 'Rabbit', 'Rex', 2, 'Adopted', 150.00),
|
||||||
|
('Peanut', 'Hamster', 'Syrian', 1, 'Adopted', 29.00),
|
||||||
|
('Nibbles', 'Hamster', 'Dwarf', 2, 'Adopted', 42.00),
|
||||||
|
('Pumpkin', 'Hamster', 'Roborovski', 1, 'Pending', 49.00),
|
||||||
|
('Mocha', 'Hamster', 'Syrian', 2, 'Pending', 48.00),
|
||||||
|
('Buttons', 'Hamster', 'Dwarf', 1, 'Pending', 61.00),
|
||||||
|
('Teddy', 'Hamster', 'Roborovski', 2, 'Pending', 35.00),
|
||||||
|
('Pip', 'Hamster', 'Syrian', 1, 'Pending', 39.00),
|
||||||
|
('Toffee', 'Hamster', 'Dwarf', 2, 'Pending', 52.00),
|
||||||
|
('Sprout', 'Hamster', 'Roborovski', 1, 'Available', 26.00),
|
||||||
|
('Bean', 'Hamster', 'Syrian', 2, 'Available', 28.00);
|
||||||
|
|
||||||
|
INSERT INTO product (prodName, prodPrice, categoryId, prodDesc)
|
||||||
|
VALUES
|
||||||
|
('Chicken Recipe Dog Food', 42.00, 1, 'Nutritious food and treats for dogs'),
|
||||||
|
('Beef Feast Dog Food', 51.00, 1, 'Nutritious food and treats for dogs'),
|
||||||
|
('Salmon Blend Dog Food', 17.00, 1, 'Nutritious food and treats for dogs'),
|
||||||
|
('Lamb Dinner Dog Food', 28.00, 1, 'Nutritious food and treats for dogs'),
|
||||||
|
('Puppy Starter Kibble', 39.00, 1, 'Nutritious food and treats for dogs'),
|
||||||
|
('Senior Care Dog Food', 40.00, 1, 'Nutritious food and treats for dogs'),
|
||||||
|
('Small Breed Kibble', 44.00, 1, 'Nutritious food and treats for dogs'),
|
||||||
|
('Large Breed Kibble', 57.00, 1, 'Nutritious food and treats for dogs'),
|
||||||
|
('Grain Free Dog Food', 68.00, 1, 'Nutritious food and treats for dogs'),
|
||||||
|
('Turkey Rice Formula', 79.00, 1, 'Nutritious food and treats for dogs'),
|
||||||
|
('Duck Sweet Potato Meal', 25.00, 1, 'Nutritious food and treats for dogs'),
|
||||||
|
('Venison Protein Blend', 36.00, 1, 'Nutritious food and treats for dogs'),
|
||||||
|
('Healthy Weight Dog Food', 48.00, 1, 'Nutritious food and treats for dogs'),
|
||||||
|
('Sensitive Stomach Kibble', 62.00, 1, 'Nutritious food and treats for dogs'),
|
||||||
|
('High Energy Dog Food', 72.00, 1, 'Nutritious food and treats for dogs'),
|
||||||
|
('Organic Dog Biscuits', 18.00, 1, 'Nutritious food and treats for dogs'),
|
||||||
|
('Peanut Butter Dog Treats', 33.00, 1, 'Nutritious food and treats for dogs'),
|
||||||
|
('Dental Chew Sticks', 38.00, 1, 'Nutritious food and treats for dogs'),
|
||||||
|
('Training Treat Bites', 48.00, 1, 'Nutritious food and treats for dogs'),
|
||||||
|
('Soft Chicken Treats', 57.00, 1, 'Nutritious food and treats for dogs'),
|
||||||
|
('Pumpkin Fiber Treats', 70.00, 1, 'Nutritious food and treats for dogs'),
|
||||||
|
('Joint Support Biscuits', 14.00, 1, 'Nutritious food and treats for dogs'),
|
||||||
|
('Mini Breed Dinner', 17.00, 1, 'Nutritious food and treats for dogs'),
|
||||||
|
('Farmhouse Dog Meal', 30.00, 1, 'Nutritious food and treats for dogs'),
|
||||||
|
('Feather Teaser Wand', 30.00, 2, 'Play items for active cats'),
|
||||||
|
('Catnip Mouse Toy', 24.00, 2, 'Play items for active cats'),
|
||||||
|
('Jingle Ball Set', 18.00, 2, 'Play items for active cats'),
|
||||||
|
('Scratching Post Small', 6.00, 2, 'Play items for active cats'),
|
||||||
|
('Crinkle Tunnel', 31.00, 2, 'Play items for active cats'),
|
||||||
|
('Laser Pointer Toy', 6.00, 2, 'Play items for active cats'),
|
||||||
|
('Plush Fish Toy', 19.00, 2, 'Play items for active cats'),
|
||||||
|
('Spring Coil Pack', 20.00, 2, 'Play items for active cats'),
|
||||||
|
('Hanging Door Toy', 12.00, 2, 'Play items for active cats'),
|
||||||
|
('Interactive Puzzle Toy', 22.00, 2, 'Play items for active cats'),
|
||||||
|
('Catnip Kicker Toy', 20.00, 2, 'Play items for active cats'),
|
||||||
|
('Rolling Bell Ball', 20.00, 2, 'Play items for active cats'),
|
||||||
|
('Ribbon Chase Toy', 19.00, 2, 'Play items for active cats'),
|
||||||
|
('Mini Plush Mouse', 21.00, 2, 'Play items for active cats'),
|
||||||
|
('Treat Dispensing Ball', 16.00, 2, 'Play items for active cats'),
|
||||||
|
('Double Pom Toy', 12.00, 2, 'Play items for active cats'),
|
||||||
|
('Window Perch Toy', 10.00, 2, 'Play items for active cats'),
|
||||||
|
('Scratch Pad Refill', 8.00, 2, 'Play items for active cats'),
|
||||||
|
('Rainbow Wand Toy', 23.00, 2, 'Play items for active cats'),
|
||||||
|
('Carpet Scratcher', 23.00, 2, 'Play items for active cats'),
|
||||||
|
('Bird Perch Set', 27.00, 3, 'Care supplies for pet birds'),
|
||||||
|
('Parakeet Seed Mix', 40.00, 3, 'Care supplies for pet birds'),
|
||||||
|
('Canary Food Blend', 53.00, 3, 'Care supplies for pet birds'),
|
||||||
|
('Mineral Cuttlebone', 57.00, 3, 'Care supplies for pet birds'),
|
||||||
|
('Bird Ladder Toy', 68.00, 3, 'Care supplies for pet birds'),
|
||||||
|
('Mirror Bell Combo', 80.00, 3, 'Care supplies for pet birds'),
|
||||||
|
('Clip On Food Cup', 92.00, 3, 'Care supplies for pet birds'),
|
||||||
|
('Bird Cage Liner Pack', 108.00, 3, 'Care supplies for pet birds'),
|
||||||
|
('Nesting Material Pack', 121.00, 3, 'Care supplies for pet birds'),
|
||||||
|
('Treat Spray Millet', 8.00, 3, 'Care supplies for pet birds'),
|
||||||
|
('Wooden Swing Perch', 22.00, 3, 'Care supplies for pet birds'),
|
||||||
|
('Foraging Ball Toy', 32.00, 3, 'Care supplies for pet birds'),
|
||||||
|
('Cage Cleaning Spray', 47.00, 3, 'Care supplies for pet birds'),
|
||||||
|
('Parrot Rope Perch', 54.00, 3, 'Care supplies for pet birds'),
|
||||||
|
('Bird Bath Dish', 54.00, 3, 'Care supplies for pet birds'),
|
||||||
|
('Songbird Vitamin Drops', 78.00, 3, 'Care supplies for pet birds'),
|
||||||
|
('Aquarium Filter Cartridge', 36.00, 4, 'Essential aquarium equipment and accessories'),
|
||||||
|
('Decorative Aquarium Gravel', 49.00, 4, 'Essential aquarium equipment and accessories'),
|
||||||
|
('Fish Net Medium', 34.00, 4, 'Essential aquarium equipment and accessories'),
|
||||||
|
('Water Conditioner', 45.00, 4, 'Essential aquarium equipment and accessories'),
|
||||||
|
('Aquarium Thermometer', 59.00, 4, 'Essential aquarium equipment and accessories'),
|
||||||
|
('LED Tank Light', 67.00, 4, 'Essential aquarium equipment and accessories'),
|
||||||
|
('Air Stone Pack', 76.00, 4, 'Essential aquarium equipment and accessories'),
|
||||||
|
('Aquarium Heater 50W', 92.00, 4, 'Essential aquarium equipment and accessories'),
|
||||||
|
('Aquarium Heater 100W', 106.00, 4, 'Essential aquarium equipment and accessories'),
|
||||||
|
('Fish Flake Food', 95.00, 4, 'Essential aquarium equipment and accessories'),
|
||||||
|
('Algae Scraper', 105.00, 4, 'Essential aquarium equipment and accessories'),
|
||||||
|
('Aquarium Plant Set', 122.00, 4, 'Essential aquarium equipment and accessories'),
|
||||||
|
('Bubble Curtain Kit', 136.00, 4, 'Essential aquarium equipment and accessories'),
|
||||||
|
('Breeder Box Insert', 149.00, 4, 'Essential aquarium equipment and accessories'),
|
||||||
|
('Filter Sponge Pack', 164.00, 4, 'Essential aquarium equipment and accessories'),
|
||||||
|
('Aquarium Background Roll', 183.00, 4, 'Essential aquarium equipment and accessories'),
|
||||||
|
('Glass Lid Clips', 174.00, 4, 'Essential aquarium equipment and accessories'),
|
||||||
|
('Submersible Pump', 191.00, 4, 'Essential aquarium equipment and accessories'),
|
||||||
|
('Hamster Bedding Pack', 50.00, 5, 'Supplies for small pets'),
|
||||||
|
('Rabbit Hay Bundle', 49.00, 5, 'Supplies for small pets'),
|
||||||
|
('Guinea Pig Pellets', 15.00, 5, 'Supplies for small pets'),
|
||||||
|
('Small Pet Water Bottle', 31.00, 5, 'Supplies for small pets'),
|
||||||
|
('Hamster Hideout Hut', 40.00, 5, 'Supplies for small pets'),
|
||||||
|
('Chew Stick Bundle', 48.00, 5, 'Supplies for small pets'),
|
||||||
|
('Rabbit Litter Tray', 58.00, 5, 'Supplies for small pets'),
|
||||||
|
('Exercise Ball Large', 68.00, 5, 'Supplies for small pets'),
|
||||||
|
('Small Pet Food Bowl', 20.00, 5, 'Supplies for small pets'),
|
||||||
|
('Timothy Hay Cubes', 28.00, 5, 'Supplies for small pets'),
|
||||||
|
('Guinea Pig Tunnel', 38.00, 5, 'Supplies for small pets'),
|
||||||
|
('Hamster Nesting Fluff', 47.00, 5, 'Supplies for small pets'),
|
||||||
|
('Rabbit Grooming Brush', 60.00, 5, 'Supplies for small pets'),
|
||||||
|
('Small Pet Carrier', 7.00, 5, 'Supplies for small pets'),
|
||||||
|
('Hay Rack Feeder', 11.00, 5, 'Supplies for small pets'),
|
||||||
|
('Wooden Chew Blocks', 27.00, 5, 'Supplies for small pets');
|
||||||
|
|
||||||
|
INSERT INTO productSupplier (supId, prodId, cost)
|
||||||
|
SELECT CASE MOD(p.prodId - 7, 5)
|
||||||
|
WHEN 0 THEN 1
|
||||||
|
WHEN 1 THEN 2
|
||||||
|
WHEN 2 THEN 3
|
||||||
|
WHEN 3 THEN 4
|
||||||
|
ELSE 5
|
||||||
|
END,
|
||||||
|
p.prodId,
|
||||||
|
ROUND(p.prodPrice * (0.62 + (MOD(p.prodId - 7, 5) * 0.03)), 2)
|
||||||
|
FROM product p
|
||||||
|
WHERE p.prodId >= 7
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM productSupplier ps WHERE ps.prodId = p.prodId
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO inventory (prodId, quantity)
|
||||||
|
SELECT p.prodId,
|
||||||
|
CASE p.categoryId
|
||||||
|
WHEN 1 THEN 120 + MOD((p.prodId - 7) * 17, 60)
|
||||||
|
WHEN 2 THEN 180 + MOD((p.prodId - 7) * 17, 60)
|
||||||
|
WHEN 3 THEN 70 + MOD((p.prodId - 7) * 17, 60)
|
||||||
|
WHEN 4 THEN 45 + MOD((p.prodId - 7) * 17, 60)
|
||||||
|
ELSE 95 + MOD((p.prodId - 7) * 17, 60)
|
||||||
|
END
|
||||||
|
FROM product p
|
||||||
|
WHERE p.prodId >= 7
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM inventory i WHERE i.prodId = p.prodId
|
||||||
|
);
|
||||||
@@ -26,6 +26,22 @@ public class DropdownApi {
|
|||||||
return apiClient.getObjectMapper().readValue(response, new TypeReference<List<DropdownOption>>() {});
|
return apiClient.getObjectMapper().readValue(response, new TypeReference<List<DropdownOption>>() {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<DropdownOption> getProductCategories() throws Exception {
|
||||||
|
String response = apiClient.getRawResponse("/api/v1/dropdowns/product-categories");
|
||||||
|
if (response == null || response.isEmpty()) {
|
||||||
|
throw new IllegalStateException("Empty response from product categories endpoint");
|
||||||
|
}
|
||||||
|
return apiClient.getObjectMapper().readValue(response, new TypeReference<List<DropdownOption>>() {});
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<DropdownOption> getPetSpecies() throws Exception {
|
||||||
|
String response = apiClient.getRawResponse("/api/v1/dropdowns/pet-species");
|
||||||
|
if (response == null || response.isEmpty()) {
|
||||||
|
throw new IllegalStateException("Empty response from pet species endpoint");
|
||||||
|
}
|
||||||
|
return apiClient.getObjectMapper().readValue(response, new TypeReference<List<DropdownOption>>() {});
|
||||||
|
}
|
||||||
|
|
||||||
public List<DropdownOption> getProducts() throws Exception {
|
public List<DropdownOption> getProducts() throws Exception {
|
||||||
String response = apiClient.getRawResponse("/api/v1/dropdowns/products");
|
String response = apiClient.getRawResponse("/api/v1/dropdowns/products");
|
||||||
if (response == null || response.isEmpty()) {
|
if (response == null || response.isEmpty()) {
|
||||||
|
|||||||
@@ -24,11 +24,17 @@ public class PetApi {
|
|||||||
return INSTANCE;
|
return INSTANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<PetResponse> listPets(String query) throws Exception {
|
public List<PetResponse> listPets(String query, String species, String status) throws Exception {
|
||||||
String path = "/api/v1/pets?page=0&size=1000";
|
String path = "/api/v1/pets?page=0&size=1000";
|
||||||
if (query != null && !query.isEmpty()) {
|
if (query != null && !query.isEmpty()) {
|
||||||
path += "&q=" + URLEncoder.encode(query, StandardCharsets.UTF_8);
|
path += "&q=" + URLEncoder.encode(query, StandardCharsets.UTF_8);
|
||||||
}
|
}
|
||||||
|
if (species != null && !species.isEmpty()) {
|
||||||
|
path += "&species=" + URLEncoder.encode(species, StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
if (status != null && !status.isEmpty()) {
|
||||||
|
path += "&status=" + URLEncoder.encode(status, StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
String response = apiClient.getRawResponse(path);
|
String response = apiClient.getRawResponse(path);
|
||||||
PageResponse<PetResponse> pageResponse = apiClient.getObjectMapper().readValue(
|
PageResponse<PetResponse> pageResponse = apiClient.getObjectMapper().readValue(
|
||||||
response,
|
response,
|
||||||
@@ -40,6 +46,10 @@ public class PetApi {
|
|||||||
return pageResponse.getContent();
|
return pageResponse.getContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<PetResponse> listPets(String query) throws Exception {
|
||||||
|
return listPets(query, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
public PetResponse createPet(PetRequest request) throws Exception {
|
public PetResponse createPet(PetRequest request) throws Exception {
|
||||||
return apiClient.post("/api/v1/pets", request, PetResponse.class);
|
return apiClient.post("/api/v1/pets", request, PetResponse.class);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,11 +24,14 @@ public class ProductApi {
|
|||||||
return INSTANCE;
|
return INSTANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ProductResponse> listProducts(String query) throws Exception {
|
public List<ProductResponse> listProducts(String query, Long categoryId) throws Exception {
|
||||||
String path = "/api/v1/products?page=0&size=1000";
|
String path = "/api/v1/products?page=0&size=1000";
|
||||||
if (query != null && !query.isEmpty()) {
|
if (query != null && !query.isEmpty()) {
|
||||||
path += "&q=" + URLEncoder.encode(query, StandardCharsets.UTF_8);
|
path += "&q=" + URLEncoder.encode(query, StandardCharsets.UTF_8);
|
||||||
}
|
}
|
||||||
|
if (categoryId != null) {
|
||||||
|
path += "&categoryId=" + categoryId;
|
||||||
|
}
|
||||||
String response = apiClient.getRawResponse(path);
|
String response = apiClient.getRawResponse(path);
|
||||||
PageResponse<ProductResponse> pageResponse = apiClient.getObjectMapper().readValue(
|
PageResponse<ProductResponse> pageResponse = apiClient.getObjectMapper().readValue(
|
||||||
response,
|
response,
|
||||||
@@ -40,6 +43,10 @@ public class ProductApi {
|
|||||||
return pageResponse.getContent();
|
return pageResponse.getContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<ProductResponse> listProducts(String query) throws Exception {
|
||||||
|
return listProducts(query, null);
|
||||||
|
}
|
||||||
|
|
||||||
public ProductResponse createProduct(ProductRequest request) throws Exception {
|
public ProductResponse createProduct(ProductRequest request) throws Exception {
|
||||||
return apiClient.post("/api/v1/products", request, ProductResponse.class);
|
return apiClient.post("/api/v1/products", request, ProductResponse.class);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ import javafx.scene.layout.StackPane;
|
|||||||
import javafx.stage.Modality;
|
import javafx.stage.Modality;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import org.example.petshopdesktop.api.dto.pet.PetResponse;
|
import org.example.petshopdesktop.api.dto.pet.PetResponse;
|
||||||
|
import org.example.petshopdesktop.api.dto.common.DropdownOption;
|
||||||
|
import org.example.petshopdesktop.api.endpoints.DropdownApi;
|
||||||
import org.example.petshopdesktop.api.endpoints.PetApi;
|
import org.example.petshopdesktop.api.endpoints.PetApi;
|
||||||
import org.example.petshopdesktop.controllers.dialogcontrollers.PetDialogController;
|
import org.example.petshopdesktop.controllers.dialogcontrollers.PetDialogController;
|
||||||
import org.example.petshopdesktop.models.Pet;
|
import org.example.petshopdesktop.models.Pet;
|
||||||
@@ -64,6 +66,12 @@ public class PetController {
|
|||||||
@FXML
|
@FXML
|
||||||
private TableView<Pet> tvPets;
|
private TableView<Pet> tvPets;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ComboBox<String> cbSpeciesFilter;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ComboBox<String> cbStatusFilter;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private TextField txtSearch;
|
private TextField txtSearch;
|
||||||
|
|
||||||
@@ -150,6 +158,11 @@ public class PetController {
|
|||||||
colPetPrice.setCellValueFactory(new PropertyValueFactory<Pet,Double>("petPrice"));
|
colPetPrice.setCellValueFactory(new PropertyValueFactory<Pet,Double>("petPrice"));
|
||||||
configureImageColumn(colPetImage);
|
configureImageColumn(colPetImage);
|
||||||
|
|
||||||
|
loadSpeciesFilter();
|
||||||
|
|
||||||
|
cbStatusFilter.setItems(FXCollections.observableArrayList("All Statuses", "Available", "Adopted", "Pending"));
|
||||||
|
cbStatusFilter.getSelectionModel().selectFirst();
|
||||||
|
|
||||||
displayPets();
|
displayPets();
|
||||||
|
|
||||||
tvPets.getSelectionModel().selectedItemProperty().addListener(
|
tvPets.getSelectionModel().selectedItemProperty().addListener(
|
||||||
@@ -159,9 +172,12 @@ public class PetController {
|
|||||||
});
|
});
|
||||||
|
|
||||||
txtSearch.textProperty().addListener((observable, oldValue, newValue) -> {
|
txtSearch.textProperty().addListener((observable, oldValue, newValue) -> {
|
||||||
displayFilteredPet(newValue);
|
applyFilters();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
cbSpeciesFilter.valueProperty().addListener((observable, oldValue, newValue) -> applyFilters());
|
||||||
|
cbStatusFilter.valueProperty().addListener((observable, oldValue, newValue) -> applyFilters());
|
||||||
|
|
||||||
//EventListener for DELETE key
|
//EventListener for DELETE key
|
||||||
tvPets.setOnKeyPressed(event -> {
|
tvPets.setOnKeyPressed(event -> {
|
||||||
if (event.getCode() == javafx.scene.input.KeyCode.DELETE) {
|
if (event.getCode() == javafx.scene.input.KeyCode.DELETE) {
|
||||||
@@ -173,12 +189,14 @@ public class PetController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void displayFilteredPet(String filter) {
|
private void displayFilteredPet(String filter) {
|
||||||
if (txtSearch.getText() == null || txtSearch.getText().isEmpty()){
|
String species = selectedSpecies();
|
||||||
|
String status = selectedStatus();
|
||||||
|
if ((filter == null || filter.isEmpty()) && species == null && status == null){
|
||||||
displayPets();
|
displayPets();
|
||||||
} else {
|
} else {
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
try {
|
try {
|
||||||
List<PetResponse> pets = PetApi.getInstance().listPets(filter);
|
List<PetResponse> pets = PetApi.getInstance().listPets(filter, species, status);
|
||||||
List<Pet> petList = pets.stream()
|
List<Pet> petList = pets.stream()
|
||||||
.map(this::mapToPet)
|
.map(this::mapToPet)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
@@ -203,7 +221,7 @@ public class PetController {
|
|||||||
private void displayPets() {
|
private void displayPets() {
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
try {
|
try {
|
||||||
List<PetResponse> pets = PetApi.getInstance().listPets(null);
|
List<PetResponse> pets = PetApi.getInstance().listPets(null, selectedSpecies(), selectedStatus());
|
||||||
List<Pet> petList = pets.stream()
|
List<Pet> petList = pets.stream()
|
||||||
.map(this::mapToPet)
|
.map(this::mapToPet)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
@@ -224,6 +242,40 @@ public class PetController {
|
|||||||
}).start();
|
}).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void applyFilters() {
|
||||||
|
displayFilteredPet(txtSearch.getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadSpeciesFilter() {
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
List<String> values = DropdownApi.getInstance().getPetSpecies().stream()
|
||||||
|
.map(DropdownOption::getLabel)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
values.add(0, "All Species");
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
cbSpeciesFilter.setItems(FXCollections.observableArrayList(values));
|
||||||
|
cbSpeciesFilter.getSelectionModel().selectFirst();
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
Platform.runLater(() -> ActivityLogger.getInstance().logException(
|
||||||
|
"PetController.loadSpeciesFilter",
|
||||||
|
e,
|
||||||
|
"Loading species filter options"));
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String selectedSpecies() {
|
||||||
|
String value = cbSpeciesFilter.getValue();
|
||||||
|
return value == null || value.equals("All Species") ? null : value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String selectedStatus() {
|
||||||
|
String value = cbStatusFilter.getValue();
|
||||||
|
return value == null || value.equals("All Statuses") ? null : value;
|
||||||
|
}
|
||||||
|
|
||||||
private void openDialog(Pet pet, String mode){
|
private void openDialog(Pet pet, String mode){
|
||||||
//Get new view
|
//Get new view
|
||||||
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/org/example/petshopdesktop/dialogviews/pet-dialog-view.fxml"));
|
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/org/example/petshopdesktop/dialogviews/pet-dialog-view.fxml"));
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ import javafx.stage.Modality;
|
|||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import org.example.petshopdesktop.DTOs.ProductDTO;
|
import org.example.petshopdesktop.DTOs.ProductDTO;
|
||||||
import org.example.petshopdesktop.api.dto.product.ProductResponse;
|
import org.example.petshopdesktop.api.dto.product.ProductResponse;
|
||||||
|
import org.example.petshopdesktop.api.dto.common.DropdownOption;
|
||||||
|
import org.example.petshopdesktop.api.endpoints.DropdownApi;
|
||||||
import org.example.petshopdesktop.api.endpoints.ProductApi;
|
import org.example.petshopdesktop.api.endpoints.ProductApi;
|
||||||
import org.example.petshopdesktop.controllers.dialogcontrollers.ProductDialogController;
|
import org.example.petshopdesktop.controllers.dialogcontrollers.ProductDialogController;
|
||||||
import org.example.petshopdesktop.util.ActivityLogger;
|
import org.example.petshopdesktop.util.ActivityLogger;
|
||||||
@@ -62,6 +64,9 @@ public class ProductController {
|
|||||||
@FXML
|
@FXML
|
||||||
private TableView<ProductDTO> tvProducts;
|
private TableView<ProductDTO> tvProducts;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ComboBox<DropdownOption> cbCategoryFilter;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private TextField txtSearch;
|
private TextField txtSearch;
|
||||||
|
|
||||||
@@ -87,6 +92,7 @@ public class ProductController {
|
|||||||
colProductCategory.setCellValueFactory(new PropertyValueFactory<ProductDTO,String>("categoryName"));
|
colProductCategory.setCellValueFactory(new PropertyValueFactory<ProductDTO,String>("categoryName"));
|
||||||
colProductDesc.setCellValueFactory(new PropertyValueFactory<ProductDTO,String>("prodDesc"));
|
colProductDesc.setCellValueFactory(new PropertyValueFactory<ProductDTO,String>("prodDesc"));
|
||||||
configureImageColumn(colProductImage);
|
configureImageColumn(colProductImage);
|
||||||
|
loadCategoryFilter();
|
||||||
|
|
||||||
displayProduct();
|
displayProduct();
|
||||||
|
|
||||||
@@ -100,9 +106,11 @@ public class ProductController {
|
|||||||
|
|
||||||
//EventListener to search when text is changed on searchbar
|
//EventListener to search when text is changed on searchbar
|
||||||
txtSearch.textProperty().addListener((observable, oldValue, newValue) -> {
|
txtSearch.textProperty().addListener((observable, oldValue, newValue) -> {
|
||||||
displayFilteredProduct(newValue);
|
applyFilters();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
cbCategoryFilter.valueProperty().addListener((observable, oldValue, newValue) -> applyFilters());
|
||||||
|
|
||||||
//EventListener for DELETE key press
|
//EventListener for DELETE key press
|
||||||
tvProducts.setOnKeyPressed(event -> {
|
tvProducts.setOnKeyPressed(event -> {
|
||||||
if (event.getCode() == javafx.scene.input.KeyCode.DELETE) {
|
if (event.getCode() == javafx.scene.input.KeyCode.DELETE) {
|
||||||
@@ -120,7 +128,7 @@ public class ProductController {
|
|||||||
private void displayProduct(){
|
private void displayProduct(){
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
try {
|
try {
|
||||||
List<ProductResponse> products = ProductApi.getInstance().listProducts(null);
|
List<ProductResponse> products = ProductApi.getInstance().listProducts(null, selectedCategoryId());
|
||||||
List<ProductDTO> productDTOs = products.stream()
|
List<ProductDTO> productDTOs = products.stream()
|
||||||
.map(this::mapToProductDTO)
|
.map(this::mapToProductDTO)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
@@ -222,12 +230,12 @@ public class ProductController {
|
|||||||
* @param filter word to filter table
|
* @param filter word to filter table
|
||||||
*/
|
*/
|
||||||
private void displayFilteredProduct(String filter){
|
private void displayFilteredProduct(String filter){
|
||||||
if (txtSearch.getText() == null || txtSearch.getText().isEmpty()){
|
if ((txtSearch.getText() == null || txtSearch.getText().isEmpty()) && selectedCategoryId() == null){
|
||||||
displayProduct();
|
displayProduct();
|
||||||
} else {
|
} else {
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
try {
|
try {
|
||||||
List<ProductResponse> products = ProductApi.getInstance().listProducts(filter);
|
List<ProductResponse> products = ProductApi.getInstance().listProducts(filter, selectedCategoryId());
|
||||||
List<ProductDTO> productDTOs = products.stream()
|
List<ProductDTO> productDTOs = products.stream()
|
||||||
.map(this::mapToProductDTO)
|
.map(this::mapToProductDTO)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
@@ -249,6 +257,37 @@ public class ProductController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void applyFilters() {
|
||||||
|
displayFilteredProduct(txtSearch.getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadCategoryFilter() {
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
List<DropdownOption> options = new ArrayList<>();
|
||||||
|
DropdownOption all = new DropdownOption();
|
||||||
|
all.setId(null);
|
||||||
|
all.setLabel("All Categories");
|
||||||
|
options.add(all);
|
||||||
|
options.addAll(DropdownApi.getInstance().getProductCategories());
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
cbCategoryFilter.setItems(FXCollections.observableArrayList(options));
|
||||||
|
cbCategoryFilter.getSelectionModel().selectFirst();
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
Platform.runLater(() -> ActivityLogger.getInstance().logException(
|
||||||
|
"ProductController.loadCategoryFilter",
|
||||||
|
e,
|
||||||
|
"Loading category filter options"));
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Long selectedCategoryId() {
|
||||||
|
DropdownOption option = cbCategoryFilter.getValue();
|
||||||
|
return option == null ? null : option.getId();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function to open the new Dialog for edit or adding
|
* Function to open the new Dialog for edit or adding
|
||||||
* depending on the mode given
|
* depending on the mode given
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ public class ProductDialogController {
|
|||||||
|
|
||||||
//Set up combobox for selecting category
|
//Set up combobox for selecting category
|
||||||
try {
|
try {
|
||||||
List<DropdownOption> categories = DropdownApi.getInstance().getCategories();
|
List<DropdownOption> categories = DropdownApi.getInstance().getProductCategories();
|
||||||
if (categories != null) {
|
if (categories != null) {
|
||||||
ObservableList<DropdownOption> categoriesObs = FXCollections.observableArrayList(categories);
|
ObservableList<DropdownOption> categoriesObs = FXCollections.observableArrayList(categories);
|
||||||
cbProdCategory.setItems(categoriesObs);
|
cbProdCategory.setItems(categoriesObs);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
<?import javafx.geometry.Insets?>
|
<?import javafx.geometry.Insets?>
|
||||||
<?import javafx.scene.control.Button?>
|
<?import javafx.scene.control.Button?>
|
||||||
|
<?import javafx.scene.control.ComboBox?>
|
||||||
<?import javafx.scene.control.Label?>
|
<?import javafx.scene.control.Label?>
|
||||||
<?import javafx.scene.control.TableColumn?>
|
<?import javafx.scene.control.TableColumn?>
|
||||||
<?import javafx.scene.control.TableView?>
|
<?import javafx.scene.control.TableView?>
|
||||||
@@ -59,13 +60,15 @@
|
|||||||
<Insets bottom="10.0" left="15.0" right="15.0" top="10.0" />
|
<Insets bottom="10.0" left="15.0" right="15.0" top="10.0" />
|
||||||
</padding>
|
</padding>
|
||||||
<children>
|
<children>
|
||||||
<TextField fx:id="txtSearch" prefHeight="31.0" prefWidth="150.0" promptText="Search Pets..." style="-fx-border-width: 0; -fx-background-color: transparent;" HBox.hgrow="ALWAYS">
|
<TextField fx:id="txtSearch" prefHeight="31.0" prefWidth="150.0" promptText="Search Pets..." style="-fx-border-width: 0; -fx-background-color: transparent;" HBox.hgrow="ALWAYS">
|
||||||
<font>
|
<font>
|
||||||
<Font size="15.0" />
|
<Font size="15.0" />
|
||||||
</font>
|
</font>
|
||||||
</TextField>
|
</TextField>
|
||||||
</children>
|
<ComboBox fx:id="cbSpeciesFilter" prefWidth="150.0" promptText="Species" />
|
||||||
</HBox>
|
<ComboBox fx:id="cbStatusFilter" prefWidth="150.0" promptText="Status" />
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
<TableView fx:id="tvPets" prefHeight="362.0" prefWidth="752.0" style="-fx-background-color: white; -fx-background-radius: 12;" VBox.vgrow="ALWAYS">
|
<TableView fx:id="tvPets" prefHeight="362.0" prefWidth="752.0" style="-fx-background-color: white; -fx-background-radius: 12;" VBox.vgrow="ALWAYS">
|
||||||
<columns>
|
<columns>
|
||||||
<TableColumn fx:id="colPetId" prefWidth="55.0" text="ID" />
|
<TableColumn fx:id="colPetId" prefWidth="55.0" text="ID" />
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
<?import javafx.geometry.Insets?>
|
<?import javafx.geometry.Insets?>
|
||||||
<?import javafx.scene.control.Button?>
|
<?import javafx.scene.control.Button?>
|
||||||
|
<?import javafx.scene.control.ComboBox?>
|
||||||
<?import javafx.scene.control.Label?>
|
<?import javafx.scene.control.Label?>
|
||||||
<?import javafx.scene.control.TableColumn?>
|
<?import javafx.scene.control.TableColumn?>
|
||||||
<?import javafx.scene.control.TableView?>
|
<?import javafx.scene.control.TableView?>
|
||||||
@@ -58,13 +59,14 @@
|
|||||||
<Insets bottom="10.0" left="15.0" right="15.0" top="10.0" />
|
<Insets bottom="10.0" left="15.0" right="15.0" top="10.0" />
|
||||||
</padding>
|
</padding>
|
||||||
<children>
|
<children>
|
||||||
<TextField fx:id="txtSearch" prefHeight="31.0" prefWidth="150.0" promptText="Search products..." style="-fx-border-width: 0; -fx-background-color: transparent;" HBox.hgrow="ALWAYS">
|
<TextField fx:id="txtSearch" prefHeight="31.0" prefWidth="150.0" promptText="Search products..." style="-fx-border-width: 0; -fx-background-color: transparent;" HBox.hgrow="ALWAYS">
|
||||||
<font>
|
<font>
|
||||||
<Font size="15.0" />
|
<Font size="15.0" />
|
||||||
</font>
|
</font>
|
||||||
</TextField>
|
</TextField>
|
||||||
</children>
|
<ComboBox fx:id="cbCategoryFilter" prefWidth="180.0" promptText="Category" />
|
||||||
</HBox>
|
</children>
|
||||||
|
</HBox>
|
||||||
<TableView fx:id="tvProducts" prefHeight="362.0" prefWidth="752.0" style="-fx-background-color: white; -fx-background-radius: 12;" VBox.vgrow="ALWAYS">
|
<TableView fx:id="tvProducts" prefHeight="362.0" prefWidth="752.0" style="-fx-background-color: white; -fx-background-radius: 12;" VBox.vgrow="ALWAYS">
|
||||||
<columns>
|
<columns>
|
||||||
<TableColumn fx:id="colProductId" prefWidth="55.0" text="ID" />
|
<TableColumn fx:id="colProductId" prefWidth="55.0" text="ID" />
|
||||||
|
|||||||
Reference in New Issue
Block a user