Merge pull request #52 from RecentRunner/desktop-pet-product-pictures
Add desktop pet and product images
This commit was merged in pull request #52.
This commit is contained in:
@@ -15,15 +15,17 @@ public class ProductDTO {
|
|||||||
private SimpleIntegerProperty categoryId; //used for edit and delete
|
private SimpleIntegerProperty categoryId; //used for edit and delete
|
||||||
private SimpleStringProperty categoryName;
|
private SimpleStringProperty categoryName;
|
||||||
private SimpleStringProperty prodDesc;
|
private SimpleStringProperty prodDesc;
|
||||||
|
private SimpleStringProperty imageUrl;
|
||||||
|
|
||||||
//constructor
|
//constructor
|
||||||
public ProductDTO(int prodId, String prodName, double prodPrice, int categoryId, String categoryName, String prodDesc) {
|
public ProductDTO(int prodId, String prodName, double prodPrice, int categoryId, String categoryName, String prodDesc, String imageUrl) {
|
||||||
this.prodId = new SimpleIntegerProperty(prodId);
|
this.prodId = new SimpleIntegerProperty(prodId);
|
||||||
this.prodName = new SimpleStringProperty(prodName);
|
this.prodName = new SimpleStringProperty(prodName);
|
||||||
this.prodPrice = new SimpleDoubleProperty(prodPrice);
|
this.prodPrice = new SimpleDoubleProperty(prodPrice);
|
||||||
this.categoryId = new SimpleIntegerProperty(categoryId);
|
this.categoryId = new SimpleIntegerProperty(categoryId);
|
||||||
this.categoryName = new SimpleStringProperty(categoryName);
|
this.categoryName = new SimpleStringProperty(categoryName);
|
||||||
this.prodDesc = new SimpleStringProperty(prodDesc);
|
this.prodDesc = new SimpleStringProperty(prodDesc);
|
||||||
|
this.imageUrl = new SimpleStringProperty(imageUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
//getter and setters
|
//getter and setters
|
||||||
@@ -99,6 +101,18 @@ public class ProductDTO {
|
|||||||
this.categoryId.set(categoryId);
|
this.categoryId.set(categoryId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getImageUrl() {
|
||||||
|
return imageUrl.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SimpleStringProperty imageUrlProperty() {
|
||||||
|
return imageUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setImageUrl(String imageUrl) {
|
||||||
|
this.imageUrl.set(imageUrl);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts DTO into product for editing and deleting
|
* Converts DTO into product for editing and deleting
|
||||||
* @return
|
* @return
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -154,4 +203,4 @@ public class Validator {
|
|||||||
|
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ public class ApiClient {
|
|||||||
} else if (statusCode == 403) {
|
} else if (statusCode == 403) {
|
||||||
throw new RuntimeException("Access restricted. You don't have permission to perform this action.");
|
throw new RuntimeException("Access restricted. You don't have permission to perform this action.");
|
||||||
} else if (statusCode == 404) {
|
} else if (statusCode == 404) {
|
||||||
throw new RuntimeException("Avatar not found.");
|
throw new RuntimeException("File not found.");
|
||||||
} else {
|
} else {
|
||||||
throw new RuntimeException("Request failed with status " + statusCode);
|
throw new RuntimeException("Request failed with status " + statusCode);
|
||||||
}
|
}
|
||||||
@@ -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) {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import org.example.petshopdesktop.api.dto.pet.PetResponse;
|
|||||||
|
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class PetApi {
|
public class PetApi {
|
||||||
@@ -47,6 +48,18 @@ public class PetApi {
|
|||||||
return apiClient.put("/api/v1/pets/" + id, request, PetResponse.class);
|
return apiClient.put("/api/v1/pets/" + id, request, PetResponse.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PetResponse uploadPetImage(Long id, Path imagePath) throws Exception {
|
||||||
|
return apiClient.postMultipart("/api/v1/pets/" + id + "/image", "image", imagePath, PetResponse.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deletePetImage(Long id) throws Exception {
|
||||||
|
apiClient.delete("/api/v1/pets/" + id + "/image");
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getPetImage(Long id) throws Exception {
|
||||||
|
return apiClient.getBytes("/api/v1/pets/" + id + "/image");
|
||||||
|
}
|
||||||
|
|
||||||
public void deletePets(List<Long> ids) throws Exception {
|
public void deletePets(List<Long> ids) throws Exception {
|
||||||
apiClient.deleteWithBody("/api/v1/pets", new BulkDeleteRequest(ids));
|
apiClient.deleteWithBody("/api/v1/pets", new BulkDeleteRequest(ids));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import org.example.petshopdesktop.api.dto.product.ProductResponse;
|
|||||||
|
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class ProductApi {
|
public class ProductApi {
|
||||||
@@ -47,6 +48,18 @@ public class ProductApi {
|
|||||||
return apiClient.put("/api/v1/products/" + id, request, ProductResponse.class);
|
return apiClient.put("/api/v1/products/" + id, request, ProductResponse.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ProductResponse uploadProductImage(Long id, Path imagePath) throws Exception {
|
||||||
|
return apiClient.postMultipart("/api/v1/products/" + id + "/image", "image", imagePath, ProductResponse.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteProductImage(Long id) throws Exception {
|
||||||
|
apiClient.delete("/api/v1/products/" + id + "/image");
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getProductImage(Long id) throws Exception {
|
||||||
|
return apiClient.getBytes("/api/v1/products/" + id + "/image");
|
||||||
|
}
|
||||||
|
|
||||||
public void deleteProducts(List<Long> ids) throws Exception {
|
public void deleteProducts(List<Long> ids) throws Exception {
|
||||||
apiClient.deleteWithBody("/api/v1/products", new BulkDeleteRequest(ids));
|
apiClient.deleteWithBody("/api/v1/products", new BulkDeleteRequest(ids));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,9 +6,12 @@ import javafx.collections.ObservableList;
|
|||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.fxml.FXMLLoader;
|
import javafx.fxml.FXMLLoader;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
import javafx.scene.control.*;
|
import javafx.scene.control.*;
|
||||||
import javafx.scene.control.cell.PropertyValueFactory;
|
import javafx.scene.control.cell.PropertyValueFactory;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
|
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;
|
||||||
@@ -16,6 +19,7 @@ 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;
|
||||||
import org.example.petshopdesktop.util.ActivityLogger;
|
import org.example.petshopdesktop.util.ActivityLogger;
|
||||||
|
import org.example.petshopdesktop.util.DesktopImageSupport;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -42,6 +46,9 @@ public class PetController {
|
|||||||
@FXML
|
@FXML
|
||||||
private TableColumn<Pet, Integer> colPetId;
|
private TableColumn<Pet, Integer> colPetId;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private TableColumn<Pet, String> colPetImage;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private TableColumn<Pet, String> colPetName;
|
private TableColumn<Pet, String> colPetName;
|
||||||
|
|
||||||
@@ -134,12 +141,14 @@ public class PetController {
|
|||||||
tvPets.getSelectionModel().setSelectionMode(javafx.scene.control.SelectionMode.MULTIPLE);
|
tvPets.getSelectionModel().setSelectionMode(javafx.scene.control.SelectionMode.MULTIPLE);
|
||||||
|
|
||||||
colPetId.setCellValueFactory(new PropertyValueFactory<Pet,Integer>("petId"));
|
colPetId.setCellValueFactory(new PropertyValueFactory<Pet,Integer>("petId"));
|
||||||
|
colPetImage.setCellValueFactory(new PropertyValueFactory<Pet, String>("imageUrl"));
|
||||||
colPetName.setCellValueFactory(new PropertyValueFactory<Pet,String>("petName"));
|
colPetName.setCellValueFactory(new PropertyValueFactory<Pet,String>("petName"));
|
||||||
colPetSpecies.setCellValueFactory(new PropertyValueFactory<Pet,String>("petSpecies"));
|
colPetSpecies.setCellValueFactory(new PropertyValueFactory<Pet,String>("petSpecies"));
|
||||||
colPetBreed.setCellValueFactory(new PropertyValueFactory<Pet,String>("petBreed"));
|
colPetBreed.setCellValueFactory(new PropertyValueFactory<Pet,String>("petBreed"));
|
||||||
colPetAge.setCellValueFactory(new PropertyValueFactory<Pet,Integer>("petAge"));
|
colPetAge.setCellValueFactory(new PropertyValueFactory<Pet,Integer>("petAge"));
|
||||||
colPetStatus.setCellValueFactory(new PropertyValueFactory<Pet,String>("petStatus"));
|
colPetStatus.setCellValueFactory(new PropertyValueFactory<Pet,String>("petStatus"));
|
||||||
colPetPrice.setCellValueFactory(new PropertyValueFactory<Pet,Double>("petPrice"));
|
colPetPrice.setCellValueFactory(new PropertyValueFactory<Pet,Double>("petPrice"));
|
||||||
|
configureImageColumn(colPetImage);
|
||||||
|
|
||||||
displayPets();
|
displayPets();
|
||||||
|
|
||||||
@@ -262,8 +271,30 @@ public class PetController {
|
|||||||
response.getPetBreed(),
|
response.getPetBreed(),
|
||||||
response.getPetAge() != null ? response.getPetAge() : 0,
|
response.getPetAge() != null ? response.getPetAge() : 0,
|
||||||
response.getPetStatus(),
|
response.getPetStatus(),
|
||||||
response.getPetPrice().doubleValue()
|
response.getPetPrice().doubleValue(),
|
||||||
|
response.getImageUrl()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void configureImageColumn(TableColumn<Pet, String> column) {
|
||||||
|
column.setCellFactory(col -> new TableCell<>() {
|
||||||
|
private final ImageView imageView = new ImageView();
|
||||||
|
private final StackPane container = new StackPane(imageView);
|
||||||
|
{
|
||||||
|
container.setAlignment(Pos.CENTER);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateItem(String item, boolean empty) {
|
||||||
|
super.updateItem(item, empty);
|
||||||
|
if (empty || item == null || item.isBlank()) {
|
||||||
|
setGraphic(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DesktopImageSupport.loadImageInto(imageView, item, 48, 48);
|
||||||
|
setGraphic(container);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,9 +6,12 @@ import javafx.collections.ObservableList;
|
|||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.fxml.FXMLLoader;
|
import javafx.fxml.FXMLLoader;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
import javafx.scene.control.*;
|
import javafx.scene.control.*;
|
||||||
import javafx.scene.control.cell.PropertyValueFactory;
|
import javafx.scene.control.cell.PropertyValueFactory;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
|
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.DTOs.ProductDTO;
|
import org.example.petshopdesktop.DTOs.ProductDTO;
|
||||||
@@ -16,6 +19,7 @@ import org.example.petshopdesktop.api.dto.product.ProductResponse;
|
|||||||
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;
|
||||||
|
import org.example.petshopdesktop.util.DesktopImageSupport;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -46,6 +50,9 @@ public class ProductController {
|
|||||||
@FXML
|
@FXML
|
||||||
private TableColumn<ProductDTO, Integer> colProductId;
|
private TableColumn<ProductDTO, Integer> colProductId;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private TableColumn<ProductDTO, String> colProductImage;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private TableColumn<ProductDTO, String> colProductName;
|
private TableColumn<ProductDTO, String> colProductName;
|
||||||
|
|
||||||
@@ -74,10 +81,12 @@ public class ProductController {
|
|||||||
tvProducts.getSelectionModel().setSelectionMode(javafx.scene.control.SelectionMode.MULTIPLE);
|
tvProducts.getSelectionModel().setSelectionMode(javafx.scene.control.SelectionMode.MULTIPLE);
|
||||||
//set up table columns
|
//set up table columns
|
||||||
colProductId.setCellValueFactory(new PropertyValueFactory<ProductDTO,Integer>("prodId"));
|
colProductId.setCellValueFactory(new PropertyValueFactory<ProductDTO,Integer>("prodId"));
|
||||||
|
colProductImage.setCellValueFactory(new PropertyValueFactory<ProductDTO,String>("imageUrl"));
|
||||||
colProductName.setCellValueFactory(new PropertyValueFactory<ProductDTO,String>("prodName"));
|
colProductName.setCellValueFactory(new PropertyValueFactory<ProductDTO,String>("prodName"));
|
||||||
colProductPrice.setCellValueFactory(new PropertyValueFactory<ProductDTO,Double>("prodPrice"));
|
colProductPrice.setCellValueFactory(new PropertyValueFactory<ProductDTO,Double>("prodPrice"));
|
||||||
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);
|
||||||
|
|
||||||
displayProduct();
|
displayProduct();
|
||||||
|
|
||||||
@@ -292,8 +301,30 @@ public class ProductController {
|
|||||||
response.getProdPrice().doubleValue(),
|
response.getProdPrice().doubleValue(),
|
||||||
0,
|
0,
|
||||||
response.getCategoryName(),
|
response.getCategoryName(),
|
||||||
response.getProdDesc()
|
response.getProdDesc(),
|
||||||
|
response.getImageUrl()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void configureImageColumn(TableColumn<ProductDTO, String> column) {
|
||||||
|
column.setCellFactory(col -> new TableCell<>() {
|
||||||
|
private final ImageView imageView = new ImageView();
|
||||||
|
private final StackPane container = new StackPane(imageView);
|
||||||
|
{
|
||||||
|
container.setAlignment(Pos.CENTER);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateItem(String item, boolean empty) {
|
||||||
|
super.updateItem(item, empty);
|
||||||
|
if (empty || item == null || item.isBlank()) {
|
||||||
|
setGraphic(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DesktopImageSupport.loadImageInto(imageView, item, 48, 48);
|
||||||
|
setGraphic(container);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import javafx.event.EventHandler;
|
|||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.control.*;
|
import javafx.scene.control.*;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
import javafx.scene.input.MouseEvent;
|
import javafx.scene.input.MouseEvent;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import org.example.petshopdesktop.Validator;
|
import org.example.petshopdesktop.Validator;
|
||||||
@@ -14,8 +15,12 @@ import org.example.petshopdesktop.api.dto.pet.PetResponse;
|
|||||||
import org.example.petshopdesktop.api.endpoints.PetApi;
|
import org.example.petshopdesktop.api.endpoints.PetApi;
|
||||||
import org.example.petshopdesktop.models.Pet;
|
import org.example.petshopdesktop.models.Pet;
|
||||||
import org.example.petshopdesktop.util.ActivityLogger;
|
import org.example.petshopdesktop.util.ActivityLogger;
|
||||||
|
import org.example.petshopdesktop.util.DesktopImageSupport;
|
||||||
|
import org.example.petshopdesktop.util.FilePickerSupport;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
public class PetDialogController {
|
public class PetDialogController {
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
@@ -24,6 +29,12 @@ public class PetDialogController {
|
|||||||
@FXML
|
@FXML
|
||||||
private Button btnSave;
|
private Button btnSave;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Button btnChangeImage;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Button btnRemoveImage;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private ComboBox<String> cbPetStatus;
|
private ComboBox<String> cbPetStatus;
|
||||||
|
|
||||||
@@ -33,6 +44,12 @@ public class PetDialogController {
|
|||||||
@FXML
|
@FXML
|
||||||
private Label lblPetId;
|
private Label lblPetId;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label lblImageStatus;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ImageView imgPetPreview;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private TextField txtPetAge;
|
private TextField txtPetAge;
|
||||||
|
|
||||||
@@ -49,6 +66,9 @@ public class PetDialogController {
|
|||||||
private TextField txtPetSpecies;
|
private TextField txtPetSpecies;
|
||||||
|
|
||||||
private String mode = null;
|
private String mode = null;
|
||||||
|
private File selectedImageFile;
|
||||||
|
private String currentImageUrl;
|
||||||
|
private boolean removeImageRequested;
|
||||||
|
|
||||||
private ObservableList<String> statusList = FXCollections.observableArrayList(
|
private ObservableList<String> statusList = FXCollections.observableArrayList(
|
||||||
"Available", "Adopted"
|
"Available", "Adopted"
|
||||||
@@ -73,6 +93,10 @@ public class PetDialogController {
|
|||||||
closeStage(mouseEvent);
|
closeStage(mouseEvent);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
btnChangeImage.setOnMouseClicked(mouseEvent -> handleChangeImage());
|
||||||
|
btnRemoveImage.setOnMouseClicked(mouseEvent -> handleRemoveImage());
|
||||||
|
refreshImagePreview();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void buttonSaveClicked(MouseEvent mouseEvent) {
|
private void buttonSaveClicked(MouseEvent mouseEvent) {
|
||||||
@@ -97,13 +121,14 @@ 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();
|
||||||
try {
|
try {
|
||||||
if(mode.equals("Add")) {
|
if(mode.equals("Add")) {
|
||||||
PetApi.getInstance().createPet(request);
|
PetResponse response = PetApi.getInstance().createPet(request);
|
||||||
|
applyImageChanges(response.getPetId());
|
||||||
} else {
|
} else {
|
||||||
String[] parts = lblPetId.getText().split(": ");
|
String[] parts = lblPetId.getText().split(": ");
|
||||||
if (parts.length < 2) {
|
if (parts.length < 2) {
|
||||||
@@ -111,6 +136,7 @@ public class PetDialogController {
|
|||||||
}
|
}
|
||||||
Long petId = Long.parseLong(parts[1]);
|
Long petId = Long.parseLong(parts[1]);
|
||||||
PetApi.getInstance().updatePet(petId, request);
|
PetApi.getInstance().updatePet(petId, request);
|
||||||
|
applyImageChanges(petId);
|
||||||
}
|
}
|
||||||
|
|
||||||
//tell the user operation was successful
|
//tell the user operation was successful
|
||||||
@@ -175,6 +201,10 @@ public class PetDialogController {
|
|||||||
txtPetBreed.setText(pet.getPetBreed());
|
txtPetBreed.setText(pet.getPetBreed());
|
||||||
txtPetAge.setText(pet.getPetAge() + "");
|
txtPetAge.setText(pet.getPetAge() + "");
|
||||||
txtPetPrice.setText(pet.getPetPrice() + "");
|
txtPetPrice.setText(pet.getPetPrice() + "");
|
||||||
|
currentImageUrl = pet.getImageUrl();
|
||||||
|
selectedImageFile = null;
|
||||||
|
removeImageRequested = false;
|
||||||
|
refreshImagePreview();
|
||||||
|
|
||||||
//get the right combobox selection
|
//get the right combobox selection
|
||||||
for (String status : cbPetStatus.getItems()) {
|
for (String status : cbPetStatus.getItems()) {
|
||||||
@@ -192,10 +222,76 @@ public class PetDialogController {
|
|||||||
lblMode.setText(mode + " Pet");
|
lblMode.setText(mode + " Pet");
|
||||||
if(mode.equals("Add")) {
|
if(mode.equals("Add")) {
|
||||||
lblPetId.setVisible(false);
|
lblPetId.setVisible(false);
|
||||||
|
currentImageUrl = null;
|
||||||
|
selectedImageFile = null;
|
||||||
|
removeImageRequested = false;
|
||||||
|
refreshImagePreview();
|
||||||
}
|
}
|
||||||
else if(mode.equals("Edit")) {
|
else if(mode.equals("Edit")) {
|
||||||
lblPetId.setVisible(true);
|
lblPetId.setVisible(true);
|
||||||
|
refreshImagePreview();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleChangeImage() {
|
||||||
|
File file = FilePickerSupport.pickImageFile(btnSave.getScene().getWindow());
|
||||||
|
if (file == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
selectedImageFile = file;
|
||||||
|
removeImageRequested = false;
|
||||||
|
lblImageStatus.setText("Selected: " + file.getName());
|
||||||
|
DesktopImageSupport.loadImageInto(imgPetPreview, file.toURI().toString(), 120, 120);
|
||||||
|
btnRemoveImage.setDisable(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleRemoveImage() {
|
||||||
|
selectedImageFile = null;
|
||||||
|
removeImageRequested = true;
|
||||||
|
currentImageUrl = null;
|
||||||
|
refreshImagePreview();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyImageChanges(Long petId) throws Exception {
|
||||||
|
String previousImageUrl = currentImageUrl;
|
||||||
|
if (removeImageRequested) {
|
||||||
|
try {
|
||||||
|
PetApi.getInstance().deletePetImage(petId);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (selectedImageFile != null) {
|
||||||
|
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() {
|
||||||
|
if (imgPetPreview == null || lblImageStatus == null || btnRemoveImage == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
imgPetPreview.setImage(null);
|
||||||
|
if (selectedImageFile != null) {
|
||||||
|
lblImageStatus.setText("Selected: " + selectedImageFile.getName());
|
||||||
|
DesktopImageSupport.loadImageInto(imgPetPreview, selectedImageFile.toURI().toString(), 120, 120);
|
||||||
|
btnRemoveImage.setDisable(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (currentImageUrl != null && !currentImageUrl.isBlank()) {
|
||||||
|
lblImageStatus.setText("Current image loaded");
|
||||||
|
DesktopImageSupport.loadImageInto(imgPetPreview, currentImageUrl, 120, 120);
|
||||||
|
btnRemoveImage.setDisable(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
lblImageStatus.setText("No image selected");
|
||||||
|
btnRemoveImage.setDisable(true);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,16 +6,21 @@ import javafx.event.EventHandler;
|
|||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.control.*;
|
import javafx.scene.control.*;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
import javafx.scene.input.MouseEvent;
|
import javafx.scene.input.MouseEvent;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import org.example.petshopdesktop.DTOs.ProductDTO;
|
import org.example.petshopdesktop.DTOs.ProductDTO;
|
||||||
import org.example.petshopdesktop.Validator;
|
import org.example.petshopdesktop.Validator;
|
||||||
import org.example.petshopdesktop.api.dto.common.DropdownOption;
|
import org.example.petshopdesktop.api.dto.common.DropdownOption;
|
||||||
import org.example.petshopdesktop.api.dto.product.ProductRequest;
|
import org.example.petshopdesktop.api.dto.product.ProductRequest;
|
||||||
|
import org.example.petshopdesktop.api.dto.product.ProductResponse;
|
||||||
import org.example.petshopdesktop.api.endpoints.DropdownApi;
|
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.util.ActivityLogger;
|
import org.example.petshopdesktop.util.ActivityLogger;
|
||||||
|
import org.example.petshopdesktop.util.DesktopImageSupport;
|
||||||
|
import org.example.petshopdesktop.util.FilePickerSupport;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -27,6 +32,12 @@ public class ProductDialogController {
|
|||||||
@FXML
|
@FXML
|
||||||
private Button btnSave;
|
private Button btnSave;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Button btnChangeImage;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Button btnRemoveImage;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private ComboBox<DropdownOption> cbProdCategory;
|
private ComboBox<DropdownOption> cbProdCategory;
|
||||||
|
|
||||||
@@ -36,6 +47,12 @@ public class ProductDialogController {
|
|||||||
@FXML
|
@FXML
|
||||||
private Label lblProdId;
|
private Label lblProdId;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label lblImageStatus;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ImageView imgProductPreview;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private TextField txtProdDesc;
|
private TextField txtProdDesc;
|
||||||
|
|
||||||
@@ -46,6 +63,9 @@ public class ProductDialogController {
|
|||||||
private TextField txtProdPrice;
|
private TextField txtProdPrice;
|
||||||
|
|
||||||
private String mode = null;
|
private String mode = null;
|
||||||
|
private File selectedImageFile;
|
||||||
|
private String currentImageUrl;
|
||||||
|
private boolean removeImageRequested;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add event listeners to buttons when dialog loads
|
* Add event listeners to buttons when dialog loads
|
||||||
@@ -82,6 +102,10 @@ public class ProductDialogController {
|
|||||||
System.out.println("Error loading categories: " + e.getMessage());
|
System.out.println("Error loading categories: " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
btnChangeImage.setOnMouseClicked(mouseEvent -> handleChangeImage());
|
||||||
|
btnRemoveImage.setOnMouseClicked(mouseEvent -> handleRemoveImage());
|
||||||
|
refreshImagePreview();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -106,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 {
|
||||||
@@ -123,7 +147,8 @@ public class ProductDialogController {
|
|||||||
request.setProdDesc(txtProdDesc.getText());
|
request.setProdDesc(txtProdDesc.getText());
|
||||||
|
|
||||||
if (mode.equals("Add")) {
|
if (mode.equals("Add")) {
|
||||||
ProductApi.getInstance().createProduct(request);
|
ProductResponse response = ProductApi.getInstance().createProduct(request);
|
||||||
|
applyImageChanges(response.getProdId());
|
||||||
} else {
|
} else {
|
||||||
String[] parts = lblProdId.getText().split(": ");
|
String[] parts = lblProdId.getText().split(": ");
|
||||||
if (parts.length < 2) {
|
if (parts.length < 2) {
|
||||||
@@ -131,6 +156,7 @@ public class ProductDialogController {
|
|||||||
}
|
}
|
||||||
Long productId = Long.parseLong(parts[1]);
|
Long productId = Long.parseLong(parts[1]);
|
||||||
ProductApi.getInstance().updateProduct(productId, request);
|
ProductApi.getInstance().updateProduct(productId, request);
|
||||||
|
applyImageChanges(productId);
|
||||||
}
|
}
|
||||||
|
|
||||||
Alert alert = new Alert(Alert.AlertType.INFORMATION);
|
Alert alert = new Alert(Alert.AlertType.INFORMATION);
|
||||||
@@ -167,6 +193,10 @@ public class ProductDialogController {
|
|||||||
txtProdName.setText(product.getProdName());
|
txtProdName.setText(product.getProdName());
|
||||||
txtProdDesc.setText(product.getProdDesc());
|
txtProdDesc.setText(product.getProdDesc());
|
||||||
txtProdPrice.setText(product.getProdPrice() + "");
|
txtProdPrice.setText(product.getProdPrice() + "");
|
||||||
|
currentImageUrl = product.getImageUrl();
|
||||||
|
selectedImageFile = null;
|
||||||
|
removeImageRequested = false;
|
||||||
|
refreshImagePreview();
|
||||||
|
|
||||||
for (DropdownOption category : cbProdCategory.getItems()) {
|
for (DropdownOption category : cbProdCategory.getItems()) {
|
||||||
if(category.getLabel().equals(product.getCategoryName())){
|
if(category.getLabel().equals(product.getCategoryName())){
|
||||||
@@ -197,10 +227,76 @@ public class ProductDialogController {
|
|||||||
lblMode.setText(mode + " Product");
|
lblMode.setText(mode + " Product");
|
||||||
if(mode.equals("Add")) {
|
if(mode.equals("Add")) {
|
||||||
lblProdId.setVisible(false);
|
lblProdId.setVisible(false);
|
||||||
|
currentImageUrl = null;
|
||||||
|
selectedImageFile = null;
|
||||||
|
removeImageRequested = false;
|
||||||
|
refreshImagePreview();
|
||||||
}
|
}
|
||||||
else if(mode.equals("Edit")) {
|
else if(mode.equals("Edit")) {
|
||||||
lblProdId.setVisible(true);
|
lblProdId.setVisible(true);
|
||||||
|
refreshImagePreview();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleChangeImage() {
|
||||||
|
File file = FilePickerSupport.pickImageFile(btnSave.getScene().getWindow());
|
||||||
|
if (file == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
selectedImageFile = file;
|
||||||
|
removeImageRequested = false;
|
||||||
|
lblImageStatus.setText("Selected: " + file.getName());
|
||||||
|
DesktopImageSupport.loadImageInto(imgProductPreview, file.toURI().toString(), 120, 120);
|
||||||
|
btnRemoveImage.setDisable(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleRemoveImage() {
|
||||||
|
selectedImageFile = null;
|
||||||
|
removeImageRequested = true;
|
||||||
|
currentImageUrl = null;
|
||||||
|
refreshImagePreview();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyImageChanges(Long productId) throws Exception {
|
||||||
|
String previousImageUrl = currentImageUrl;
|
||||||
|
if (removeImageRequested) {
|
||||||
|
try {
|
||||||
|
ProductApi.getInstance().deleteProductImage(productId);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (selectedImageFile != null) {
|
||||||
|
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() {
|
||||||
|
if (imgProductPreview == null || lblImageStatus == null || btnRemoveImage == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
imgProductPreview.setImage(null);
|
||||||
|
if (selectedImageFile != null) {
|
||||||
|
lblImageStatus.setText("Selected: " + selectedImageFile.getName());
|
||||||
|
DesktopImageSupport.loadImageInto(imgProductPreview, selectedImageFile.toURI().toString(), 120, 120);
|
||||||
|
btnRemoveImage.setDisable(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (currentImageUrl != null && !currentImageUrl.isBlank()) {
|
||||||
|
lblImageStatus.setText("Current image loaded");
|
||||||
|
DesktopImageSupport.loadImageInto(imgProductPreview, currentImageUrl, 120, 120);
|
||||||
|
btnRemoveImage.setDisable(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
lblImageStatus.setText("No image selected");
|
||||||
|
btnRemoveImage.setDisable(true);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,8 +12,9 @@ public class Pet {
|
|||||||
private SimpleIntegerProperty petAge;
|
private SimpleIntegerProperty petAge;
|
||||||
private SimpleStringProperty petStatus;
|
private SimpleStringProperty petStatus;
|
||||||
private SimpleDoubleProperty petPrice;
|
private SimpleDoubleProperty petPrice;
|
||||||
|
private SimpleStringProperty imageUrl;
|
||||||
|
|
||||||
public Pet(int petId, String petName, String petSpecies, String petBreed, int petAge, String petStatus, double petPrice) {
|
public Pet(int petId, String petName, String petSpecies, String petBreed, int petAge, String petStatus, double petPrice, String imageUrl) {
|
||||||
this.petId = new SimpleIntegerProperty(petId);
|
this.petId = new SimpleIntegerProperty(petId);
|
||||||
this.petName = new SimpleStringProperty(petName);
|
this.petName = new SimpleStringProperty(petName);
|
||||||
this.petSpecies = new SimpleStringProperty(petSpecies);
|
this.petSpecies = new SimpleStringProperty(petSpecies);
|
||||||
@@ -21,6 +22,7 @@ public class Pet {
|
|||||||
this.petAge = new SimpleIntegerProperty(petAge);
|
this.petAge = new SimpleIntegerProperty(petAge);
|
||||||
this.petStatus = new SimpleStringProperty(petStatus);
|
this.petStatus = new SimpleStringProperty(petStatus);
|
||||||
this.petPrice = new SimpleDoubleProperty(petPrice);
|
this.petPrice = new SimpleDoubleProperty(petPrice);
|
||||||
|
this.imageUrl = new SimpleStringProperty(imageUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getPetId() {
|
public int getPetId() {
|
||||||
@@ -106,4 +108,16 @@ public class Pet {
|
|||||||
public SimpleDoubleProperty petPriceProperty() {
|
public SimpleDoubleProperty petPriceProperty() {
|
||||||
return petPrice;
|
return petPrice;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getImageUrl() {
|
||||||
|
return imageUrl.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setImageUrl(String imageUrl) {
|
||||||
|
this.imageUrl.set(imageUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SimpleStringProperty imageUrlProperty() {
|
||||||
|
return imageUrl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
package org.example.petshopdesktop.util;
|
||||||
|
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
|
import org.example.petshopdesktop.api.ApiClient;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
public final class DesktopImageSupport {
|
||||||
|
|
||||||
|
private static final Map<String, Image> IMAGE_CACHE = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
private DesktopImageSupport() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void loadImageInto(ImageView imageView, String imageUrl, double width, double height) {
|
||||||
|
imageView.setFitWidth(width);
|
||||||
|
imageView.setFitHeight(height);
|
||||||
|
imageView.setPreserveRatio(true);
|
||||||
|
imageView.setSmooth(true);
|
||||||
|
imageView.setImage(null);
|
||||||
|
|
||||||
|
if (imageUrl == null || imageUrl.isBlank()) {
|
||||||
|
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);
|
||||||
|
if (cached != null) {
|
||||||
|
imageView.setImage(cached);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
byte[] bytes = ApiClient.getInstance().getBytes(imageUrl);
|
||||||
|
Image image = new Image(new ByteArrayInputStream(bytes));
|
||||||
|
if (!image.isError()) {
|
||||||
|
IMAGE_CACHE.put(imageUrl, image);
|
||||||
|
Platform.runLater(() -> imageView.setImage(image));
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}, "desktop-image-loader").start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void evict(String imageUrl) {
|
||||||
|
if (imageUrl != null && !imageUrl.isBlank()) {
|
||||||
|
IMAGE_CACHE.remove(imageUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,15 +5,15 @@
|
|||||||
<?import javafx.scene.control.ComboBox?>
|
<?import javafx.scene.control.ComboBox?>
|
||||||
<?import javafx.scene.control.Label?>
|
<?import javafx.scene.control.Label?>
|
||||||
<?import javafx.scene.control.TextField?>
|
<?import javafx.scene.control.TextField?>
|
||||||
|
<?import javafx.scene.image.ImageView?>
|
||||||
<?import javafx.scene.layout.ColumnConstraints?>
|
<?import javafx.scene.layout.ColumnConstraints?>
|
||||||
<?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>
|
||||||
@@ -62,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>
|
||||||
@@ -163,6 +158,22 @@
|
|||||||
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0" />
|
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0" />
|
||||||
</VBox.margin>
|
</VBox.margin>
|
||||||
</GridPane>
|
</GridPane>
|
||||||
|
<HBox alignment="CENTER_LEFT" spacing="15.0">
|
||||||
|
<children>
|
||||||
|
<ImageView fx:id="imgPetPreview" fitHeight="120.0" fitWidth="120.0" pickOnBounds="true" preserveRatio="true" />
|
||||||
|
<VBox spacing="10.0">
|
||||||
|
<children>
|
||||||
|
<Label fx:id="lblImageStatus" text="No image selected" textFill="#2c3e50" />
|
||||||
|
<HBox spacing="10.0">
|
||||||
|
<children>
|
||||||
|
<Button fx:id="btnChangeImage" mnemonicParsing="false" text="Change Image" />
|
||||||
|
<Button fx:id="btnRemoveImage" mnemonicParsing="false" text="Remove Image" />
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
</children>
|
||||||
|
</VBox>
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
</children>
|
</children>
|
||||||
<padding>
|
<padding>
|
||||||
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0" />
|
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0" />
|
||||||
|
|||||||
@@ -5,15 +5,15 @@
|
|||||||
<?import javafx.scene.control.ComboBox?>
|
<?import javafx.scene.control.ComboBox?>
|
||||||
<?import javafx.scene.control.Label?>
|
<?import javafx.scene.control.Label?>
|
||||||
<?import javafx.scene.control.TextField?>
|
<?import javafx.scene.control.TextField?>
|
||||||
|
<?import javafx.scene.image.ImageView?>
|
||||||
<?import javafx.scene.layout.ColumnConstraints?>
|
<?import javafx.scene.layout.ColumnConstraints?>
|
||||||
<?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>
|
||||||
@@ -62,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">
|
||||||
@@ -136,8 +131,24 @@
|
|||||||
<VBox.margin>
|
<VBox.margin>
|
||||||
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0" />
|
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0" />
|
||||||
</VBox.margin>
|
</VBox.margin>
|
||||||
</GridPane>
|
</GridPane>
|
||||||
</children>
|
<HBox alignment="CENTER_LEFT" spacing="15.0">
|
||||||
|
<children>
|
||||||
|
<ImageView fx:id="imgProductPreview" fitHeight="120.0" fitWidth="120.0" pickOnBounds="true" preserveRatio="true" />
|
||||||
|
<VBox spacing="10.0">
|
||||||
|
<children>
|
||||||
|
<Label fx:id="lblImageStatus" text="No image selected" textFill="#2c3e50" />
|
||||||
|
<HBox spacing="10.0">
|
||||||
|
<children>
|
||||||
|
<Button fx:id="btnChangeImage" mnemonicParsing="false" text="Change Image" />
|
||||||
|
<Button fx:id="btnRemoveImage" mnemonicParsing="false" text="Remove Image" />
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
</children>
|
||||||
|
</VBox>
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
</children>
|
||||||
<padding>
|
<padding>
|
||||||
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0" />
|
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0" />
|
||||||
</padding>
|
</padding>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
<?import javafx.scene.control.TableColumn?>
|
<?import javafx.scene.control.TableColumn?>
|
||||||
<?import javafx.scene.control.TableView?>
|
<?import javafx.scene.control.TableView?>
|
||||||
<?import javafx.scene.control.TextField?>
|
<?import javafx.scene.control.TextField?>
|
||||||
|
<?import javafx.scene.image.ImageView?>
|
||||||
<?import javafx.scene.layout.HBox?>
|
<?import javafx.scene.layout.HBox?>
|
||||||
<?import javafx.scene.layout.Region?>
|
<?import javafx.scene.layout.Region?>
|
||||||
<?import javafx.scene.layout.VBox?>
|
<?import javafx.scene.layout.VBox?>
|
||||||
@@ -67,14 +68,15 @@
|
|||||||
</HBox>
|
</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="60.0" text="ID" />
|
<TableColumn fx:id="colPetId" prefWidth="55.0" text="ID" />
|
||||||
<TableColumn fx:id="colPetName" prefWidth="113.14285278320312" text="Name" />
|
<TableColumn fx:id="colPetImage" prefWidth="80.0" text="Image" />
|
||||||
<TableColumn fx:id="colPetSpecies" prefWidth="110.28570556640625" text="Species" />
|
<TableColumn fx:id="colPetName" prefWidth="110.0" text="Name" />
|
||||||
<TableColumn fx:id="colPetBreed" prefWidth="174.85711669921875" text="Breed" />
|
<TableColumn fx:id="colPetSpecies" prefWidth="105.0" text="Species" />
|
||||||
<TableColumn fx:id="colPetAge" prefWidth="72.0" text="Age" />
|
<TableColumn fx:id="colPetBreed" prefWidth="145.0" text="Breed" />
|
||||||
<TableColumn fx:id="colPetStatus" prefWidth="133.142822265625" text="Status" />
|
<TableColumn fx:id="colPetAge" prefWidth="60.0" text="Age" />
|
||||||
<TableColumn fx:id="colPetPrice" prefWidth="89.142822265625" text="Price" />
|
<TableColumn fx:id="colPetStatus" prefWidth="110.0" text="Status" />
|
||||||
</columns>
|
<TableColumn fx:id="colPetPrice" prefWidth="80.0" text="Price" />
|
||||||
|
</columns>
|
||||||
</TableView>
|
</TableView>
|
||||||
</children>
|
</children>
|
||||||
</VBox>
|
</VBox>
|
||||||
|
|||||||
@@ -67,12 +67,13 @@
|
|||||||
</HBox>
|
</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="60.0" text="ID" />
|
<TableColumn fx:id="colProductId" prefWidth="55.0" text="ID" />
|
||||||
<TableColumn fx:id="colProductName" prefWidth="170.85714721679688" text="Name" />
|
<TableColumn fx:id="colProductImage" prefWidth="80.0" text="Image" />
|
||||||
<TableColumn fx:id="colProductCategory" prefWidth="195.4285888671875" text="Category" />
|
<TableColumn fx:id="colProductName" prefWidth="150.0" text="Name" />
|
||||||
<TableColumn fx:id="colProductDesc" prefWidth="210.28570556640625" text="Description" />
|
<TableColumn fx:id="colProductCategory" prefWidth="160.0" text="Category" />
|
||||||
<TableColumn fx:id="colProductPrice" prefWidth="115.4285888671875" text="Price" />
|
<TableColumn fx:id="colProductDesc" prefWidth="195.0" text="Description" />
|
||||||
</columns>
|
<TableColumn fx:id="colProductPrice" prefWidth="110.0" text="Price" />
|
||||||
|
</columns>
|
||||||
</TableView>
|
</TableView>
|
||||||
</children>
|
</children>
|
||||||
</VBox>
|
</VBox>
|
||||||
|
|||||||
Reference in New Issue
Block a user