fix desktop pet and product dialogs

This commit is contained in:
2026-03-29 16:54:01 -06:00
parent 8c6a53250a
commit 93e71434df
7 changed files with 99 additions and 28 deletions

View File

@@ -44,6 +44,33 @@ public class Validator {
return msg; return msg;
} }
/**
* Checks if the input is a positive double
* @param value input of string
* @param name name of input
* @return error msg if input is not a number or not positive, otherwise empty
*/
public static String isPositiveDouble(String value, String name){
String msg ="";
if (value == null) {
msg += name + " must be a number.\n";
return msg;
}
double result;
try {
result = Double.parseDouble(value);
if (result <= 0){
msg += name + " must be greater than 0. \n";
}
}
catch (NumberFormatException e){
msg += name + " must be a number.\n";
}
return msg;
}
/** /**
* Checks if the input is a double in 2 different range * Checks if the input is a double in 2 different range
* @param value input of string * @param value input of string
@@ -95,6 +122,28 @@ public class Validator {
return msg; return msg;
} }
/**
* Checks if the input is a positive integer
* @param value input of string
* @param name name of input
* @return error msg if input is not a number or not positive, otherwise empty
*/
public static String isPositiveInteger(String value, String name){
String msg ="";
int result;
try {
result = Integer.parseInt(value);
if (result <= 0){
msg += name + " must be greater than 0. \n";
}
}
catch (NumberFormatException e){
msg += name + " must be a whole number.\n";
}
return msg;
}
/** /**
* check if the string is a given amount of characters or fewer * check if the string is a given amount of characters or fewer
* @param value input of string * @param value input of string

View File

@@ -224,15 +224,21 @@ public class ApiClient {
try { try {
if (response.body() != null && !response.body().isEmpty()) { if (response.body() != null && !response.body().isEmpty()) {
var errorNode = objectMapper.readTree(response.body()); var errorNode = objectMapper.readTree(response.body());
if (errorNode.has("message")) {
return errorNode.get("message").asText();
}
if (errorNode.has("errors")) { if (errorNode.has("errors")) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
errorNode.get("errors").fields().forEachRemaining(entry -> { errorNode.get("errors").fields().forEachRemaining(entry -> {
sb.append(entry.getValue().asText()).append("\n"); String errorText = entry.getValue().asText();
if (errorText != null && !errorText.isBlank()) {
sb.append(errorText).append("\n");
}
}); });
return sb.toString().trim(); if (sb.length() > 0) {
String message = errorNode.has("message") ? errorNode.get("message").asText() : null;
return (message != null && !message.isBlank() ? message + "\n" : "") + sb.toString().trim();
}
}
if (errorNode.has("message")) {
return errorNode.get("message").asText();
} }
} }
} catch (Exception e) { } catch (Exception e) {

View File

@@ -20,6 +20,7 @@ import org.example.petshopdesktop.util.FilePickerSupport;
import java.io.File; import java.io.File;
import java.math.BigDecimal; import java.math.BigDecimal;
public class PetDialogController { public class PetDialogController {
@FXML @FXML
@@ -120,7 +121,7 @@ public class PetDialogController {
//Check validation (format) //Check validation (format)
errorMsg += Validator.isNonNegativeDouble(txtPetPrice.getText(), "Price"); errorMsg += Validator.isNonNegativeDouble(txtPetPrice.getText(), "Price");
errorMsg += Validator.isNonNegativeInteger(txtPetAge.getText(), "Age"); errorMsg += Validator.isPositiveInteger(txtPetAge.getText(), "Age");
if(errorMsg.isEmpty()){ if(errorMsg.isEmpty()){
PetRequest request = buildPetRequest(); PetRequest request = buildPetRequest();
@@ -252,6 +253,7 @@ public class PetDialogController {
} }
private void applyImageChanges(Long petId) throws Exception { private void applyImageChanges(Long petId) throws Exception {
String previousImageUrl = currentImageUrl;
if (removeImageRequested) { if (removeImageRequested) {
try { try {
PetApi.getInstance().deletePetImage(petId); PetApi.getInstance().deletePetImage(petId);
@@ -260,7 +262,15 @@ public class PetDialogController {
} }
if (selectedImageFile != null) { if (selectedImageFile != null) {
PetApi.getInstance().uploadPetImage(petId, selectedImageFile.toPath()); PetApi.getInstance().uploadPetImage(petId, selectedImageFile.toPath());
currentImageUrl = "/api/v1/pets/" + petId + "/image";
} else if (removeImageRequested) {
currentImageUrl = null;
} }
DesktopImageSupport.evict(previousImageUrl);
DesktopImageSupport.evict(currentImageUrl);
selectedImageFile = null;
removeImageRequested = false;
refreshImagePreview();
} }
private void refreshImagePreview() { private void refreshImagePreview() {

View File

@@ -130,7 +130,7 @@ public class ProductDialogController {
errorMsg += Validator.isLessThanVarChars(txtProdPrice.getText(), "Product Price", 12); errorMsg += Validator.isLessThanVarChars(txtProdPrice.getText(), "Product Price", 12);
//Check Validation (format) //Check Validation (format)
errorMsg += Validator.isNonNegativeDouble(txtProdPrice.getText(), "Product Price"); errorMsg += Validator.isPositiveDouble(txtProdPrice.getText(), "Product Price");
if (errorMsg.isEmpty()) { if (errorMsg.isEmpty()) {
try { try {
@@ -258,6 +258,7 @@ public class ProductDialogController {
} }
private void applyImageChanges(Long productId) throws Exception { private void applyImageChanges(Long productId) throws Exception {
String previousImageUrl = currentImageUrl;
if (removeImageRequested) { if (removeImageRequested) {
try { try {
ProductApi.getInstance().deleteProductImage(productId); ProductApi.getInstance().deleteProductImage(productId);
@@ -266,7 +267,15 @@ public class ProductDialogController {
} }
if (selectedImageFile != null) { if (selectedImageFile != null) {
ProductApi.getInstance().uploadProductImage(productId, selectedImageFile.toPath()); ProductApi.getInstance().uploadProductImage(productId, selectedImageFile.toPath());
currentImageUrl = "/api/v1/products/" + productId + "/image";
} else if (removeImageRequested) {
currentImageUrl = null;
} }
DesktopImageSupport.evict(previousImageUrl);
DesktopImageSupport.evict(currentImageUrl);
selectedImageFile = null;
removeImageRequested = false;
refreshImagePreview();
} }
private void refreshImagePreview() { private void refreshImagePreview() {

View File

@@ -20,12 +20,21 @@ public final class DesktopImageSupport {
imageView.setFitWidth(width); imageView.setFitWidth(width);
imageView.setFitHeight(height); imageView.setFitHeight(height);
imageView.setPreserveRatio(true); imageView.setPreserveRatio(true);
imageView.setSmooth(true);
imageView.setImage(null); imageView.setImage(null);
if (imageUrl == null || imageUrl.isBlank()) { if (imageUrl == null || imageUrl.isBlank()) {
return; return;
} }
if (imageUrl.startsWith("file:")) {
Image image = new Image(imageUrl, 0, 0, true, true);
if (!image.isError()) {
imageView.setImage(image);
}
return;
}
Image cached = IMAGE_CACHE.get(imageUrl); Image cached = IMAGE_CACHE.get(imageUrl);
if (cached != null) { if (cached != null) {
imageView.setImage(cached); imageView.setImage(cached);
@@ -35,7 +44,7 @@ public final class DesktopImageSupport {
new Thread(() -> { new Thread(() -> {
try { try {
byte[] bytes = ApiClient.getInstance().getBytes(imageUrl); byte[] bytes = ApiClient.getInstance().getBytes(imageUrl);
Image image = new Image(new ByteArrayInputStream(bytes), width, height, true, true); Image image = new Image(new ByteArrayInputStream(bytes));
if (!image.isError()) { if (!image.isError()) {
IMAGE_CACHE.put(imageUrl, image); IMAGE_CACHE.put(imageUrl, image);
Platform.runLater(() -> imageView.setImage(image)); Platform.runLater(() -> imageView.setImage(image));

View File

@@ -10,11 +10,10 @@
<?import javafx.scene.layout.GridPane?> <?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.HBox?> <?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.Region?> <?import javafx.scene.layout.Region?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.layout.VBox?> <?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?> <?import javafx.scene.text.Font?>
<VBox minHeight="-Infinity" minWidth="-Infinity" prefHeight="523.0" prefWidth="790.0" spacing="20.0" style="-fx-font-size: 14px;" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.example.petshopdesktop.controllers.dialogcontrollers.PetDialogController"> <VBox minHeight="-Infinity" minWidth="-Infinity" prefHeight="560.0" prefWidth="790.0" spacing="20.0" style="-fx-font-size: 14px;" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.example.petshopdesktop.controllers.dialogcontrollers.PetDialogController">
<children> <children>
<HBox alignment="CENTER_LEFT" prefHeight="79.0" prefWidth="727.0" spacing="20.0" style="-fx-background-color: #2C3E50; -fx-background-radius: 14;"> <HBox alignment="CENTER_LEFT" prefHeight="79.0" prefWidth="727.0" spacing="20.0" style="-fx-background-color: #2C3E50; -fx-background-radius: 14;">
<children> <children>
@@ -63,18 +62,13 @@
<Insets left="15.0" right="15.0" /> <Insets left="15.0" right="15.0" />
</padding> </padding>
</HBox> </HBox>
<VBox prefHeight="370.0" prefWidth="750.0" style="-fx-background-color: white; -fx-background-radius: 14; -fx-border-width: 2; -fx-border-color: #5580b5; -fx-border-radius: 14;"> <VBox prefHeight="405.0" prefWidth="750.0" style="-fx-background-color: white; -fx-background-radius: 14; -fx-border-width: 2; -fx-border-color: #5580b5; -fx-border-radius: 14;">
<children> <children>
<GridPane hgap="25.0" VBox.vgrow="ALWAYS"> <GridPane hgap="25.0" vgap="10.0">
<columnConstraints> <columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints> </columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<children> <children>
<VBox prefHeight="200.0" prefWidth="100.0" spacing="8.0"> <VBox prefHeight="200.0" prefWidth="100.0" spacing="8.0">
<children> <children>

View File

@@ -10,11 +10,10 @@
<?import javafx.scene.layout.GridPane?> <?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.HBox?> <?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.Region?> <?import javafx.scene.layout.Region?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.layout.VBox?> <?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?> <?import javafx.scene.text.Font?>
<VBox minHeight="-Infinity" minWidth="-Infinity" prefHeight="523.0" prefWidth="790.0" spacing="20.0" style="-fx-font-size: 14px;" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.example.petshopdesktop.controllers.dialogcontrollers.ProductDialogController"> <VBox minHeight="-Infinity" minWidth="-Infinity" prefHeight="560.0" prefWidth="790.0" spacing="20.0" style="-fx-font-size: 14px;" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.example.petshopdesktop.controllers.dialogcontrollers.ProductDialogController">
<children> <children>
<HBox alignment="CENTER_LEFT" prefHeight="79.0" prefWidth="727.0" spacing="20.0" style="-fx-background-color: #2C3E50; -fx-background-radius: 14;"> <HBox alignment="CENTER_LEFT" prefHeight="79.0" prefWidth="727.0" spacing="20.0" style="-fx-background-color: #2C3E50; -fx-background-radius: 14;">
<children> <children>
@@ -63,19 +62,14 @@
<Insets left="15.0" right="15.0" /> <Insets left="15.0" right="15.0" />
</padding> </padding>
</HBox> </HBox>
<VBox prefHeight="370.0" prefWidth="750.0" style="-fx-background-color: white; -fx-background-radius: 14; -fx-border-width: 2; -fx-border-color: #5580b5; -fx-border-radius: 14;"> <VBox prefHeight="405.0" prefWidth="750.0" style="-fx-background-color: white; -fx-background-radius: 14; -fx-border-width: 2; -fx-border-color: #5580b5; -fx-border-radius: 14;">
<children> <children>
<GridPane hgap="25.0" VBox.vgrow="ALWAYS"> <GridPane hgap="25.0" vgap="10.0">
<columnConstraints> <columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints> </columnConstraints>
<rowConstraints> <children>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<VBox prefHeight="200.0" prefWidth="100.0" spacing="8.0"> <VBox prefHeight="200.0" prefWidth="100.0" spacing="8.0">
<children> <children>
<Label text="Product Name:" textFill="#2c3e50"> <Label text="Product Name:" textFill="#2c3e50">