Add pet product filters

This commit is contained in:
2026-03-29 22:54:25 -06:00
parent c48e3b8a95
commit e572d9f3cf
15 changed files with 166 additions and 61 deletions

View File

@@ -24,11 +24,17 @@ public class PetApi {
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";
if (query != null && !query.isEmpty()) {
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);
PageResponse<PetResponse> pageResponse = apiClient.getObjectMapper().readValue(
response,
@@ -40,6 +46,10 @@ public class PetApi {
return pageResponse.getContent();
}
public List<PetResponse> listPets(String query) throws Exception {
return listPets(query, null, null);
}
public PetResponse createPet(PetRequest request) throws Exception {
return apiClient.post("/api/v1/pets", request, PetResponse.class);
}

View File

@@ -24,11 +24,14 @@ public class ProductApi {
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";
if (query != null && !query.isEmpty()) {
path += "&q=" + URLEncoder.encode(query, StandardCharsets.UTF_8);
}
if (categoryId != null) {
path += "&categoryId=" + categoryId;
}
String response = apiClient.getRawResponse(path);
PageResponse<ProductResponse> pageResponse = apiClient.getObjectMapper().readValue(
response,
@@ -40,6 +43,10 @@ public class ProductApi {
return pageResponse.getContent();
}
public List<ProductResponse> listProducts(String query) throws Exception {
return listProducts(query, null);
}
public ProductResponse createProduct(ProductRequest request) throws Exception {
return apiClient.post("/api/v1/products", request, ProductResponse.class);
}

View File

@@ -64,6 +64,12 @@ public class PetController {
@FXML
private TableView<Pet> tvPets;
@FXML
private ComboBox<String> cbSpeciesFilter;
@FXML
private ComboBox<String> cbStatusFilter;
@FXML
private TextField txtSearch;
@@ -150,6 +156,12 @@ public class PetController {
colPetPrice.setCellValueFactory(new PropertyValueFactory<Pet,Double>("petPrice"));
configureImageColumn(colPetImage);
cbSpeciesFilter.setItems(FXCollections.observableArrayList("All Species", "Dog", "Cat", "Bird", "Fish", "Rabbit", "Hamster"));
cbSpeciesFilter.getSelectionModel().selectFirst();
cbStatusFilter.setItems(FXCollections.observableArrayList("All Statuses", "Available", "Adopted", "Pending"));
cbStatusFilter.getSelectionModel().selectFirst();
displayPets();
tvPets.getSelectionModel().selectedItemProperty().addListener(
@@ -159,9 +171,12 @@ public class PetController {
});
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
tvPets.setOnKeyPressed(event -> {
if (event.getCode() == javafx.scene.input.KeyCode.DELETE) {
@@ -173,12 +188,14 @@ public class PetController {
}
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();
} else {
new Thread(() -> {
try {
List<PetResponse> pets = PetApi.getInstance().listPets(filter);
List<PetResponse> pets = PetApi.getInstance().listPets(filter, species, status);
List<Pet> petList = pets.stream()
.map(this::mapToPet)
.collect(Collectors.toList());
@@ -203,7 +220,7 @@ public class PetController {
private void displayPets() {
new Thread(() -> {
try {
List<PetResponse> pets = PetApi.getInstance().listPets(null);
List<PetResponse> pets = PetApi.getInstance().listPets(null, selectedSpecies(), selectedStatus());
List<Pet> petList = pets.stream()
.map(this::mapToPet)
.collect(Collectors.toList());
@@ -224,6 +241,20 @@ public class PetController {
}).start();
}
private void applyFilters() {
displayFilteredPet(txtSearch.getText());
}
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){
//Get new view
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/org/example/petshopdesktop/dialogviews/pet-dialog-view.fxml"));

View File

@@ -16,6 +16,8 @@ import javafx.stage.Modality;
import javafx.stage.Stage;
import org.example.petshopdesktop.DTOs.ProductDTO;
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.controllers.dialogcontrollers.ProductDialogController;
import org.example.petshopdesktop.util.ActivityLogger;
@@ -62,6 +64,9 @@ public class ProductController {
@FXML
private TableView<ProductDTO> tvProducts;
@FXML
private ComboBox<DropdownOption> cbCategoryFilter;
@FXML
private TextField txtSearch;
@@ -87,6 +92,7 @@ public class ProductController {
colProductCategory.setCellValueFactory(new PropertyValueFactory<ProductDTO,String>("categoryName"));
colProductDesc.setCellValueFactory(new PropertyValueFactory<ProductDTO,String>("prodDesc"));
configureImageColumn(colProductImage);
loadCategoryFilter();
displayProduct();
@@ -100,9 +106,11 @@ public class ProductController {
//EventListener to search when text is changed on searchbar
txtSearch.textProperty().addListener((observable, oldValue, newValue) -> {
displayFilteredProduct(newValue);
applyFilters();
});
cbCategoryFilter.valueProperty().addListener((observable, oldValue, newValue) -> applyFilters());
//EventListener for DELETE key press
tvProducts.setOnKeyPressed(event -> {
if (event.getCode() == javafx.scene.input.KeyCode.DELETE) {
@@ -120,7 +128,7 @@ public class ProductController {
private void displayProduct(){
new Thread(() -> {
try {
List<ProductResponse> products = ProductApi.getInstance().listProducts(null);
List<ProductResponse> products = ProductApi.getInstance().listProducts(null, selectedCategoryId());
List<ProductDTO> productDTOs = products.stream()
.map(this::mapToProductDTO)
.collect(Collectors.toList());
@@ -222,12 +230,12 @@ public class ProductController {
* @param filter word to filter table
*/
private void displayFilteredProduct(String filter){
if (txtSearch.getText() == null || txtSearch.getText().isEmpty()){
if ((txtSearch.getText() == null || txtSearch.getText().isEmpty()) && selectedCategoryId() == null){
displayProduct();
} else {
new Thread(() -> {
try {
List<ProductResponse> products = ProductApi.getInstance().listProducts(filter);
List<ProductResponse> products = ProductApi.getInstance().listProducts(filter, selectedCategoryId());
List<ProductDTO> productDTOs = products.stream()
.map(this::mapToProductDTO)
.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().getCategories());
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
* depending on the mode given

View File

@@ -2,6 +2,7 @@
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ComboBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
@@ -59,13 +60,15 @@
<Insets bottom="10.0" left="15.0" right="15.0" top="10.0" />
</padding>
<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">
<font>
<Font size="15.0" />
</font>
</TextField>
</children>
</HBox>
<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 size="15.0" />
</font>
</TextField>
<ComboBox fx:id="cbSpeciesFilter" prefWidth="150.0" promptText="Species" />
<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">
<columns>
<TableColumn fx:id="colPetId" prefWidth="55.0" text="ID" />

View File

@@ -2,6 +2,7 @@
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ComboBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
@@ -58,13 +59,14 @@
<Insets bottom="10.0" left="15.0" right="15.0" top="10.0" />
</padding>
<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">
<font>
<Font size="15.0" />
</font>
</TextField>
</children>
</HBox>
<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 size="15.0" />
</font>
</TextField>
<ComboBox fx:id="cbCategoryFilter" prefWidth="180.0" promptText="Category" />
</children>
</HBox>
<TableView fx:id="tvProducts" prefHeight="362.0" prefWidth="752.0" style="-fx-background-color: white; -fx-background-radius: 12;" VBox.vgrow="ALWAYS">
<columns>
<TableColumn fx:id="colProductId" prefWidth="55.0" text="ID" />