Merge conflict resolution and updates

This commit is contained in:
2026-02-24 20:26:50 -07:00
parent 208aaae652
commit 13e8c01a28
38 changed files with 594 additions and 266 deletions

View File

@@ -146,6 +146,14 @@ CREATE TABLE saleItem (
FOREIGN KEY (prodId) REFERENCES product(prodId)
);
CREATE TABLE purchaseOrder (
purchaseOrderId INT AUTO_INCREMENT PRIMARY KEY,
supId INT NOT NULL,
orderDate DATE NOT NULL,
status VARCHAR(50) NOT NULL,
FOREIGN KEY (supId) REFERENCES supplier(supId)
);
CREATE TABLE activityLog (
logId INT AUTO_INCREMENT PRIMARY KEY,
employeeId INT NOT NULL,
@@ -281,20 +289,97 @@ VALUES
INSERT INTO sale (saleDate, totalAmount, paymentMethod, employeeId, storeId)
VALUES
(NOW(), 60.00, 'Card', 2, 1),
('2026-01-28 10:30:00', 50.00, 'Cash', 1, 1),
('2026-01-29 14:15:00', 120.00, 'Card', 4, 3),
('2026-01-30 16:45:00', 80.00, 'Card', 2, 2),
('2026-02-01 11:20:00', 150.00, 'Cash', 5, 4);
-- January Sales
('2026-01-05 09:15:00', 125.00, 'Card', 1, 1), -- Sale 1: Dog food + treats
('2026-01-08 11:30:00', 200.00, 'Card', 2, 1), -- Sale 2: Bird cage + fish tank
('2026-01-12 14:20:00', 60.00, 'Cash', 3, 2), -- Sale 3: Cat toys + hamster wheel
('2026-01-15 10:45:00', 150.00, 'Debit', 1, 1), -- Sale 4: Dog food (bulk purchase)
('2026-01-18 16:30:00', 80.00, 'Card', 4, 3), -- Sale 5: Fish tank
('2026-01-22 13:15:00', 95.00, 'Cash', 2, 2), -- Sale 6: Mixed items
('2026-01-25 15:40:00', 240.00, 'Card', 5, 4), -- Sale 7: Two bird cages
('2026-01-28 10:30:00', 80.00, 'Cash', 1, 1), -- Sale 8: Dog food + cat toys
-- February Sales
('2026-02-01 09:00:00', 175.00, 'Card', 3, 3), -- Sale 9: Dog food + treats (bulk)
('2026-02-03 11:20:00', 120.00, 'Card', 2, 1), -- Sale 10: Bird cage
('2026-02-05 14:50:00', 45.00, 'Cash', 4, 2), -- Sale 11: Hamster wheel + cat toys
('2026-02-08 16:15:00', 160.00, 'Debit', 1, 1), -- Sale 12: Fish tank + accessories
('2026-02-10 10:25:00', 100.00, 'Card', 5, 4), -- Sale 13: Dog treats (bulk)
('2026-02-12 13:45:00', 50.00, 'Cash', 2, 2), -- Sale 14: Dog food
('2026-02-15 15:30:00', 85.00, 'Card', 3, 3), -- Sale 15: Mixed pet supplies
('2026-02-18 11:10:00', 200.00, 'Card', 1, 1), -- Sale 16: Bird cage + hamster wheel
('2026-02-20 14:35:00', 155.00, 'Debit', 4, 3), -- Sale 17: Fish tank + cat toys
('2026-02-22 16:50:00', 75.00, 'Cash', 2, 1), -- Sale 18: Dog treats + toys
('2026-02-24 10:15:00', 140.00, 'Card', 5, 4), -- Sale 19: Dog food + treats
(NOW(), 95.00, 'Card', 1, 1); -- Sale 20: Recent sale (current timestamp)
INSERT INTO saleItem (saleId, prodId, quantity, unitPrice)
VALUES
(1, 2, 2, 10.00),
(2, 1, 1, 50.00),
(3, 3, 1, 120.00),
(4, 4, 1, 80.00),
(5, 6, 6, 25.00),
(2, 2, 3, 10.00);
-- Sale 1 items (Dog food + treats)
(1, 1, 2, 50.00), -- 2x Premium Dog Food
(1, 6, 1, 25.00), -- 1x Organic Dog Treats
-- Sale 2 items (Bird cage + fish tank)
(2, 3, 1, 120.00), -- 1x Bird Cage Large
(2, 4, 1, 80.00), -- 1x Fish Tank 20 Gallon
-- Sale 3 items (Cat toys + hamster wheel)
(3, 2, 3, 10.00), -- 3x Cat Toy Ball
(3, 5, 2, 15.00), -- 2x Hamster Wheel
-- Sale 4 items (Dog food bulk)
(4, 1, 3, 50.00), -- 3x Premium Dog Food
-- Sale 5 items (Fish tank)
(5, 4, 1, 80.00), -- 1x Fish Tank 20 Gallon
-- Sale 6 items (Mixed)
(6, 2, 4, 10.00), -- 4x Cat Toy Ball
(6, 5, 1, 15.00), -- 1x Hamster Wheel
(6, 6, 1, 25.00), -- 1x Organic Dog Treats
(6, 1, 1, 50.00), -- 1x Premium Dog Food (partial - discount applied)
-- Sale 7 items (Two bird cages)
(7, 3, 2, 120.00), -- 2x Bird Cage Large
-- Sale 8 items (Dog food + cat toys)
(8, 1, 1, 50.00), -- 1x Premium Dog Food
(8, 2, 3, 10.00), -- 3x Cat Toy Ball
-- Sale 9 items (Dog food + treats bulk)
(9, 1, 3, 50.00), -- 3x Premium Dog Food
(9, 6, 1, 25.00), -- 1x Organic Dog Treats
-- Sale 10 items (Bird cage)
(10, 3, 1, 120.00), -- 1x Bird Cage Large
-- Sale 11 items (Hamster wheel + cat toys)
(11, 5, 1, 15.00), -- 1x Hamster Wheel
(11, 2, 3, 10.00), -- 3x Cat Toy Ball
-- Sale 12 items (Fish tank + accessories)
(12, 4, 2, 80.00), -- 2x Fish Tank 20 Gallon
-- Sale 13 items (Dog treats bulk)
(13, 6, 4, 25.00), -- 4x Organic Dog Treats
-- Sale 14 items (Dog food)
(14, 1, 1, 50.00), -- 1x Premium Dog Food
-- Sale 15 items (Mixed supplies)
(15, 2, 2, 10.00), -- 2x Cat Toy Ball
(15, 5, 1, 15.00), -- 1x Hamster Wheel
(15, 6, 2, 25.00), -- 2x Organic Dog Treats
-- Sale 16 items (Bird cage + hamster wheel)
(16, 3, 1, 120.00), -- 1x Bird Cage Large
(16, 4, 1, 80.00), -- 1x Fish Tank 20 Gallon
-- Sale 17 items (Fish tank + cat toys)
(17, 4, 1, 80.00), -- 1x Fish Tank 20 Gallon
(17, 1, 1, 50.00), -- 1x Premium Dog Food
(17, 6, 1, 25.00), -- 1x Organic Dog Treats
-- Sale 18 items (Dog treats + toys)
(18, 6, 2, 25.00), -- 2x Organic Dog Treats
(18, 2, 2, 10.00), -- 2x Cat Toy Ball
(18, 5, 1, 15.00), -- 1x Hamster Wheel
-- Sale 19 items (Dog food + treats)
(19, 1, 2, 50.00), -- 2x Premium Dog Food
(19, 6, 2, 25.00), -- 2x Organic Dog Treats (discount applied)
-- Sale 20 items (Recent sale)
(20, 2, 5, 10.00), -- 5x Cat Toy Ball
(20, 5, 3, 15.00); -- 3x Hamster Wheel
INSERT INTO purchaseOrder (supId, orderDate, status)
VALUES
(1, '2025-01-15', 'Delivered'),
(2, '2025-01-20', 'Pending'),
(3, '2025-02-01', 'Delivered'),
(4, '2025-02-10', 'In Transit'),
(1, '2025-02-15', 'Pending');
INSERT INTO activityLog (employeeId, activity)
VALUES

48
README.md Normal file
View File

@@ -0,0 +1,48 @@
# Pet Shop Desktop (JavaFX)
Desktop pet shop management app built with JavaFX and MySQL.
Made by **Group 2**, Shiv, Nikitha, Alex, Harkamal.
## Requirements
- IntelliJ IDEA (Community or Ultimate)
- Java 17+
- Maven (handled through IntelliJ)
- Docker and Docker Compose (for the local MySQL container)
## Database setup (IntelliJ)
1. Open the project in IntelliJ.
2. Open **View → Tool Windows → Services**.
3. Add a Docker connection if needed, then open the **Docker** section in Services.
4. Start the Compose stack from `docker-compose.yml` (Compose Up, or Start).
5. Confirm the `mysql` service is running.
The container uses `mysql:8.4`, creates the `Petstoredb` database, and imports `Petstoredata.sql`.
## App configuration
An example connection file is provided at `connectionpetstore.properties.example`. Copy it to `connectionpetstore.properties` and edit the values to match the local database setup.
## Run the app (IntelliJ, Maven)
1. Open **View → Tool Windows → Maven**.
2. Click **Reload All Maven Projects** if the dependencies have not loaded yet.
3. In the Maven tool window, expand **Plugins → javafx**.
4. Double click **javafx:run**.
Optional, run a clean first:
- In the Maven tool window, expand **Lifecycle** and run **clean**.
## Default accounts
On first run, the app creates a `users` table (if missing) and seeds two accounts:
- Admin: `admin` / `admin123`
- Staff: `staff` / `staff123`
## Notes
- `connectionpetstore.properties` is gitignored so credentials are not committed.
- If the app cannot connect to MySQL, confirm the Compose stack is running and MySQL is available.

View File

@@ -10,7 +10,7 @@
<name>PetShopDesktop</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<javafx.version>25.0.2</javafx.version>
<javafx.version>17.0.10</javafx.version>
<junit.version>5.12.1</junit.version>
</properties>
@@ -53,8 +53,7 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<source>23</source>
<target>23</target>
<release>17</release>
</configuration>
</plugin>
<plugin>

View File

@@ -0,0 +1,93 @@
package org.example.petshopdesktop.DTOs;
import javafx.beans.property.*;
public class SaleDTO {
private IntegerProperty saleId;
private StringProperty saleDate;
private StringProperty employeeName;
private StringProperty productName;
private IntegerProperty quantity;
private DoubleProperty unitPrice;
private DoubleProperty total;
private StringProperty paymentMethod;
public SaleDTO(int saleId, String saleDate, String employeeName, String productName,
int quantity, double unitPrice, double total, String paymentMethod) {
this.saleId = new SimpleIntegerProperty(saleId);
this.saleDate = new SimpleStringProperty(saleDate);
this.employeeName = new SimpleStringProperty(employeeName);
this.productName = new SimpleStringProperty(productName);
this.quantity = new SimpleIntegerProperty(quantity);
this.unitPrice = new SimpleDoubleProperty(unitPrice);
this.total = new SimpleDoubleProperty(total);
this.paymentMethod = new SimpleStringProperty(paymentMethod);
}
// Getters
public int getSaleId() {
return saleId.get();
}
public String getSaleDate() {
return saleDate.get();
}
public String getEmployeeName() {
return employeeName.get();
}
public String getProductName() {
return productName.get();
}
public int getQuantity() {
return quantity.get();
}
public double getUnitPrice() {
return unitPrice.get();
}
public double getTotal() {
return total.get();
}
public String getPaymentMethod() {
return paymentMethod.get();
}
// Properties
public IntegerProperty saleIdProperty() {
return saleId;
}
public StringProperty saleDateProperty() {
return saleDate;
}
public StringProperty employeeNameProperty() {
return employeeName;
}
public StringProperty productNameProperty() {
return productName;
}
public IntegerProperty quantityProperty() {
return quantity;
}
public DoubleProperty unitPriceProperty() {
return unitPrice;
}
public DoubleProperty totalProperty() {
return total;
}
public StringProperty paymentMethodProperty() {
return paymentMethod;
}
}

View File

@@ -1,5 +1,3 @@
//Initial commmit
package org.example.petshopdesktop;
import javafx.application.Application;

View File

@@ -3,14 +3,14 @@ package org.example.petshopdesktop;
public class Validator {
/**
* Checks if string is not empty
* Checks if string is not blank
* @param value string to check
* @param name name of the input
* @return error msg if string is not empty, otherwise empty
* @return error msg if string is blank, otherwise empty
*/
public static String isPresent(String value, String name){
String msg =""; //OK so far
if(value.isEmpty() || name.isBlank()){
String msg = "";
if (value == null || value.isBlank()){
msg += name + " is required. \n";
}
return msg;
@@ -23,7 +23,7 @@ public class Validator {
* @return error msg if input is not a number or negative, otherwise empty
*/
public static String isNonNegativeDouble(String value, String name){
String msg =""; //OK so far
String msg ="";
double result;
try{
result = Double.parseDouble(value);
@@ -40,13 +40,13 @@ public class Validator {
/**
* Checks if the input is a double in 2 different range
* @param value input of string
* @param name name of inpt
* @param name name of input
* @param minValue min value of range
* @param maxValue max value of range
* @return error msg if input is out of range, otherwise empty
*/
public static String isDoubleInRange(String value, String name, double minValue, double maxValue){
String msg =""; //OK so far
String msg ="";
double result;
try{
result = Double.parseDouble(value);
@@ -67,7 +67,7 @@ public class Validator {
* @return error msg if input is not a number or negative, otherwise empty
*/
public static String isNonNegativeInteger(String value, String name){
String msg =""; //OK so far
String msg ="";
int result;
try{
result = Integer.parseInt(value);
@@ -85,6 +85,7 @@ public class Validator {
* check if the string is a given amount of characters or fewer
* @param value input of string
* @param name name of input
* @param length max allowed length
* @return error msg if input is more than given characters length
*/
public static String isLessThanVarChars(String value, String name, int length){
@@ -102,8 +103,7 @@ public class Validator {
* @return error msg if input is not a valid email format, otherwise empty
*/
public static String isValidEmail(String value, String name){
String msg = ""; //OK so far
// Email regex
String msg = "";
String regex = "^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$";
if (!value.matches(regex)){
@@ -119,8 +119,7 @@ public class Validator {
* @return error msg if input is not in valid phone format, otherwise empty
*/
public static String isValidPhoneNumber(String value, String name){
String msg = ""; //OK so far
// Phone regex
String msg = "";
String regex = "^\\d{3}-\\d{3}-\\d{4}$";
if (!value.matches(regex)){

View File

@@ -1,13 +1,6 @@
package org.example.petshopdesktop.auth;
/*
Petshop Desktop
Purpose: Application role definitions used by session state and role based access control.
*/
public enum Role {
// Administrative access, includes system management screens.
ADMIN,
// Staff access, limited to day to day operational screens.
STAFF
}

View File

@@ -1,24 +1,15 @@
package org.example.petshopdesktop.auth;
/*
Petshop Desktop
Purpose: In memory session state for the authenticated user.
Notes: Session is process local and cleared on logout or application restart.
*/
public class UserSession {
// Singleton instance used to share session state across controllers.
private static UserSession instance;
// Current authenticated username, null when logged out.
private String username;
// Current authenticated role, null when logged out.
private Role role;
private UserSession() {}
// Lazily initialised singleton accessor.
public static UserSession getInstance() {
if (instance == null) {
instance = new UserSession();
@@ -26,13 +17,11 @@ public class UserSession {
return instance;
}
// Stores identity and role for the active session.
public void login(String username, Role role) {
this.username = username;
this.role = role;
}
// Clears session state and returns the application to an unauthenticated state.
public void logout() {
this.username = null;
this.role = null;
@@ -46,13 +35,10 @@ public class UserSession {
return role;
}
// Convenience check for administrative privileges.
// Role.ADMIN.equals(role) remains safe when role is null.
public boolean isAdmin() {
return Role.ADMIN.equals(role);
}
// Session is considered active only when both username and role are set.
public boolean isLoggedIn() {
return username != null && role != null;
}

View File

@@ -14,10 +14,6 @@ import org.example.petshopdesktop.models.User;
import java.sql.SQLException;
/*
Petshop Desktop
Purpose: Authentication controller responsible for validating credentials and initialising the user session.
*/
public class LoginController {
@FXML
@@ -31,18 +27,15 @@ public class LoginController {
@FXML
void btnLoginClicked(ActionEvent event) {
// Input normalisation keeps authentication behaviour consistent.
String username = txtUsername.getText().trim();
String password = txtPassword.getText();
// Basic validation to avoid unnecessary database calls.
if (username.isEmpty() || password.isEmpty()) {
lblError.setText("Please enter username and password.");
return;
}
try {
// Credential verification returns a fully populated User on success.
User user = UserDB.authenticate(username, password);
if (user == null) {
lblError.setText("Invalid username or password.");
@@ -50,7 +43,6 @@ public class LoginController {
return;
}
// Session state is stored in memory for use by controllers and UI RBAC.
UserSession.getInstance().login(user.getUsername(), user.getRole());
openMainLayout();
@@ -61,7 +53,6 @@ public class LoginController {
private void openMainLayout() {
try {
// View transition into the post login application shell.
FXMLLoader loader = new FXMLLoader(
getClass().getResource("/org/example/petshopdesktop/main-layout-view.fxml"));
Scene scene = new Scene(loader.load());

View File

@@ -11,12 +11,23 @@ import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import org.example.petshopdesktop.auth.UserSession;
/*
Petshop Desktop
Purpose: Main application shell controller, includes navigation and UI level role based access control.
*/
public class MainLayoutController {
private static final String NAV_BASE_STYLE = "-fx-background-color: transparent; " +
"-fx-text-fill: #cbd5e1; " +
"-fx-background-radius: 10; " +
"-fx-cursor: hand; " +
"-fx-focus-color: transparent; " +
"-fx-faint-focus-color: transparent;";
private static final String NAV_ACTIVE_STYLE = "-fx-background-color: #FF6B6B; " +
"-fx-text-fill: white; " +
"-fx-background-radius: 10; " +
"-fx-cursor: hand; " +
"-fx-focus-color: transparent; " +
"-fx-faint-focus-color: transparent; " +
"-fx-effect: dropshadow(gaussian, rgba(0,0,0,0.22), 10, 0.15, 0, 2);";
@FXML
private Button btnAdoptions;
@@ -124,7 +135,6 @@ public class MainLayoutController {
@FXML
void btnLogoutClicked(ActionEvent event) {
// Logout clears session state before returning to the login view.
UserSession.getInstance().logout();
try {
FXMLLoader loader = new FXMLLoader(
@@ -141,22 +151,18 @@ public class MainLayoutController {
@FXML
public void initialize() {
// RBAC state is applied once during initial layout load.
applyRBAC();
// Default landing view after successful authentication.
loadView("pet-view.fxml");
updateButtons(btnPets);
}
private void applyRBAC() {
UserSession session = UserSession.getInstance();
// Session identity is displayed in the header for clarity and auditing.
lblUsername.setText(session.getUsername());
lblRole.setText(session.getRole().toString());
lblRole.setText("Leon's Petstore");
// UI level RBAC hides admin only navigation entries for non admin users.
// setManaged(false) removes the node from layout calculations to avoid empty spacing.
boolean isAdmin = session.isAdmin();
btnInventory.setVisible(isAdmin);
btnInventory.setManaged(isAdmin);
@@ -165,7 +171,6 @@ public class MainLayoutController {
btnProductSuppliers.setVisible(isAdmin);
btnProductSuppliers.setManaged(isAdmin);
// Privileged operations should still be enforced within the relevant controllers and database methods.
}
/**
@@ -174,10 +179,8 @@ public class MainLayoutController {
*/
private void loadView(String fxmlFile) {
try {
//Get the location of the fxml for view
FXMLLoader loader = new FXMLLoader(getClass().getResource("/org/example/petshopdesktop/modelviews/" + fxmlFile));
Parent view = loader.load();
//Clear any content that is in the stack pane and add the new view to display
spContentArea.getChildren().clear();
spContentArea.getChildren().add(view);
} catch (Exception e) {
@@ -191,21 +194,13 @@ public class MainLayoutController {
* @param activeButton the button to be set active
*/
private void updateButtons(Button activeButton) {
//reset all buttons
Button[] BUTTONS = {btnAdoptions, btnPets, btnAppointments, btnInventory,
btnSalesHistory, btnServices, btnSuppliers, btnProductSuppliers, btnProducts};
btnSalesHistory, btnServices, btnSuppliers, btnProductSuppliers, btnProducts, btnPurchaseOrders};
for (Button button : BUTTONS) {
//set all buttons to inactive
button.setStyle("-fx-background-color: transparent; " +
"-fx-text-fill: #CCCCCC; " +
"-fx-cursor: hand");
button.setStyle(NAV_BASE_STYLE);
}
//set active button
activeButton.setStyle("-fx-background-color: #FF6B6B; " +
"-fx-text-fill: white; " +
"-fx-cursor: hand; " +
"-fx-background-radius: 8");
activeButton.setStyle(NAV_ACTIVE_STYLE);
}
}

View File

@@ -1,11 +1,14 @@
package org.example.petshopdesktop.controllers;
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.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import org.example.petshopdesktop.DTOs.SaleDTO;
import org.example.petshopdesktop.database.SaleDB;
import java.sql.SQLException;
public class SaleController {
@@ -13,38 +16,137 @@ public class SaleController {
private Button btnRefresh;
@FXML
private TableColumn<?, ?> colCustomerName;
private TableColumn<SaleDTO, String> colCustomerName;
@FXML
private TableColumn<?, ?> colSaleDate;
private TableColumn<SaleDTO, String> colSaleDate;
@FXML
private TableColumn<?, ?> colSaleId;
private TableColumn<SaleDTO, Integer> colSaleId;
@FXML
private TableColumn<?, ?> colSalePaymentType;
private TableColumn<SaleDTO, String> colSalePaymentType;
@FXML
private TableColumn<?, ?> colSaleQuantity;
private TableColumn<SaleDTO, Integer> colSaleQuantity;
@FXML
private TableColumn<?, ?> colSaleTotal;
private TableColumn<SaleDTO, Double> colSaleTotal;
@FXML
private TableColumn<?, ?> colSaleUnitPrice;
private TableColumn<SaleDTO, Double> colSaleUnitPrice;
@FXML
private TableColumn<?, ?> colServiceProduct;
private TableColumn<SaleDTO, String> colServiceProduct;
@FXML
private TableView<?> tvSales;
private TableView<SaleDTO> tvSales;
@FXML
private TextField txtSearch;
@FXML
void btnRefresh(ActionEvent event) {
private ObservableList<SaleDTO> salesData;
/**
* Initialize the controller - set up table columns and load data
*/
@FXML
public void initialize() {
// Set up table columns
colSaleId.setCellValueFactory(new PropertyValueFactory<>("saleId"));
colSaleDate.setCellValueFactory(new PropertyValueFactory<>("saleDate"));
colCustomerName.setCellValueFactory(new PropertyValueFactory<>("employeeName"));
colServiceProduct.setCellValueFactory(new PropertyValueFactory<>("productName"));
colSaleQuantity.setCellValueFactory(new PropertyValueFactory<>("quantity"));
colSaleUnitPrice.setCellValueFactory(new PropertyValueFactory<>("unitPrice"));
colSaleTotal.setCellValueFactory(new PropertyValueFactory<>("total"));
colSalePaymentType.setCellValueFactory(new PropertyValueFactory<>("paymentMethod"));
// Format currency columns
colSaleUnitPrice.setCellFactory(tc -> new TableCell<SaleDTO, Double>() {
@Override
protected void updateItem(Double price, boolean empty) {
super.updateItem(price, empty);
if (empty || price == null) {
setText(null);
} else {
setText(String.format("$%.2f", price));
}
}
});
colSaleTotal.setCellFactory(tc -> new TableCell<SaleDTO, Double>() {
@Override
protected void updateItem(Double total, boolean empty) {
super.updateItem(total, empty);
if (empty || total == null) {
setText(null);
} else {
setText(String.format("$%.2f", total));
}
}
});
// Load initial data
loadSales();
// Add search functionality
txtSearch.textProperty().addListener((observable, oldValue, newValue) -> {
if (newValue == null || newValue.trim().isEmpty()) {
loadSales();
} else {
searchSales(newValue.trim());
}
});
}
/**
* Load all sales from database
*/
private void loadSales() {
try {
salesData = SaleDB.getSales();
tvSales.setItems(salesData);
} catch (SQLException e) {
e.printStackTrace();
showError("Failed to load sales data", e.getMessage());
}
}
/**
* Search sales based on filter text
* @param filter search term
*/
private void searchSales(String filter) {
try {
ObservableList<SaleDTO> filteredSales = SaleDB.getFilteredSales(filter);
tvSales.setItems(filteredSales);
} catch (SQLException e) {
e.printStackTrace();
showError("Failed to search sales", e.getMessage());
}
}
/**
* Refresh button handler - reload all sales data
* @param event button click event
*/
@FXML
void btnRefresh(ActionEvent event) {
txtSearch.clear();
loadSales();
}
/**
* Show error alert
* @param title alert title
* @param message error message
*/
private void showError(String title, String message) {
Alert alert = new Alert(Alert.AlertType.ERROR);
alert.setTitle(title);
alert.setHeaderText(null);
alert.setContentText(message);
alert.showAndWait();
}
}

View File

@@ -115,10 +115,10 @@ public class AdoptionDB {
Connection conn = ConnectionDB.getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT customerId, firstName, lastName FROM customer");
ResultSet rs = stmt.executeQuery("SELECT customerId, firstName, lastName, email, phone FROM customer");
while (rs.next()) {
customers.add(new Customer(rs.getInt(1), rs.getString(2), rs.getString(3)));
customers.add(new Customer(rs.getInt(1), rs.getString(2), rs.getString(3), rs.getString(4), rs.getString(5)));
}
conn.close();

View File

@@ -23,7 +23,7 @@ public class ProductSupplierDB {
//Execute Query
Statement stmt = conn.createStatement();
String sql = "SELECT ps.supId, ps.prodId, s.supCompany, p.prodName, ps.cost " +
"FROM productsupplier ps " +
"FROM productSupplier ps " +
"LEFT JOIN product p " +
"ON p.prodId = ps.prodId " +
"LEFT JOIN supplier s " +
@@ -62,7 +62,7 @@ public class ProductSupplierDB {
String sql =
"SELECT ps.supId, ps.prodId, s.supCompany, p.prodName, ps.cost " +
"FROM product p " +
"LEFT JOIN productsupplier ps " +
"LEFT JOIN productSupplier ps " +
"ON p.prodId = ps.prodId " +
"LEFT JOIN supplier s " +
"ON s.supId = ps.supId " +
@@ -108,7 +108,7 @@ public class ProductSupplierDB {
int numRows = 0;
Connection conn = ConnectionDB.getConnection();
String sql = "INSERT INTO productsupplier (prodId, supId, cost) " +
String sql = "INSERT INTO productSupplier (prodId, supId, cost) " +
"VALUES (?, ?, ?)";
//These are the values from productSupplier to put into query above
@@ -141,7 +141,7 @@ public class ProductSupplierDB {
try{
//Delete old data first
String sql = "DELETE FROM productsupplier WHERE supId = ? AND prodId = ?";
String sql = "DELETE FROM productSupplier WHERE supId = ? AND prodId = ?";
PreparedStatement stmt = conn.prepareStatement(sql);
stmt.setInt(1, oldSupId);
stmt.setInt(2, oldProdId);
@@ -149,7 +149,7 @@ public class ProductSupplierDB {
//Then change the data by inserting a new relation with given keys (only if delete worked)
if(numRows > 0){
sql = "INSERT INTO productsupplier (prodId, supId, cost) " +
sql = "INSERT INTO productSupplier (prodId, supId, cost) " +
"VALUES (?, ?, ?)";
stmt = conn.prepareStatement(sql);
stmt.setInt(1, productSupplier.getProdId());
@@ -184,7 +184,7 @@ public class ProductSupplierDB {
int numRows = 0;
Connection conn = ConnectionDB.getConnection();
String sql = "DELETE FROM productsupplier WHERE supId = ? AND prodId = ?";
String sql = "DELETE FROM productSupplier WHERE supId = ? AND prodId = ?";
PreparedStatement stmt = conn.prepareStatement(sql);
stmt.setInt(1, supId);
stmt.setInt(2, prodId);

View File

@@ -0,0 +1,113 @@
package org.example.petshopdesktop.database;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import org.example.petshopdesktop.DTOs.SaleDTO;
import java.sql.*;
public class SaleDB {
/**
* Get all sale items with details
* @return ObservableList of SaleDTOs
* @throws SQLException if database operation fails
*/
public static ObservableList<SaleDTO> getSales() throws SQLException {
ObservableList<SaleDTO> sales = FXCollections.observableArrayList();
Connection conn = ConnectionDB.getConnection();
String sql = """
SELECT
s.saleId,
DATE_FORMAT(s.saleDate, '%Y-%m-%d %H:%i') as saleDate,
CONCAT(e.firstName, ' ', e.lastName) as employeeName,
p.prodName,
si.quantity,
si.unitPrice,
(si.quantity * si.unitPrice) as lineTotal,
s.paymentMethod
FROM sale s
JOIN saleItem si ON s.saleId = si.saleId
JOIN product p ON si.prodId = p.prodId
JOIN employee e ON s.employeeId = e.employeeId
ORDER BY s.saleDate DESC, s.saleId, si.saleItemId
""";
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);
while (rs.next()) {
sales.add(new SaleDTO(
rs.getInt("saleId"),
rs.getString("saleDate"),
rs.getString("employeeName"),
rs.getString("prodName"),
rs.getInt("quantity"),
rs.getDouble("unitPrice"),
rs.getDouble("lineTotal"),
rs.getString("paymentMethod")
));
}
conn.close();
return sales;
}
/**
* Get filtered sale items
* @param filter search term
* @return ObservableList of SaleDTOs matching the filter
* @throws SQLException if database operation fails
*/
public static ObservableList<SaleDTO> getFilteredSales(String filter) throws SQLException {
ObservableList<SaleDTO> sales = FXCollections.observableArrayList();
Connection conn = ConnectionDB.getConnection();
String sql = """
SELECT
s.saleId,
DATE_FORMAT(s.saleDate, '%Y-%m-%d %H:%i') as saleDate,
CONCAT(e.firstName, ' ', e.lastName) as employeeName,
p.prodName,
si.quantity,
si.unitPrice,
(si.quantity * si.unitPrice) as lineTotal,
s.paymentMethod
FROM sale s
JOIN saleItem si ON s.saleId = si.saleId
JOIN product p ON si.prodId = p.prodId
JOIN employee e ON s.employeeId = e.employeeId
WHERE s.saleId LIKE ?
OR p.prodName LIKE ?
OR CONCAT(e.firstName, ' ', e.lastName) LIKE ?
OR s.paymentMethod LIKE ?
ORDER BY s.saleDate DESC, s.saleId, si.saleItemId
""";
PreparedStatement pstmt = conn.prepareStatement(sql);
String searchPattern = "%" + filter + "%";
pstmt.setString(1, searchPattern);
pstmt.setString(2, searchPattern);
pstmt.setString(3, searchPattern);
pstmt.setString(4, searchPattern);
ResultSet rs = pstmt.executeQuery();
while (rs.next()) {
sales.add(new SaleDTO(
rs.getInt("saleId"),
rs.getString("saleDate"),
rs.getString("employeeName"),
rs.getString("prodName"),
rs.getInt("quantity"),
rs.getDouble("unitPrice"),
rs.getDouble("lineTotal"),
rs.getString("paymentMethod")
));
}
conn.close();
return sales;
}
}

View File

@@ -5,10 +5,6 @@ import org.example.petshopdesktop.models.User;
import java.sql.*;
/*
Petshop Desktop
Purpose: User authentication and role lookup against the users table.
*/
public class UserDB {
/**
@@ -34,8 +30,6 @@ public class UserDB {
int userId = rs.getInt("user_id");
String uname = rs.getString("username");
// Role values are stored in the database as strings and normalised to match the enum.
// Table constraints limit role values, Role.valueOf is expected to be safe under normal operation.
Role role = Role.valueOf(rs.getString("role").toUpperCase());
return new User(userId, uname, role);
@@ -59,7 +53,6 @@ public class UserDB {
)
""";
// Default accounts support initial development and testing, credentials should be rotated or removed for deployment.
String seedAdmin = """
INSERT IGNORE INTO users (username, password_hash, role)
VALUES ('admin', SHA2('admin123', 256), 'ADMIN')

View File

@@ -13,7 +13,7 @@
<?import javafx.scene.layout.VBox?>
<?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/25" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.example.petshopdesktop.controllers.dialogcontrollers.AdoptionDialogController">
<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.AdoptionDialogController">
<children>
<HBox alignment="CENTER_LEFT" prefHeight="79.0" prefWidth="727.0" spacing="20.0" style="-fx-background-color: #2C3E50; -fx-background-radius: 14;">
<children>

View File

@@ -17,7 +17,7 @@
<VBox minHeight="-Infinity" minWidth="-Infinity" prefHeight="523.0" prefWidth="790.0"
spacing="20.0" style="-fx-font-size: 14px;"
xmlns="http://javafx.com/javafx/25" xmlns:fx="http://javafx.com/fxml/1"
xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="org.example.petshopdesktop.controllers.dialogcontrollers.AppointmentDialogController">
<children>
<HBox alignment="CENTER_LEFT" prefHeight="79.0" prefWidth="727.0" spacing="20.0"

View File

@@ -13,7 +13,7 @@
<?import javafx.scene.layout.VBox?>
<?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/25" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.example.petshopdesktop.controllers.dialogcontrollers.InventoryDialogController">
<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.InventoryDialogController">
<children>
<HBox alignment="CENTER_LEFT" prefHeight="79.0" prefWidth="727.0" spacing="20.0" style="-fx-background-color: #2C3E50; -fx-background-radius: 14;">
<children>

View File

@@ -13,7 +13,7 @@
<?import javafx.scene.layout.VBox?>
<?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/25" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.example.petshopdesktop.controllers.dialogcontrollers.PetDialogController">
<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">
<children>
<HBox alignment="CENTER_LEFT" prefHeight="79.0" prefWidth="727.0" spacing="20.0" style="-fx-background-color: #2C3E50; -fx-background-radius: 14;">
<children>

View File

@@ -13,7 +13,7 @@
<?import javafx.scene.layout.VBox?>
<?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/25" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.example.petshopdesktop.controllers.dialogcontrollers.ProductDialogController">
<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">
<children>
<HBox alignment="CENTER_LEFT" prefHeight="79.0" prefWidth="727.0" spacing="20.0" style="-fx-background-color: #2C3E50; -fx-background-radius: 14;">
<children>

View File

@@ -13,7 +13,7 @@
<?import javafx.scene.layout.VBox?>
<?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/25" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.example.petshopdesktop.controllers.dialogcontrollers.ProductSupplierDialogController">
<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.ProductSupplierDialogController">
<children>
<HBox alignment="CENTER_LEFT" prefHeight="79.0" prefWidth="727.0" spacing="20.0" style="-fx-background-color: #2C3E50; -fx-background-radius: 14;">
<children>

View File

@@ -13,7 +13,7 @@
<?import javafx.scene.text.Font?>
<?import javafx.scene.control.ComboBox?>
<VBox minHeight="-Infinity" minWidth="-Infinity" prefHeight="523.0" prefWidth="790.0" spacing="20.0" style="-fx-font-size: 14px;" xmlns="http://javafx.com/javafx/25" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.example.petshopdesktop.controllers.dialogcontrollers.ServiceDialogController">
<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.ServiceDialogController">
<children>
<HBox alignment="CENTER_LEFT" prefHeight="79.0" prefWidth="727.0" spacing="20.0" style="-fx-background-color: #2C3E50; -fx-background-radius: 14;">
<children>

View File

@@ -12,7 +12,7 @@
<?import javafx.scene.layout.VBox?>
<?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/25" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.example.petshopdesktop.controllers.dialogcontrollers.SupplierDialogController">
<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.SupplierDialogController">
<children>
<HBox alignment="CENTER_LEFT" prefHeight="79.0" prefWidth="727.0" spacing="20.0" style="-fx-background-color: #2C3E50; -fx-background-radius: 14;">
<children>

View File

@@ -10,7 +10,7 @@
<VBox alignment="CENTER" prefHeight="400.0" prefWidth="380.0" spacing="16.0"
style="-fx-background-color: #2C3E50;"
xmlns="http://javafx.com/javafx/25"
xmlns="http://javafx.com/javafx/17"
xmlns:fx="http://javafx.com/fxml/1"
fx:controller="org.example.petshopdesktop.controllers.LoginController">
<padding>

View File

@@ -5,131 +5,146 @@
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.Separator?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.Region?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="742.0" prefWidth="1069.0" xmlns="http://javafx.com/javafx/25" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.example.petshopdesktop.controllers.MainLayoutController">
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="742.0" prefWidth="1069.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.example.petshopdesktop.controllers.MainLayoutController">
<left>
<VBox prefHeight="700.0" prefWidth="250.0" spacing="15.0" style="-fx-background-color: #2C3E50;" BorderPane.alignment="CENTER">
<VBox prefHeight="700.0" prefWidth="250.0" spacing="10.0" style="-fx-background-color: #2C3E50;" BorderPane.alignment="CENTER">
<padding>
<Insets bottom="30.0" left="30.0" right="30.0" top="30.0" />
<Insets bottom="22.0" left="22.0" right="22.0" top="24.0" />
</padding>
<children>
<Label fx:id="lblUsername" text="Name" textFill="WHITE">
<font>
<Font name="Comic Sans MS Bold" size="30.0" />
<Font name="System Bold" size="24.0" />
</font>
</Label>
<Label fx:id="lblRole" text="Pet Store Manager" textFill="#ffe66d">
<Label fx:id="lblRole" text="Leon's Petstore" textFill="#ffe66d">
<font>
<Font name="Comic Sans MS Bold" size="16.0" />
<Font name="System" size="14.0" />
</font>
<padding>
<Insets bottom="20.0" />
<Insets bottom="8.0" />
</padding>
</Label>
<Separator prefWidth="200.0" style="-fx-background-color: #444444;" />
<Button fx:id="btnPets" mnemonicParsing="false" onAction="#btnPetsClicked" prefWidth="250.0" style="-fx-background-color: #FF6b6b; -fx-background-radius: 8; -fx-cursor: hand;" text="🐾 Pets" textFill="WHITE">
<VBox.margin>
<Insets />
</VBox.margin>
<Separator prefWidth="200.0" style="-fx-background-color: #444444; -fx-opacity: 0.35;" />
<Label text="PETS" textFill="#94a3b8">
<font>
<Font name="System Bold" size="12.0" />
</font>
<padding>
<Insets bottom="12.0" left="12.0" right="110.0" top="12.0" />
<Insets top="8.0" />
</padding>
</Label>
<Button fx:id="btnPets" alignment="CENTER_LEFT" mnemonicParsing="false" onAction="#btnPetsClicked" prefWidth="250.0" style="-fx-background-color: transparent; -fx-background-radius: 10; -fx-cursor: hand; -fx-focus-color: transparent; -fx-faint-focus-color: transparent;" text="Pets" textFill="#cbd5e1">
<padding>
<Insets bottom="10.0" left="14.0" right="14.0" top="10.0" />
</padding>
<font>
<Font name="Comic Sans MS Bold" size="14.0" />
<Font name="System" size="13.0" />
</font>
</Button>
<Button fx:id="btnAdoptions" layoutX="40.0" layoutY="234.0" mnemonicParsing="false" onAction="#btnAdoptionsClicked" prefWidth="250.0" style="-fx-background-color: transparent; -fx-background-radius: 8; -fx-cursor: hand;" text="Adoptions" textFill="#cccccc">
<Button fx:id="btnAdoptions" alignment="CENTER_LEFT" layoutX="40.0" layoutY="234.0" mnemonicParsing="false" onAction="#btnAdoptionsClicked" prefWidth="250.0" style="-fx-background-color: transparent; -fx-background-radius: 10; -fx-cursor: hand; -fx-focus-color: transparent; -fx-faint-focus-color: transparent;" text="Adoptions" textFill="#cbd5e1">
<font>
<Font name="Comic Sans MS Bold" size="14.0" />
<Font name="System" size="13.0" />
</font>
<padding>
<Insets bottom="12.0" left="12.0" right="75.0" top="12.0" />
<Insets bottom="10.0" left="14.0" right="14.0" top="10.0" />
</padding>
</Button>
<Button fx:id="btnAppointments" layoutX="40.0" layoutY="294.0" mnemonicParsing="false" onAction="#btnAppointmentsClicked" prefWidth="250.0" style="-fx-background-color: transparent; -fx-background-radius: 8; -fx-cursor: hand;" text="📅 Appointments" textFill="#cccccc">
<Button fx:id="btnAppointments" alignment="CENTER_LEFT" layoutX="40.0" layoutY="294.0" mnemonicParsing="false" onAction="#btnAppointmentsClicked" prefWidth="250.0" style="-fx-background-color: transparent; -fx-background-radius: 10; -fx-cursor: hand; -fx-focus-color: transparent; -fx-faint-focus-color: transparent;" text="Appointments" textFill="#cbd5e1">
<font>
<Font name="Comic Sans MS Bold" size="14.0" />
<Font name="System" size="13.0" />
</font>
<padding>
<Insets bottom="12.0" left="12.0" right="50.0" top="12.0" />
<Insets bottom="10.0" left="14.0" right="14.0" top="10.0" />
</padding>
</Button>
<Button fx:id="btnServices" layoutX="40.0" layoutY="354.0" mnemonicParsing="false" onAction="#btnServicesClicked" prefWidth="250.0" style="-fx-background-color: transparent; -fx-background-radius: 8; -fx-cursor: hand;" text="Services" textFill="#cccccc">
<Button fx:id="btnServices" alignment="CENTER_LEFT" layoutX="40.0" layoutY="354.0" mnemonicParsing="false" onAction="#btnServicesClicked" prefWidth="250.0" style="-fx-background-color: transparent; -fx-background-radius: 10; -fx-cursor: hand; -fx-focus-color: transparent; -fx-faint-focus-color: transparent;" text="Services" textFill="#cbd5e1">
<font>
<Font name="Comic Sans MS Bold" size="14.0" />
<Font name="System" size="13.0" />
</font>
<padding>
<Insets bottom="12.0" left="12.0" right="85.0" top="12.0" />
<Insets bottom="10.0" left="14.0" right="14.0" top="10.0" />
</padding>
</Button>
<Separator layoutX="40.0" layoutY="156.0" prefWidth="200.0" style="-fx-background-color: #444444;" />
<Button fx:id="btnProducts" layoutX="40.0" layoutY="414.0" mnemonicParsing="false" onAction="#btnProductsClicked" prefWidth="250.0" style="-fx-background-color: transparent; -fx-background-radius: 8; -fx-cursor: hand;" text="📦 Products" textFill="#cccccc">
<Separator layoutX="40.0" layoutY="156.0" prefWidth="200.0" style="-fx-background-color: #444444; -fx-opacity: 0.35;" />
<Label text="PRODUCTS" textFill="#94a3b8">
<font>
<Font name="Comic Sans MS Bold" size="14.0" />
<Font name="System Bold" size="12.0" />
</font>
<padding>
<Insets bottom="12.0" left="12.0" right="80.0" top="12.0" />
<Insets top="8.0" />
</padding>
</Label>
<Button fx:id="btnProducts" alignment="CENTER_LEFT" layoutX="40.0" layoutY="414.0" mnemonicParsing="false" onAction="#btnProductsClicked" prefWidth="250.0" style="-fx-background-color: transparent; -fx-background-radius: 10; -fx-cursor: hand; -fx-focus-color: transparent; -fx-faint-focus-color: transparent;" text="Products" textFill="#cbd5e1">
<font>
<Font name="System" size="13.0" />
</font>
<padding>
<Insets bottom="10.0" left="14.0" right="14.0" top="10.0" />
</padding>
</Button>
<Button fx:id="btnInventory" layoutX="40.0" layoutY="492.0" mnemonicParsing="false" onAction="#btnInventoryClicked" prefWidth="250.0" style="-fx-background-color: transparent; -fx-background-radius: 8; -fx-cursor: hand;" text="📋 Inventory" textFill="#cccccc">
<Button fx:id="btnInventory" alignment="CENTER_LEFT" layoutX="40.0" layoutY="492.0" mnemonicParsing="false" onAction="#btnInventoryClicked" prefWidth="250.0" style="-fx-background-color: transparent; -fx-background-radius: 10; -fx-cursor: hand; -fx-focus-color: transparent; -fx-faint-focus-color: transparent;" text="Inventory" textFill="#cbd5e1">
<font>
<Font name="Comic Sans MS Bold" size="14.0" />
<Font name="System" size="13.0" />
</font>
<padding>
<Insets bottom="12.0" left="12.0" right="74.0" top="12.0" />
<Insets bottom="10.0" left="14.0" right="14.0" top="10.0" />
</padding>
</Button>
<Button fx:id="btnSuppliers" layoutX="40.0" layoutY="552.0" mnemonicParsing="false" onAction="#btnSuppliersClicked" prefWidth="250.0" style="-fx-background-color: transparent; -fx-background-radius: 8; -fx-cursor: hand;" text="🏭 Suppliers" textFill="#cccccc">
<Button fx:id="btnSuppliers" alignment="CENTER_LEFT" layoutX="40.0" layoutY="552.0" mnemonicParsing="false" onAction="#btnSuppliersClicked" prefWidth="250.0" style="-fx-background-color: transparent; -fx-background-radius: 10; -fx-cursor: hand; -fx-focus-color: transparent; -fx-faint-focus-color: transparent;" text="Suppliers" textFill="#cbd5e1">
<font>
<Font name="Comic Sans MS Bold" size="14.0" />
<Font name="System" size="13.0" />
</font>
<padding>
<Insets bottom="12.0" left="12.0" right="76.0" top="12.0" />
<Insets bottom="10.0" left="14.0" right="14.0" top="10.0" />
</padding>
</Button>
<Button fx:id="btnProductSuppliers" layoutX="40.0" layoutY="612.0" mnemonicParsing="false" onAction="#btnProductSuppliersClicked" prefWidth="250.0" style="-fx-background-color: transparent; -fx-background-radius: 8; -fx-cursor: hand;" text="🔗 Product-Suppliers" textFill="#cccccc">
<Button fx:id="btnProductSuppliers" alignment="CENTER_LEFT" layoutX="40.0" layoutY="612.0" mnemonicParsing="false" onAction="#btnProductSuppliersClicked" prefWidth="250.0" style="-fx-background-color: transparent; -fx-background-radius: 10; -fx-cursor: hand; -fx-focus-color: transparent; -fx-faint-focus-color: transparent;" text="Product-Suppliers" textFill="#cbd5e1">
<font>
<Font name="Comic Sans MS Bold" size="14.0" />
<Font name="System" size="13.0" />
</font>
<padding>
<Insets bottom="12.0" left="12.0" right="18.0" top="12.0" />
<Insets bottom="10.0" left="14.0" right="14.0" top="10.0" />
</padding>
</Button>
<Button fx:id="btnSalesHistory" layoutX="40.0" layoutY="672.0" mnemonicParsing="false" onAction="#btnSalesHistoryClicked" prefWidth="250.0" style="-fx-background-color: transparent; -fx-background-radius: 8; -fx-cursor: hand;" text="📄 Sales History" textFill="#cccccc">
<Button fx:id="btnSalesHistory" alignment="CENTER_LEFT" layoutX="40.0" layoutY="672.0" mnemonicParsing="false" onAction="#btnSalesHistoryClicked" prefWidth="250.0" style="-fx-background-color: transparent; -fx-background-radius: 10; -fx-cursor: hand; -fx-focus-color: transparent; -fx-faint-focus-color: transparent;" text="Sales History" textFill="#cbd5e1">
<font>
<Font name="Comic Sans MS Bold" size="14.0" />
<Font name="System" size="13.0" />
</font>
<padding>
<Insets bottom="12.0" left="12.0" right="45.0" top="12.0" />
<Insets bottom="10.0" left="14.0" right="14.0" top="10.0" />
</padding>
</Button>
<Button fx:id="btnPurchaseOrders"
mnemonicParsing="false"
onAction="#btnPurchaseOrdersClicked"
prefWidth="250.0"
style="-fx-background-color: transparent; -fx-background-radius: 8; -fx-cursor: hand;"
text="🧾 Purchase Orders"
textFill="#cccccc">
<Button fx:id="btnPurchaseOrders" alignment="CENTER_LEFT" mnemonicParsing="false" onAction="#btnPurchaseOrdersClicked" prefWidth="250.0" style="-fx-background-color: transparent; -fx-background-radius: 10; -fx-cursor: hand; -fx-focus-color: transparent; -fx-faint-focus-color: transparent;" text="Purchase Orders" textFill="#cbd5e1">
<font>
<Font name="Comic Sans MS Bold" size="14.0" />
<Font name="System" size="13.0" />
</font>
<padding>
<Insets bottom="12.0" left="12.0" right="40.0" top="12.0" />
<Insets bottom="10.0" left="14.0" right="14.0" top="10.0" />
</padding>
</Button>
<Button fx:id="btnLogout" mnemonicParsing="false" onAction="#btnLogoutClicked" prefWidth="250.0" style="-fx-background-color: #34495E; -fx-background-radius: 8; -fx-cursor: hand;" text="🚪 Logout" textFill="#cccccc">
<Region VBox.vgrow="ALWAYS" />
<Button fx:id="btnLogout" alignment="CENTER_LEFT" mnemonicParsing="false" onAction="#btnLogoutClicked" prefWidth="250.0" style="-fx-background-color: rgba(255,255,255,0.08); -fx-background-radius: 10; -fx-cursor: hand; -fx-focus-color: transparent; -fx-faint-focus-color: transparent;" text="Logout" textFill="#e2e8f0">
<font>
<Font name="Comic Sans MS Bold" size="14.0" />
<Font name="System" size="13.0" />
</font>
<padding>
<Insets bottom="12.0" left="12.0" right="94.0" top="12.0" />
<Insets bottom="10.0" left="14.0" right="14.0" top="10.0" />
</padding>
<VBox.margin>
<Insets top="20.0" />
<Insets top="12.0" />
</VBox.margin>
</Button>
</children>

View File

@@ -11,7 +11,7 @@
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>
<VBox minHeight="-Infinity" minWidth="-Infinity" spacing="20.0" style="-fx-font-size: 14px;" xmlns="http://javafx.com/javafx/25" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.example.petshopdesktop.controllers.AdoptionController">
<VBox minHeight="-Infinity" minWidth="-Infinity" 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.AdoptionController">
<padding>
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
</padding>

View File

@@ -11,7 +11,7 @@
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>
<VBox minHeight="-Infinity" minWidth="-Infinity" spacing="20.0" style="-fx-font-size: 14px;" xmlns="http://javafx.com/javafx/25" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.example.petshopdesktop.controllers.AppointmentController">
<VBox minHeight="-Infinity" minWidth="-Infinity" 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.AppointmentController">
<padding>
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
</padding>

View File

@@ -11,7 +11,7 @@
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>
<VBox minHeight="-Infinity" minWidth="-Infinity" spacing="20.0" style="-fx-font-size: 14px;" xmlns="http://javafx.com/javafx/25" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.example.petshopdesktop.controllers.InventoryController">
<VBox minHeight="-Infinity" minWidth="-Infinity" 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.InventoryController">
<padding>
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
</padding>

View File

@@ -11,7 +11,7 @@
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>
<VBox minHeight="-Infinity" minWidth="-Infinity" spacing="20.0" style="-fx-font-size: 14px;" xmlns="http://javafx.com/javafx/25" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.example.petshopdesktop.controllers.PetController">
<VBox minHeight="-Infinity" minWidth="-Infinity" 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.PetController">
<padding>
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
</padding>

View File

@@ -11,7 +11,7 @@
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>
<VBox minHeight="-Infinity" minWidth="-Infinity" spacing="20.0" style="-fx-font-size: 14px;" xmlns="http://javafx.com/javafx/25" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.example.petshopdesktop.controllers.ProductSupplierController">
<VBox minHeight="-Infinity" minWidth="-Infinity" 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.ProductSupplierController">
<padding>
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
</padding>

View File

@@ -11,7 +11,7 @@
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>
<VBox minHeight="-Infinity" minWidth="-Infinity" spacing="20.0" style="-fx-font-size: 14px;" xmlns="http://javafx.com/javafx/25" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.example.petshopdesktop.controllers.ProductController">
<VBox minHeight="-Infinity" minWidth="-Infinity" 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.ProductController">
<padding>
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
</padding>

View File

@@ -7,7 +7,7 @@
<VBox spacing="20.0"
style="-fx-font-size: 14px;"
xmlns="http://javafx.com/javafx/25"
xmlns="http://javafx.com/javafx/17"
xmlns:fx="http://javafx.com/fxml/1"
fx:controller="org.example.petshopdesktop.controllers.PurchaseOrderController">

View File

@@ -1,82 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.Region?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>
<VBox minHeight="-Infinity"
minWidth="-Infinity"
spacing="20"
style="-fx-font-size:14px;"
xmlns="http://javafx.com/javafx/25"
xmlns:fx="http://javafx.com/fxml/1"
fx:controller="org.example.petshopdesktop.controllers.PurchaseOrderdialogController">
<padding>
<Insets top="20" right="20" bottom="20" left="20"/>
</padding>
<!-- HEADER -->
<HBox alignment="CENTER_LEFT" spacing="20">
<Label text="Purchase Orders" textFill="#2c3e50">
<font>
<Font name="System Bold" size="30"/>
</font>
</Label>
<Region HBox.hgrow="ALWAYS"/>
</HBox>
<!-- SEARCH BAR -->
<HBox alignment="CENTER_LEFT"
spacing="10"
style="-fx-background-color:white; -fx-background-radius:14;">
<padding>
<Insets top="10" bottom="10" left="15" right="15"/>
</padding>
<TextField fx:id="txtSearch"
promptText="Search Purchase Orders..."
style="-fx-border-width:0; -fx-background-color:transparent;"
HBox.hgrow="ALWAYS"/>
</HBox>
<!-- TABLE -->
<TableView fx:id="tvPurchaseOrders"
prefHeight="362"
style="-fx-background-color:white; -fx-background-radius:12;"
VBox.vgrow="ALWAYS">
<columns>
<TableColumn fx:id="colOrderId"
text="Order ID"
prefWidth="120"/>
<TableColumn fx:id="colSupplier"
text="Supplier"
prefWidth="250"/>
<TableColumn fx:id="colOrderDate"
text="Order Date"
prefWidth="200"/>
<TableColumn fx:id="colStatus"
text="Status"
prefWidth="180"/>
</columns>
</TableView>
</VBox>

View File

@@ -11,7 +11,7 @@
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>
<VBox minHeight="-Infinity" minWidth="-Infinity" spacing="20.0" style="-fx-font-size: 14px;" xmlns="http://javafx.com/javafx/25" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.example.petshopdesktop.controllers.SaleController">
<VBox minHeight="-Infinity" minWidth="-Infinity" 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.SaleController">
<padding>
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
</padding>

View File

@@ -11,7 +11,7 @@
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>
<VBox minHeight="-Infinity" minWidth="-Infinity" spacing="20.0" style="-fx-font-size: 14px;" xmlns="http://javafx.com/javafx/25" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.example.petshopdesktop.controllers.ServiceController">
<VBox minHeight="-Infinity" minWidth="-Infinity" 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.ServiceController">
<padding>
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
</padding>

View File

@@ -11,7 +11,7 @@
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>
<VBox minHeight="-Infinity" minWidth="-Infinity" spacing="20.0" style="-fx-font-size: 14px;" xmlns="http://javafx.com/javafx/25" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.example.petshopdesktop.controllers.SupplierController">
<VBox minHeight="-Infinity" minWidth="-Infinity" 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.SupplierController">
<padding>
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
</padding>