diff --git a/src/main/java/org/example/petshopdesktop/controllers/AdoptionController.java b/src/main/java/org/example/petshopdesktop/controllers/AdoptionController.java index f59c8e8d..e5b783eb 100644 --- a/src/main/java/org/example/petshopdesktop/controllers/AdoptionController.java +++ b/src/main/java/org/example/petshopdesktop/controllers/AdoptionController.java @@ -1,11 +1,23 @@ package org.example.petshopdesktop.controllers; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; import javafx.event.ActionEvent; import javafx.fxml.FXML; -import javafx.scene.control.Button; -import javafx.scene.control.TableColumn; -import javafx.scene.control.TableView; -import javafx.scene.control.TextField; +import javafx.fxml.FXMLLoader; +import javafx.scene.Scene; +import javafx.scene.control.*; +import javafx.scene.control.cell.PropertyValueFactory; +import javafx.stage.Modality; +import javafx.stage.Stage; +import org.example.petshopdesktop.controllers.dialogcontrollers.AdoptionDialogController; +import org.example.petshopdesktop.database.AdoptionDB; +import org.example.petshopdesktop.models.Adoption; + +import java.io.IOException; +import java.sql.SQLException; +import java.sql.SQLIntegrityConstraintViolationException; +import java.util.Optional; public class AdoptionController { @@ -19,42 +31,167 @@ public class AdoptionController { private Button btnEdit; @FXML - private TableColumn colAdoptionDate; + private TableColumn colAdoptionId; @FXML - private TableColumn colAdoptionFee; + private TableColumn colPetId; @FXML - private TableColumn colAdoptionId; + private TableColumn colCustomerName; @FXML - private TableColumn colAdoptionStatus; + private TableColumn colAdoptionDate; @FXML - private TableColumn colCustomerName; + private TableColumn colAdoptionFee; @FXML - private TableColumn colPetId; + private TableColumn colAdoptionStatus; @FXML - private TableView tvAdoptions; + private TableView tvAdoptions; @FXML private TextField txtSearch; + private ObservableList data = FXCollections.observableArrayList(); + private String mode = null; + + @FXML + void initialize() { + btnEdit.setDisable(true); + btnDelete.setDisable(true); + + colAdoptionId.setCellValueFactory(new PropertyValueFactory<>("adoptionId")); + colPetId.setCellValueFactory(new PropertyValueFactory<>("petId")); + colCustomerName.setCellValueFactory(new PropertyValueFactory<>("customerName")); + colAdoptionDate.setCellValueFactory(new PropertyValueFactory<>("adoptionDate")); + colAdoptionFee.setCellValueFactory(new PropertyValueFactory<>("adoptionFee")); + colAdoptionStatus.setCellValueFactory(new PropertyValueFactory<>("adoptionStatus")); + + displayAdoptions(); + + tvAdoptions.getSelectionModel().selectedItemProperty().addListener( + (observable, oldValue, newValue) -> { + btnEdit.setDisable(false); + btnDelete.setDisable(false); + }); + + txtSearch.textProperty().addListener((observable, oldValue, newValue) -> { + displayFilteredAdoptions(newValue); + }); + } + @FXML void btnAddClicked(ActionEvent event) { - + mode = "Add"; + openDialog(null, mode); } @FXML void btnDeleteClicked(ActionEvent event) { + int numRows = 0; + Adoption selectedAdoption = tvAdoptions.getSelectionModel().getSelectedItem(); + Alert question = new Alert(Alert.AlertType.CONFIRMATION); + question.setHeaderText("Please confirm delete"); + question.setContentText("Are you sure you want to delete this adoption record?"); + Optional result = question.showAndWait(); + + if (result.isPresent() && result.get() == ButtonType.OK) { + int adoptionId = selectedAdoption.getAdoptionId(); + + try { + numRows = AdoptionDB.deleteAdoption(adoptionId); + } + catch (SQLIntegrityConstraintViolationException e) { + Alert alert = new Alert(Alert.AlertType.ERROR); + alert.setHeaderText("Database Operation Error"); + alert.setContentText("Delete failed\nThe selected adoption is being referred in another table"); + alert.showAndWait(); + return; + } catch (SQLException e) { + throw new RuntimeException(e); + } + + if (numRows == 0) { + Alert alert = new Alert(Alert.AlertType.ERROR); + alert.setHeaderText("Database Operation Error"); + alert.setContentText("Delete failed"); + alert.showAndWait(); + } else { + Alert alert = new Alert(Alert.AlertType.CONFIRMATION); + alert.setHeaderText("Database Operation Confirmed"); + alert.setContentText("Delete successful"); + alert.showAndWait(); + displayAdoptions(); + btnDelete.setDisable(true); + btnEdit.setDisable(true); + txtSearch.setText(""); + } + } } @FXML void btnEditClicked(ActionEvent event) { + Adoption selectedAdoption = tvAdoptions.getSelectionModel().getSelectedItem(); + if (selectedAdoption != null) { + mode = "Edit"; + openDialog(selectedAdoption, mode); + } } + private void displayFilteredAdoptions(String filter) { + data.clear(); + try { + if (txtSearch.getText() == null || txtSearch.getText().isEmpty()) { + displayAdoptions(); + } else { + data = AdoptionDB.getFilteredAdoptions(filter); + tvAdoptions.setItems(data); + } + } catch (Exception e) { + System.out.println("Error while fetching table data: " + e.getMessage()); + } + } + + private void displayAdoptions() { + data.clear(); + try { + data = AdoptionDB.getAdoptions(); + } catch (SQLException e) { + System.out.println("Error while fetching table data: " + e.getMessage()); + } + tvAdoptions.setItems(data); + } + + private void openDialog(Adoption adoption, String mode) { + FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource( + "/org/example/petshopdesktop/dialogviews/adoption-dialog-view.fxml")); + Scene scene = null; + try { + scene = new Scene(fxmlLoader.load()); + } catch (IOException e) { + throw new RuntimeException(e); + } + + AdoptionDialogController dialogController = fxmlLoader.getController(); + dialogController.setMode(mode); + + if (mode.equals("Edit")) { + dialogController.displayAdoptionDetails(adoption); + } + + Stage dialogStage = new Stage(); + dialogStage.initModality(Modality.APPLICATION_MODAL); + dialogStage.setTitle(mode.equals("Add") ? "Add Adoption" : "Edit Adoption"); + dialogStage.setScene(scene); + dialogStage.showAndWait(); + + displayAdoptions(); + btnDelete.setDisable(true); + btnEdit.setDisable(true); + txtSearch.setText(""); + } } diff --git a/src/main/java/org/example/petshopdesktop/controllers/InventoryController.java b/src/main/java/org/example/petshopdesktop/controllers/InventoryController.java index 18dbd6a4..43000c51 100644 --- a/src/main/java/org/example/petshopdesktop/controllers/InventoryController.java +++ b/src/main/java/org/example/petshopdesktop/controllers/InventoryController.java @@ -1,14 +1,27 @@ package org.example.petshopdesktop.controllers; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; import javafx.event.ActionEvent; import javafx.fxml.FXML; -import javafx.scene.control.Button; -import javafx.scene.control.TableColumn; -import javafx.scene.control.TableView; -import javafx.scene.control.TextField; +import javafx.fxml.FXMLLoader; +import javafx.scene.Scene; +import javafx.scene.control.*; +import javafx.scene.control.cell.PropertyValueFactory; +import javafx.stage.Modality; +import javafx.stage.Stage; +import org.example.petshopdesktop.controllers.dialogcontrollers.InventoryDialogController; +import org.example.petshopdesktop.database.InventoryDB; +import org.example.petshopdesktop.models.Inventory; + +import java.io.IOException; +import java.sql.SQLException; +import java.sql.SQLIntegrityConstraintViolationException; +import java.util.Optional; public class InventoryController { + //FXML elements @FXML private Button btnAdd; @@ -19,36 +32,193 @@ public class InventoryController { private Button btnEdit; @FXML - private TableColumn colInventoryId; + private TableColumn colInventoryId; @FXML - private TableColumn colProductId; + private TableColumn colProductId; @FXML - private TableColumn colProductName; + private TableColumn colProductName; @FXML - private TableColumn colQuantity; + private TableColumn colQuantity; @FXML - private TableView tvInventory; + private TableView tvInventory; @FXML private TextField txtSearch; + private ObservableList data = FXCollections.observableArrayList(); + + //Determines if in add/edit mode + private String mode = null; + + //Loads upon view bootup + @FXML + void initialize() { + //Buttons disabled until row is selected + btnEdit.setDisable(true); + btnDelete.setDisable(true); + + colInventoryId.setCellValueFactory(new PropertyValueFactory<>("inventoryId")); + colProductId.setCellValueFactory(new PropertyValueFactory<>("prodId")); + colProductName.setCellValueFactory(new PropertyValueFactory<>("prodName")); + colQuantity.setCellValueFactory(new PropertyValueFactory<>("quantity")); + + displayInventory(); + + //Enables buttons when row is selected + tvInventory.getSelectionModel().selectedItemProperty().addListener( + (observable, oldValue, newValue) -> { + btnEdit.setDisable(false); + btnDelete.setDisable(false); + }); + + //Filter as user types + txtSearch.textProperty().addListener((observable, oldValue, newValue) -> { + displayFilteredInventory(newValue); + }); + } + + //Opens dialog in add mode @FXML void btnAddClicked(ActionEvent event) { - + mode = "Add"; + openDialog(null, mode); } + //Prompts user for confirmation prior to deletion @FXML void btnDeleteClicked(ActionEvent event) { + int numRows = 0; + Inventory selectedInventory = tvInventory.getSelectionModel().getSelectedItem(); + //Confirmation popup + Alert question = new Alert(Alert.AlertType.CONFIRMATION); + question.setHeaderText("Please confirm delete"); + question.setContentText("Are you sure you want to delete this inventory record?"); + Optional result = question.showAndWait(); + + //If user confirms, proceed with trying to delete... + if (result.isPresent() && result.get() == ButtonType.OK) { + int inventoryId = selectedInventory.getInventoryId(); + + try { + numRows = InventoryDB.deleteInventory(inventoryId); + } + + catch (SQLIntegrityConstraintViolationException e) { + Alert alert = new Alert(Alert.AlertType.ERROR); + alert.setHeaderText("Database Operation Error"); + alert.setContentText("Delete failed\nThe selected inventory record is being referred in another table"); + alert.showAndWait(); + return; + } + + catch (SQLException e) { + throw new RuntimeException(e); + } + + //Checks if deletion succeeded + if (numRows == 0) { + Alert alert = new Alert(Alert.AlertType.ERROR); + alert.setHeaderText("Database Operation Error"); + alert.setContentText("Delete failed"); + alert.showAndWait(); + } + + else { + Alert alert = new Alert(Alert.AlertType.CONFIRMATION); + alert.setHeaderText("Database Operation Confirmed"); + alert.setContentText("Delete successful"); + alert.showAndWait(); + + //Refresh UI + displayInventory(); + btnDelete.setDisable(true); + btnEdit.setDisable(true); + txtSearch.setText(""); + } + } } + //Editing a record @FXML void btnEditClicked(ActionEvent event) { + Inventory selectedInventory = tvInventory.getSelectionModel().getSelectedItem(); + if (selectedInventory != null) { + mode = "Edit"; + openDialog(selectedInventory, mode); + } } + //Search filter + private void displayFilteredInventory(String filter) { + data.clear(); + try { + //If search box is empty, display all inventory + if (txtSearch.getText() == null || txtSearch.getText().isEmpty()) { + displayInventory(); + } + + else { + data = InventoryDB.getFilteredInventory(filter); + tvInventory.setItems(data); + } + } + + catch (Exception e) { + System.out.println("Error while fetching table data: " + e.getMessage()); + } + } + + //Displays all records from DB + private void displayInventory() { + data.clear(); + try { + data = InventoryDB.getInventory(); + } + + catch (SQLException e) { + System.out.println("Error while fetching table data: " + e.getMessage()); + } + tvInventory.setItems(data); + } + + //Opens inventory-dialog-view + private void openDialog(Inventory inventory, String mode) { + //Opens FXML + FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/org/example/petshopdesktop/dialogviews/inventory-dialog-view.fxml")); + Scene scene = null; + + try { + scene = new Scene(fxmlLoader.load()); + } + + catch (IOException e) { + throw new RuntimeException(e); + } + + //Passes data and mode to the view + InventoryDialogController dialogController = fxmlLoader.getController(); + dialogController.setMode(mode); + + if (mode.equals("Edit")) { + dialogController.displayInventoryDetails(inventory); + } + + Stage dialogStage = new Stage(); + dialogStage.initModality(Modality.APPLICATION_MODAL); + dialogStage.setTitle(mode.equals("Add") ? "Add Inventory" : "Edit Inventory"); + dialogStage.setScene(scene); + dialogStage.showAndWait(); + + //Refresh inventory + displayInventory(); + btnDelete.setDisable(true); + btnEdit.setDisable(true); + txtSearch.setText(""); + } } diff --git a/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/AdoptionDialogController.java b/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/AdoptionDialogController.java new file mode 100644 index 00000000..40b3c6cb --- /dev/null +++ b/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/AdoptionDialogController.java @@ -0,0 +1,266 @@ +package org.example.petshopdesktop.controllers.dialogcontrollers; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.event.EventHandler; +import javafx.fxml.FXML; +import javafx.scene.Node; +import javafx.scene.control.Alert; +import javafx.scene.control.Button; +import javafx.scene.control.ComboBox; +import javafx.scene.control.DatePicker; +import javafx.scene.control.Label; +import javafx.scene.input.MouseEvent; +import javafx.stage.Stage; +import javafx.util.StringConverter; +import org.example.petshopdesktop.database.AdoptionDB; +import org.example.petshopdesktop.database.PetDB; +import org.example.petshopdesktop.models.Adoption; +import org.example.petshopdesktop.models.Customer; +import org.example.petshopdesktop.models.Pet; + +import java.sql.SQLException; +import java.time.LocalDate; + +public class AdoptionDialogController { + + //FXML elements + @FXML + private Button btnCancel; + + @FXML + private Button btnSave; + + @FXML + private ComboBox cbAdoptionStatus; + + @FXML + private ComboBox cbCustomer; + + @FXML + private ComboBox cbPet; + + @FXML + private DatePicker dpAdoptionDate; + + @FXML + private Label lblAdoptionId; + + @FXML + private Label lblMode; + + //Stores if the dialog view is in add/edit mode + private String mode = null; + + //Adoption statuses + private ObservableList statusList = FXCollections.observableArrayList( + "Pending", "Completed", "Cancelled" + ); + + //Loads upon boot + @FXML + void initialize() { + + //Loads statusList into combo box + cbAdoptionStatus.setItems(statusList); + + //Pet objects are converted into readable text for combobox (PetID + PetName) + cbPet.setConverter(new StringConverter() { + @Override + public String toString(Pet pet) { + return pet == null ? "" : pet.getPetId() + ": " + pet.getPetName(); + } + + //Not used + @Override + public Pet fromString(String string) { return null; } + }); + + //Load pets from DB into pet combobox + try { + cbPet.setItems(PetDB.getPets()); + } + + catch (SQLException e) { + System.out.println("Error loading pets: " + e.getMessage()); + } + + //Load customers from DB into customer combobox + try { + cbCustomer.setItems(AdoptionDB.getCustomers()); + } + + catch (SQLException e) { + System.out.println("Error loading customers: " + e.getMessage()); + } + + //Save button handler + btnSave.setOnMouseClicked(new EventHandler() { + @Override + public void handle(MouseEvent mouseEvent) { + buttonSaveClicked(mouseEvent); + } + }); + + //Cancel button handler, closes dialog view + btnCancel.setOnMouseClicked(new EventHandler() { + @Override + public void handle(MouseEvent mouseEvent) { + closeStage(mouseEvent); + } + }); + } + + //Handles logic when clicking Save + private void buttonSaveClicked(MouseEvent mouseEvent) { + int numRow = 0; + String errorMsg = ""; + + //Validation: checks if anything is missing + if (cbPet.getSelectionModel().getSelectedItem() == null) { + errorMsg += "Pet is required.\n"; + } + + if (cbCustomer.getSelectionModel().getSelectedItem() == null) { + errorMsg += "Customer is required.\n"; + } + + if (dpAdoptionDate.getValue() == null) { + errorMsg += "Adoption Date is required.\n"; + } + + if (cbAdoptionStatus.getSelectionModel().getSelectedItem() == null) { + errorMsg += "Status is required.\n"; + } + + //If no errors, attempt DB operation + if (errorMsg.isEmpty()) { + Adoption adoption = collectAdoption(); + + //Try inserting into DB + if (mode.equals("Add")) { + try { + numRow = AdoptionDB.insertAdoption(adoption); + } + + catch (SQLException e) { + throw new RuntimeException(e); + } + } + + //Try updating adoption + else { + try { + numRow = AdoptionDB.updateAdoption(adoption.getAdoptionId(), adoption); + } + + catch (SQLException e) { + throw new RuntimeException(e); + } + } + + //If no rows are affected, an issue has occurred + if (numRow == 0) { + Alert alert = new Alert(Alert.AlertType.ERROR); + alert.setHeaderText("Database Operation Error"); + alert.setContentText(mode + " failed"); + alert.showAndWait(); + closeStage(mouseEvent); + } + + //DB operation worked! + else { + Alert alert = new Alert(Alert.AlertType.CONFIRMATION); + alert.setHeaderText("Database Operation Confirmed"); + alert.setContentText(mode + " succeeded"); + alert.showAndWait(); + closeStage(mouseEvent); + } + } + + //If there are errors, display them + else { + Alert alert = new Alert(Alert.AlertType.ERROR); + alert.setHeaderText("Input Error"); + alert.setContentText(errorMsg); + alert.showAndWait(); + } + } + + //Collects user input, builds an Adoption object + private Adoption collectAdoption() { + int adoptionId = 0; + + //Only grab adoption ID if in edit mode + if (lblAdoptionId.isVisible()) { + adoptionId = Integer.parseInt(lblAdoptionId.getText().split(": ")[1]); + } + + Pet selectedPet = cbPet.getSelectionModel().getSelectedItem(); + Customer selectedCustomer = cbCustomer.getSelectionModel().getSelectedItem(); + String date = dpAdoptionDate.getValue().toString(); + String status = cbAdoptionStatus.getValue(); + + return new Adoption( + adoptionId, + selectedPet.getPetId(), + selectedCustomer.getCustomerId(), + selectedCustomer.toString(), + date, + selectedPet.getPetPrice(), + status + ); + } + + private void closeStage(MouseEvent mouseEvent) { + Node node = (Node) mouseEvent.getSource(); + Stage stage = (Stage) node.getScene().getWindow(); + stage.close(); + } + + //Edit mode + //Inserts data into fields + public void displayAdoptionDetails(Adoption adoption) { + if (adoption != null) { + //Displays adoption ID + lblAdoptionId.setText("ID: " + adoption.getAdoptionId()); + + //Select pet + for (Pet pet : cbPet.getItems()) { + if (pet.getPetId() == adoption.getPetId()) { + cbPet.getSelectionModel().select(pet); + break; + } + } + + //Select customer + for (Customer customer : cbCustomer.getItems()) { + if (customer.getCustomerId() == adoption.getCustomerId()) { + cbCustomer.getSelectionModel().select(customer); + break; + } + } + + //Select adoption date + if (adoption.getAdoptionDate() != null && !adoption.getAdoptionDate().isEmpty()) { + dpAdoptionDate.setValue(LocalDate.parse(adoption.getAdoptionDate())); + } + + //Select adoption status + for (String status : cbAdoptionStatus.getItems()) { + if (status.equals(adoption.getAdoptionStatus())) { + cbAdoptionStatus.getSelectionModel().select(status); + break; + } + } + } + } + + //Sets dialog mode + //Also updates label and adoption ID visibility + public void setMode(String mode) { + this.mode = mode; + lblMode.setText(mode + " Adoption"); + lblAdoptionId.setVisible(mode.equals("Edit")); + } +} diff --git a/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/InventoryDialogController.java b/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/InventoryDialogController.java new file mode 100644 index 00000000..7b0a69c0 --- /dev/null +++ b/src/main/java/org/example/petshopdesktop/controllers/dialogcontrollers/InventoryDialogController.java @@ -0,0 +1,228 @@ +package org.example.petshopdesktop.controllers.dialogcontrollers; + +import javafx.event.EventHandler; +import javafx.fxml.FXML; +import javafx.scene.Node; +import javafx.scene.control.Alert; +import javafx.scene.control.Button; +import javafx.scene.control.ComboBox; +import javafx.scene.control.Label; +import javafx.scene.control.TextField; +import javafx.scene.input.MouseEvent; +import javafx.stage.Stage; +import javafx.util.StringConverter; +import org.example.petshopdesktop.Validator; +import org.example.petshopdesktop.database.InventoryDB; +import org.example.petshopdesktop.database.ProductDB; +import org.example.petshopdesktop.models.Inventory; +import org.example.petshopdesktop.models.Product; + +import java.sql.SQLException; + +public class InventoryDialogController { + + //FXML elements + @FXML + private Button btnCancel; + + @FXML + private Button btnSave; + + @FXML + private ComboBox cbProduct; + + @FXML + private Label lblInventoryId; + + @FXML + private Label lblMode; + + @FXML + private TextField txtQuantity; + + //Determines if the mode is add or edit + private String mode = null; + + //Loads upon .FXML boot + @FXML + void initialize() { + cbProduct.setConverter(new StringConverter() { + + //Displays product in combobox (prodID + name) + @Override + public String toString(Product product) { + return product == null ? "" : product.getProdId() + ": " + product.getProdName(); + } + + //Not needed + @Override + public Product fromString(String string) { return null; } + }); + + //Load product list from DB into combobox + try { + cbProduct.setItems(ProductDB.getProducts()); + } + + catch (SQLException e) { + System.out.println("Error loading products: " + e.getMessage()); + } + + //Save button handler + btnSave.setOnMouseClicked(new EventHandler() { + @Override + public void handle(MouseEvent mouseEvent) { + buttonSaveClicked(mouseEvent); + } + }); + + //Cancel button handler + btnCancel.setOnMouseClicked(new EventHandler() { + @Override + public void handle(MouseEvent mouseEvent) { + closeStage(mouseEvent); + } + }); + } + + //Handles save button click event + private void buttonSaveClicked(MouseEvent mouseEvent) { + int numRow = 0; + String errorMsg = ""; + + if (cbProduct.getSelectionModel().getSelectedItem() == null) { + errorMsg += "Product is required.\n"; + } + + //Validate inputs + errorMsg += Validator.isPresent(txtQuantity.getText(), "Quantity"); + errorMsg += Validator.isLessThanVarChars(txtQuantity.getText(), "Quantity", 11); + errorMsg += Validator.isNonNegativeInteger(txtQuantity.getText(), "Quantity"); + + //Operation only occurs if there are no errors + if (errorMsg.isEmpty()) { + + //Ensures duplicate entries aren't possible + if (mode.equals("Add")) { + Product selectedProduct = cbProduct.getSelectionModel().getSelectedItem(); + + try { + + if (InventoryDB.productExistsInInventory(selectedProduct.getProdId())) { + Alert alert = new Alert(Alert.AlertType.ERROR); + alert.setHeaderText("Duplicate Entry"); + alert.setContentText("An inventory record for \"" + selectedProduct.getProdName() + "\" already exists."); + alert.showAndWait(); + return; + } + } + + catch (SQLException e) { + throw new RuntimeException(e); + } + } + + Inventory inventory = collectInventory(); + + //Adding inventory + if (mode.equals("Add")) { + try { + numRow = InventoryDB.insertInventory(inventory); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + //Updating inventory + else { + try { + numRow = InventoryDB.updateInventory(inventory.getInventoryId(), inventory); + } + + catch (SQLException e) { + throw new RuntimeException(e); + } + } + + //Display database operation result + if (numRow == 0) { + Alert alert = new Alert(Alert.AlertType.ERROR); + alert.setHeaderText("Database Operation Error"); + alert.setContentText(mode + " failed"); + alert.showAndWait(); + closeStage(mouseEvent); + } + + else { + Alert alert = new Alert(Alert.AlertType.CONFIRMATION); + alert.setHeaderText("Database Operation Confirmed"); + alert.setContentText(mode + " succeeded"); + alert.showAndWait(); + closeStage(mouseEvent); + } + } + + //Displays validation errors + else { + Alert alert = new Alert(Alert.AlertType.ERROR); + alert.setHeaderText("Input Error"); + alert.setContentText(errorMsg); + alert.showAndWait(); + } + } + + //Create Inventory object using values entered by user + private Inventory collectInventory() { + int inventoryId = 0; + + //Grab inventory ID when editing pre-existing record + if (lblInventoryId.isVisible()) { + inventoryId = Integer.parseInt(lblInventoryId.getText().split(": ")[1]); + } + + //Get selected product + Product selectedProduct = cbProduct.getSelectionModel().getSelectedItem(); + + //Build and returns Inventory object + return new Inventory( + inventoryId, + selectedProduct.getProdId(), + selectedProduct.getProdName(), + Integer.parseInt(txtQuantity.getText()) + ); + } + + //Close dialog view + private void closeStage(MouseEvent mouseEvent) { + Node node = (Node) mouseEvent.getSource(); + Stage stage = (Stage) node.getScene().getWindow(); + stage.close(); + } + + //Editing + //Displays fields with existing inventory data + public void displayInventoryDetails(Inventory inventory) { + if (inventory != null) { + + //Displays inventory ID + lblInventoryId.setText("ID: " + inventory.getInventoryId()); + + //Selecting matching product in combobox + for (Product product : cbProduct.getItems()) { + if (product.getProdId() == inventory.getProdId()) { + cbProduct.getSelectionModel().select(product); + break; + } + } + + txtQuantity.setText(String.valueOf(inventory.getQuantity())); + } + } + + //Sets dialog view to add/edit. Updates UI labels + public void setMode(String mode) { + this.mode = mode; + lblMode.setText(mode + " Inventory"); + lblInventoryId.setVisible(mode.equals("Edit")); + } +} \ No newline at end of file diff --git a/src/main/java/org/example/petshopdesktop/database/AdoptionDB.java b/src/main/java/org/example/petshopdesktop/database/AdoptionDB.java new file mode 100644 index 00000000..87c087ef --- /dev/null +++ b/src/main/java/org/example/petshopdesktop/database/AdoptionDB.java @@ -0,0 +1,140 @@ +package org.example.petshopdesktop.database; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import org.example.petshopdesktop.models.Adoption; +import org.example.petshopdesktop.models.Customer; +import org.example.petshopdesktop.models.Pet; + +import java.sql.*; + +public class AdoptionDB { + + //Select query + private static final String BASE_SELECT = + "SELECT a.adoptionId, a.petId, a.customerId, " + + "CONCAT(c.firstName, ' ', c.lastName) AS customerName, " + + "a.adoptionDate, p.petPrice AS adoptionFee, a.adoptionStatus " + + "FROM adoption a " + + "JOIN customer c ON a.customerId = c.customerId " + + "JOIN pet p ON a.petId = p.petId"; + + //Retrieve all adoption records from DB + public static ObservableList getAdoptions() throws SQLException { + ObservableList adoptions = FXCollections.observableArrayList(); + Connection conn = ConnectionDB.getConnection(); + + Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery(BASE_SELECT); + + //Map results + while (rs.next()) { + adoptions.add(mapRow(rs)); + } + + conn.close(); + return adoptions; + } + + //Returns data depending on search query + public static ObservableList getFilteredAdoptions(String filter) throws SQLException { + ObservableList adoptions = FXCollections.observableArrayList(); + Connection conn = ConnectionDB.getConnection(); + + String sql = BASE_SELECT + + " WHERE a.adoptionId LIKE '%" + filter + "%' OR " + + "a.petId LIKE '%" + filter + "%' OR " + + "CONCAT(c.firstName, ' ', c.lastName) LIKE '%" + filter + "%' OR " + + "a.adoptionDate LIKE '%" + filter + "%' OR " + + "p.petPrice LIKE '%" + filter + "%' OR " + + "a.adoptionStatus LIKE '%" + filter + "%'"; + + Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery(sql); + + //Map results + while (rs.next()) { + adoptions.add(mapRow(rs)); + } + + conn.close(); + return adoptions; + } + + //Add new adoption + public static int insertAdoption(Adoption adoption) throws SQLException { + Connection conn = ConnectionDB.getConnection(); + String sql = "INSERT INTO adoption (petId, customerId, adoptionDate, adoptionStatus) VALUES (?, ?, ?, ?)"; + + PreparedStatement stmt = conn.prepareStatement(sql); + + //Put data in Adoption object + stmt.setInt(1, adoption.getPetId()); + stmt.setInt(2, adoption.getCustomerId()); + stmt.setString(3, adoption.getAdoptionDate()); + stmt.setString(4, adoption.getAdoptionStatus()); + + int numRows = stmt.executeUpdate(); + conn.close(); + return numRows; + } + + //Updating pre-existing adoption + public static int updateAdoption(int adoptionId, Adoption adoption) throws SQLException { + Connection conn = ConnectionDB.getConnection(); + String sql = "UPDATE adoption SET petId = ?, customerId = ?, adoptionDate = ?, adoptionStatus = ? WHERE adoptionId = ?"; + + PreparedStatement stmt = conn.prepareStatement(sql); + stmt.setInt(1, adoption.getPetId()); + stmt.setInt(2, adoption.getCustomerId()); + stmt.setString(3, adoption.getAdoptionDate()); + stmt.setString(4, adoption.getAdoptionStatus()); + stmt.setInt(5, adoptionId); + + int numRows = stmt.executeUpdate(); + conn.close(); + return numRows; + } + + //Delete adoption + public static int deleteAdoption(int adoptionId) throws SQLException { + Connection conn = ConnectionDB.getConnection(); + String sql = "DELETE FROM adoption WHERE adoptionId = ?"; + + PreparedStatement stmt = conn.prepareStatement(sql); + stmt.setInt(1, adoptionId); + + int numRows = stmt.executeUpdate(); + conn.close(); + return numRows; + } + + //Grab list of customers from DB for comboboxes + public static ObservableList getCustomers() throws SQLException { + ObservableList customers = FXCollections.observableArrayList(); + Connection conn = ConnectionDB.getConnection(); + + Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT customerId, firstName, lastName FROM customer"); + + while (rs.next()) { + customers.add(new Customer(rs.getInt(1), rs.getString(2), rs.getString(3))); + } + + conn.close(); + return customers; + } + + //DRY: converts DB data into usable Java object + private static Adoption mapRow(ResultSet rs) throws SQLException { + return new Adoption( + rs.getInt("adoptionId"), + rs.getInt("petId"), + rs.getInt("customerId"), + rs.getString("customerName"), + rs.getString("adoptionDate"), + rs.getDouble("adoptionFee"), + rs.getString("adoptionStatus") + ); + } +} diff --git a/src/main/java/org/example/petshopdesktop/database/InventoryDB.java b/src/main/java/org/example/petshopdesktop/database/InventoryDB.java new file mode 100644 index 00000000..737c952f --- /dev/null +++ b/src/main/java/org/example/petshopdesktop/database/InventoryDB.java @@ -0,0 +1,133 @@ +package org.example.petshopdesktop.database; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import org.example.petshopdesktop.models.Inventory; + +import java.sql.*; + +public class InventoryDB { + + //Base selection query + //Returns invID, ProdID, Quantity from Inventory table + //Returns ProdName from Product table + private static final String BASE_SELECT = + "SELECT i.inventoryId, i.prodId, p.prodName, i.quantity " + + "FROM inventory i " + + "JOIN product p ON i.prodId = p.prodId"; + + + //Retrieves inventory records from DB + public static ObservableList getInventory() throws SQLException { + ObservableList inventoryList = FXCollections.observableArrayList(); + + Connection conn = ConnectionDB.getConnection(); + + Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery(BASE_SELECT); + + while (rs.next()) { + inventoryList.add(mapRow(rs)); + } + + conn.close(); + + return inventoryList; + } + + //Returns records depending on search + public static ObservableList getFilteredInventory(String filter) throws SQLException { + ObservableList inventoryList = FXCollections.observableArrayList(); + Connection conn = ConnectionDB.getConnection(); + + String sql = BASE_SELECT + + " WHERE i.inventoryId LIKE ? OR " + + "i.prodId LIKE ? OR " + + "p.prodName LIKE ? OR " + + "i.quantity LIKE ?"; + + String filteredString = "%" + filter + "%"; + PreparedStatement stmt = conn.prepareStatement(sql); + + for (int i = 1; i <= 4; i++) { + stmt.setString(i, filteredString); + } + + ResultSet rs = stmt.executeQuery(); + + while (rs.next()) { + inventoryList.add(mapRow(rs)); + } + + conn.close(); + + return inventoryList; + } + + //Checks if the product already has an inventory entry + public static boolean productExistsInInventory(int prodId) throws SQLException { + Connection conn = ConnectionDB.getConnection(); + String sql = "SELECT COUNT(*) FROM inventory WHERE prodId = ?"; + + PreparedStatement stmt = conn.prepareStatement(sql); + stmt.setInt(1, prodId); + ResultSet rs = stmt.executeQuery(); + + boolean exists = rs.next() && rs.getInt(1) > 0; + conn.close(); + + return exists; + } + + //Inserting new inventory record + public static int insertInventory(Inventory inventory) throws SQLException { + Connection conn = ConnectionDB.getConnection(); + String sql = "INSERT INTO inventory (prodId, quantity) VALUES (?, ?)"; + + PreparedStatement stmt = conn.prepareStatement(sql); + stmt.setInt(1, inventory.getProdId()); + stmt.setInt(2, inventory.getQuantity()); + + int numRows = stmt.executeUpdate(); + conn.close(); + return numRows; + } + + //Updating inventory record + public static int updateInventory(int inventoryId, Inventory inventory) throws SQLException { + Connection conn = ConnectionDB.getConnection(); + String sql = "UPDATE inventory SET prodId = ?, quantity = ? WHERE inventoryId = ?"; + + PreparedStatement stmt = conn.prepareStatement(sql); + stmt.setInt(1, inventory.getProdId()); + stmt.setInt(2, inventory.getQuantity()); + stmt.setInt(3, inventoryId); + + int numRows = stmt.executeUpdate(); + conn.close(); + return numRows; + } + + //Deleting inventory record + public static int deleteInventory(int inventoryId) throws SQLException { + Connection conn = ConnectionDB.getConnection(); + String sql = "DELETE FROM inventory WHERE inventoryId = ?"; + + PreparedStatement stmt = conn.prepareStatement(sql); + stmt.setInt(1, inventoryId); + + int numRows = stmt.executeUpdate(); + conn.close(); + return numRows; + } + + //DRY: converts DB data into usable Java object + private static Inventory mapRow(ResultSet rs) throws SQLException { + return new Inventory( + rs.getInt("inventoryId"), + rs.getInt("prodId"), + rs.getString("prodName"), + rs.getInt("quantity") + ); + } +} \ No newline at end of file diff --git a/src/main/java/org/example/petshopdesktop/models/Adoption.java b/src/main/java/org/example/petshopdesktop/models/Adoption.java new file mode 100644 index 00000000..d424ff72 --- /dev/null +++ b/src/main/java/org/example/petshopdesktop/models/Adoption.java @@ -0,0 +1,68 @@ +package org.example.petshopdesktop.models; + +import javafx.beans.property.SimpleDoubleProperty; +import javafx.beans.property.SimpleIntegerProperty; +import javafx.beans.property.SimpleStringProperty; + +public class Adoption { + private SimpleIntegerProperty adoptionId; + private SimpleIntegerProperty petId; + private SimpleIntegerProperty customerId; + private SimpleStringProperty customerName; + private SimpleStringProperty adoptionDate; + private SimpleDoubleProperty adoptionFee; + private SimpleStringProperty adoptionStatus; + + //Constructor + public Adoption(int adoptionId, int petId, int customerId, String customerName, String adoptionDate, double adoptionFee, String adoptionStatus) { + this.adoptionId = new SimpleIntegerProperty(adoptionId); + this.petId = new SimpleIntegerProperty(petId); + this.customerId = new SimpleIntegerProperty(customerId); + this.customerName = new SimpleStringProperty(customerName); + this.adoptionDate = new SimpleStringProperty(adoptionDate); + this.adoptionFee = new SimpleDoubleProperty(adoptionFee); + this.adoptionStatus = new SimpleStringProperty(adoptionStatus); + } + + public int getAdoptionId() { return adoptionId.get(); } + + public void setAdoptionId(int adoptionId) { this.adoptionId.set(adoptionId); } + + public SimpleIntegerProperty adoptionIdProperty() { return adoptionId; } + + public int getPetId() { return petId.get(); } + + public void setPetId(int petId) { this.petId.set(petId); } + + public SimpleIntegerProperty petIdProperty() { return petId; } + + public int getCustomerId() { return customerId.get(); } + + public void setCustomerId(int customerId) { this.customerId.set(customerId); } + + public SimpleIntegerProperty customerIdProperty() { return customerId; } + + public String getCustomerName() { return customerName.get(); } + + public void setCustomerName(String customerName) { this.customerName.set(customerName); } + + public SimpleStringProperty customerNameProperty() { return customerName; } + + public String getAdoptionDate() { return adoptionDate.get(); } + + public void setAdoptionDate(String adoptionDate) { this.adoptionDate.set(adoptionDate); } + + public SimpleStringProperty adoptionDateProperty() { return adoptionDate; } + + public double getAdoptionFee() { return adoptionFee.get(); } + + public void setAdoptionFee(double adoptionFee) { this.adoptionFee.set(adoptionFee); } + + public SimpleDoubleProperty adoptionFeeProperty() { return adoptionFee; } + + public String getAdoptionStatus() { return adoptionStatus.get(); } + + public void setAdoptionStatus(String adoptionStatus) { this.adoptionStatus.set(adoptionStatus); } + + public SimpleStringProperty adoptionStatusProperty() { return adoptionStatus; } +} diff --git a/src/main/java/org/example/petshopdesktop/models/Customer.java b/src/main/java/org/example/petshopdesktop/models/Customer.java new file mode 100644 index 00000000..e2ccd005 --- /dev/null +++ b/src/main/java/org/example/petshopdesktop/models/Customer.java @@ -0,0 +1,35 @@ +package org.example.petshopdesktop.models; + +public class Customer { + private int customerId; + private String firstName; + private String lastName; + + //Constructor + public Customer(int customerId, String firstName, String lastName) { + this.customerId = customerId; + this.firstName = firstName; + this.lastName = lastName; + } + + //Returns customer ID + public int getCustomerId() { + return customerId; + } + + //Returns first name + public String getFirstName() { + return firstName; + } + + //Returns last name + public String getLastName() { + return lastName; + } + + //Returns first + last name + @Override + public String toString() { + return firstName + " " + lastName; + } +} diff --git a/src/main/java/org/example/petshopdesktop/models/Inventory.java b/src/main/java/org/example/petshopdesktop/models/Inventory.java new file mode 100644 index 00000000..ce6ec5e2 --- /dev/null +++ b/src/main/java/org/example/petshopdesktop/models/Inventory.java @@ -0,0 +1,43 @@ +package org.example.petshopdesktop.models; + +import javafx.beans.property.SimpleIntegerProperty; +import javafx.beans.property.SimpleStringProperty; + +public class Inventory { + private SimpleIntegerProperty inventoryId; + private SimpleIntegerProperty prodId; + private SimpleStringProperty prodName; + private SimpleIntegerProperty quantity; + + //Constructor + public Inventory(int inventoryId, int prodId, String prodName, int quantity) { + this.inventoryId = new SimpleIntegerProperty(inventoryId); + this.prodId = new SimpleIntegerProperty(prodId); + this.prodName = new SimpleStringProperty(prodName); + this.quantity = new SimpleIntegerProperty(quantity); + } + + public int getInventoryId() { return inventoryId.get(); } + + public void setInventoryId(int inventoryId) { this.inventoryId.set(inventoryId); } + + public SimpleIntegerProperty inventoryIdProperty() { return inventoryId; } + + public int getProdId() { return prodId.get(); } + + public void setProdId(int prodId) { this.prodId.set(prodId); } + + public SimpleIntegerProperty prodIdProperty() { return prodId; } + + public String getProdName() { return prodName.get(); } + + public void setProdName(String prodName) { this.prodName.set(prodName); } + + public SimpleStringProperty prodNameProperty() { return prodName; } + + public int getQuantity() { return quantity.get(); } + + public void setQuantity(int quantity) { this.quantity.set(quantity); } + + public SimpleIntegerProperty quantityProperty() { return quantity; } +} diff --git a/src/main/resources/org/example/petshopdesktop/dialogviews/adoption-dialog-view.fxml b/src/main/resources/org/example/petshopdesktop/dialogviews/adoption-dialog-view.fxml new file mode 100644 index 00000000..ef02cdf0 --- /dev/null +++ b/src/main/resources/org/example/petshopdesktop/dialogviews/adoption-dialog-view.fxml @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/org/example/petshopdesktop/dialogviews/inventory-dialog-view.fxml b/src/main/resources/org/example/petshopdesktop/dialogviews/inventory-dialog-view.fxml new file mode 100644 index 00000000..8d076531 --- /dev/null +++ b/src/main/resources/org/example/petshopdesktop/dialogviews/inventory-dialog-view.fxml @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +