From f503a7e2b67f98ae4a76455a3a9ea92506d41503 Mon Sep 17 00:00:00 2001 From: Harkamal Randhawa Date: Sat, 28 Feb 2026 14:25:55 -0700 Subject: [PATCH 1/4] Add sales analytics screen --- .../controllers/AnalyticsController.java | 183 ++++++++++++++++ .../controllers/MainLayoutController.java | 17 +- .../petshopdesktop/database/SaleDB.java | 167 +++++++++++++++ .../models/analytics/DailySalesData.java | 21 ++ .../models/analytics/EmployeeSalesData.java | 31 +++ .../models/analytics/PaymentMethodData.java | 25 +++ .../models/analytics/ProductSalesData.java | 25 +++ .../models/analytics/SalesSummary.java | 31 +++ .../petshopdesktop/main-layout-view.fxml | 9 + .../modelviews/analytics-view.fxml | 202 ++++++++++++++++++ 10 files changed, 710 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/example/petshopdesktop/controllers/AnalyticsController.java create mode 100644 src/main/java/org/example/petshopdesktop/models/analytics/DailySalesData.java create mode 100644 src/main/java/org/example/petshopdesktop/models/analytics/EmployeeSalesData.java create mode 100644 src/main/java/org/example/petshopdesktop/models/analytics/PaymentMethodData.java create mode 100644 src/main/java/org/example/petshopdesktop/models/analytics/ProductSalesData.java create mode 100644 src/main/java/org/example/petshopdesktop/models/analytics/SalesSummary.java create mode 100644 src/main/resources/org/example/petshopdesktop/modelviews/analytics-view.fxml diff --git a/src/main/java/org/example/petshopdesktop/controllers/AnalyticsController.java b/src/main/java/org/example/petshopdesktop/controllers/AnalyticsController.java new file mode 100644 index 00000000..468c4132 --- /dev/null +++ b/src/main/java/org/example/petshopdesktop/controllers/AnalyticsController.java @@ -0,0 +1,183 @@ +package org.example.petshopdesktop.controllers; + +import javafx.collections.ObservableList; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.chart.*; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import org.example.petshopdesktop.auth.UserSession; +import org.example.petshopdesktop.database.SaleDB; +import org.example.petshopdesktop.models.analytics.*; +import org.example.petshopdesktop.util.ActivityLogger; + +import java.text.NumberFormat; +import java.time.format.DateTimeFormatter; +import java.util.Locale; + +public class AnalyticsController { + + @FXML + private Button btnRefresh; + + @FXML + private Label lblError; + + @FXML + private Label lblTotalRevenue; + + @FXML + private Label lblTotalTransactions; + + @FXML + private Label lblAvgTransaction; + + @FXML + private Label lblTotalItems; + + @FXML + private LineChart chartSalesOverTime; + + @FXML + private BarChart chartTopRevenue; + + @FXML + private BarChart chartTopQuantity; + + @FXML + private PieChart chartPaymentMethods; + + @FXML + private BarChart chartEmployeePerformance; + + private final NumberFormat currency = NumberFormat.getCurrencyInstance(Locale.CANADA); + private final NumberFormat wholeNumber = NumberFormat.getIntegerInstance(); + + @FXML + public void initialize() { + if (!UserSession.getInstance().isAdmin()) { + lblError.setText("Access restricted to administrators only."); + lblError.setVisible(true); + disableAllCharts(); + return; + } + + configureCharts(); + loadAnalyticsData(); + } + + private void disableAllCharts() { + chartSalesOverTime.setVisible(false); + chartTopRevenue.setVisible(false); + chartTopQuantity.setVisible(false); + chartPaymentMethods.setVisible(false); + chartEmployeePerformance.setVisible(false); + btnRefresh.setDisable(true); + } + + private void configureCharts() { + chartSalesOverTime.setAnimated(true); + chartTopRevenue.setAnimated(true); + chartTopQuantity.setAnimated(true); + chartPaymentMethods.setAnimated(true); + chartEmployeePerformance.setAnimated(true); + } + + private void loadAnalyticsData() { + lblError.setVisible(false); + try { + loadSummaryData(); + loadSalesOverTime(); + loadTopProductsByRevenue(); + loadTopProductsByQuantity(); + loadPaymentMethodDistribution(); + loadEmployeePerformance(); + } catch (Exception e) { + ActivityLogger.getInstance().logException("AnalyticsController.loadAnalyticsData", e, "Loading analytics data"); + lblError.setText("Error loading analytics data. Please try again."); + lblError.setVisible(true); + } + } + + private void loadSummaryData() throws Exception { + SalesSummary summary = SaleDB.getSalesSummary(); + if (summary != null) { + lblTotalRevenue.setText(currency.format(summary.getTotalRevenue())); + lblTotalTransactions.setText(wholeNumber.format(summary.getTotalTransactions())); + lblAvgTransaction.setText(currency.format(summary.getAvgTransactionValue())); + lblTotalItems.setText(wholeNumber.format(summary.getTotalItemsSold())); + } + } + + private void loadSalesOverTime() throws Exception { + ObservableList data = SaleDB.getDailySalesRevenue(); + XYChart.Series series = new XYChart.Series<>(); + series.setName("Daily Revenue"); + + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMM dd"); + for (DailySalesData dailySale : data) { + String dateStr = dailySale.getDate().format(formatter); + series.getData().add(new XYChart.Data<>(dateStr, dailySale.getRevenue())); + } + + chartSalesOverTime.getData().clear(); + chartSalesOverTime.getData().add(series); + } + + private void loadTopProductsByRevenue() throws Exception { + ObservableList data = SaleDB.getTopProductsByRevenue(10); + XYChart.Series series = new XYChart.Series<>(); + series.setName("Revenue"); + + for (ProductSalesData product : data) { + series.getData().add(new XYChart.Data<>(product.getTotalRevenue(), product.getProductName())); + } + + chartTopRevenue.getData().clear(); + chartTopRevenue.getData().add(series); + } + + private void loadTopProductsByQuantity() throws Exception { + ObservableList data = SaleDB.getTopProductsByQuantity(10); + XYChart.Series series = new XYChart.Series<>(); + series.setName("Quantity"); + + for (ProductSalesData product : data) { + series.getData().add(new XYChart.Data<>(product.getTotalQuantity(), product.getProductName())); + } + + chartTopQuantity.getData().clear(); + chartTopQuantity.getData().add(series); + } + + private void loadPaymentMethodDistribution() throws Exception { + ObservableList data = SaleDB.getPaymentMethodDistribution(); + chartPaymentMethods.getData().clear(); + + for (PaymentMethodData payment : data) { + PieChart.Data slice = new PieChart.Data( + payment.getPaymentMethod() + " (" + payment.getTransactionCount() + ")", + payment.getTransactionCount() + ); + chartPaymentMethods.getData().add(slice); + } + } + + private void loadEmployeePerformance() throws Exception { + ObservableList data = SaleDB.getEmployeeSalesPerformance(); + XYChart.Series series = new XYChart.Series<>(); + series.setName("Revenue"); + + for (EmployeeSalesData employee : data) { + series.getData().add(new XYChart.Data<>(employee.getEmployeeName(), employee.getTotalRevenue())); + } + + chartEmployeePerformance.getData().clear(); + chartEmployeePerformance.getData().add(series); + } + + @FXML + void handleRefresh(ActionEvent event) { + loadAnalyticsData(); + } +} diff --git a/src/main/java/org/example/petshopdesktop/controllers/MainLayoutController.java b/src/main/java/org/example/petshopdesktop/controllers/MainLayoutController.java index 76294154..8de3e6c3 100644 --- a/src/main/java/org/example/petshopdesktop/controllers/MainLayoutController.java +++ b/src/main/java/org/example/petshopdesktop/controllers/MainLayoutController.java @@ -66,6 +66,9 @@ public class MainLayoutController { @FXML private Button btnStaffAccounts; + @FXML + private Button btnAnalytics; + @FXML private Label lblUsername; @@ -129,6 +132,12 @@ public class MainLayoutController { updateButtons(btnStaffAccounts); } + @FXML + void btnAnalyticsClicked(ActionEvent event) { + loadView("analytics-view.fxml"); + updateButtons(btnAnalytics); + } + @FXML void btnServicesClicked(ActionEvent event) { loadView("service-view.fxml"); @@ -195,6 +204,11 @@ public class MainLayoutController { btnStaffAccounts.setManaged(isAdmin); } + if (btnAnalytics != null) { + btnAnalytics.setVisible(isAdmin); + btnAnalytics.setManaged(isAdmin); + } + btnSalesHistory.setText(isAdmin ? "Sales History" : "Sales"); @@ -234,7 +248,8 @@ public class MainLayoutController { btnProductSuppliers, btnProducts, btnPurchaseOrders, - btnStaffAccounts + btnStaffAccounts, + btnAnalytics }; for (Button button : buttons) { diff --git a/src/main/java/org/example/petshopdesktop/database/SaleDB.java b/src/main/java/org/example/petshopdesktop/database/SaleDB.java index 7c8a541c..bedd6566 100644 --- a/src/main/java/org/example/petshopdesktop/database/SaleDB.java +++ b/src/main/java/org/example/petshopdesktop/database/SaleDB.java @@ -5,9 +5,11 @@ import javafx.collections.ObservableList; import org.example.petshopdesktop.DTOs.SaleDTO; import org.example.petshopdesktop.models.SaleCartItem; import org.example.petshopdesktop.models.SaleLineItem; +import org.example.petshopdesktop.models.analytics.*; import org.example.petshopdesktop.util.ActivityLogger; import java.sql.*; +import java.time.LocalDate; public class SaleDB { @@ -230,4 +232,169 @@ public class SaleDB { conn.close(); } } + + public static ObservableList getDailySalesRevenue() throws SQLException { + ObservableList dailySales = FXCollections.observableArrayList(); + Connection conn = ConnectionDB.getConnection(); + + String sql = """ + SELECT DATE(s.saleDate) as saleDate, SUM(s.totalAmount) as revenue + FROM sale s + GROUP BY DATE(s.saleDate) + ORDER BY saleDate ASC + """; + + Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery(sql); + + while (rs.next()) { + LocalDate date = rs.getDate("saleDate").toLocalDate(); + double revenue = rs.getDouble("revenue"); + dailySales.add(new DailySalesData(date, revenue)); + } + + conn.close(); + return dailySales; + } + + public static ObservableList getTopProductsByRevenue(int limit) throws SQLException { + ObservableList products = FXCollections.observableArrayList(); + Connection conn = ConnectionDB.getConnection(); + + String sql = """ + SELECT p.prodName, SUM(si.quantity * si.unitPrice) as totalRevenue + FROM saleItem si + JOIN product p ON si.prodId = p.prodId + GROUP BY p.prodId, p.prodName + ORDER BY totalRevenue DESC + LIMIT ? + """; + + PreparedStatement pstmt = conn.prepareStatement(sql); + pstmt.setInt(1, limit); + ResultSet rs = pstmt.executeQuery(); + + while (rs.next()) { + String productName = rs.getString("prodName"); + double totalRevenue = rs.getDouble("totalRevenue"); + products.add(new ProductSalesData(productName, 0, totalRevenue)); + } + + conn.close(); + return products; + } + + public static ObservableList getTopProductsByQuantity(int limit) throws SQLException { + ObservableList products = FXCollections.observableArrayList(); + Connection conn = ConnectionDB.getConnection(); + + String sql = """ + SELECT p.prodName, SUM(si.quantity) as totalQuantity, + SUM(si.quantity * si.unitPrice) as totalRevenue + FROM saleItem si + JOIN product p ON si.prodId = p.prodId + GROUP BY p.prodId, p.prodName + ORDER BY totalQuantity DESC + LIMIT ? + """; + + PreparedStatement pstmt = conn.prepareStatement(sql); + pstmt.setInt(1, limit); + ResultSet rs = pstmt.executeQuery(); + + while (rs.next()) { + String productName = rs.getString("prodName"); + int totalQuantity = rs.getInt("totalQuantity"); + double totalRevenue = rs.getDouble("totalRevenue"); + products.add(new ProductSalesData(productName, totalQuantity, totalRevenue)); + } + + conn.close(); + return products; + } + + public static ObservableList getPaymentMethodDistribution() throws SQLException { + ObservableList paymentMethods = FXCollections.observableArrayList(); + Connection conn = ConnectionDB.getConnection(); + + String sql = """ + SELECT s.paymentMethod, COUNT(*) as transactionCount, + SUM(s.totalAmount) as totalRevenue + FROM sale s + GROUP BY s.paymentMethod + ORDER BY totalRevenue DESC + """; + + Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery(sql); + + while (rs.next()) { + String paymentMethod = rs.getString("paymentMethod"); + int transactionCount = rs.getInt("transactionCount"); + double totalRevenue = rs.getDouble("totalRevenue"); + paymentMethods.add(new PaymentMethodData(paymentMethod, transactionCount, totalRevenue)); + } + + conn.close(); + return paymentMethods; + } + + public static ObservableList getEmployeeSalesPerformance() throws SQLException { + ObservableList employees = FXCollections.observableArrayList(); + Connection conn = ConnectionDB.getConnection(); + + String sql = """ + SELECT CONCAT(e.firstName, ' ', e.lastName) as employeeName, + COUNT(DISTINCT s.saleId) as transactionCount, + SUM(s.totalAmount) as totalRevenue, + COALESCE(SUM(si.quantity), 0) as totalItemsSold + FROM sale s + JOIN employee e ON s.employeeId = e.employeeId + LEFT JOIN saleItem si ON s.saleId = si.saleId + GROUP BY e.employeeId, e.firstName, e.lastName + ORDER BY totalRevenue DESC + """; + + Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery(sql); + + while (rs.next()) { + String employeeName = rs.getString("employeeName"); + int transactionCount = rs.getInt("transactionCount"); + double totalRevenue = rs.getDouble("totalRevenue"); + int totalItemsSold = rs.getInt("totalItemsSold"); + employees.add(new EmployeeSalesData(employeeName, transactionCount, totalRevenue, totalItemsSold)); + } + + conn.close(); + return employees; + } + + public static SalesSummary getSalesSummary() throws SQLException { + Connection conn = ConnectionDB.getConnection(); + + String sql = """ + SELECT COUNT(DISTINCT s.saleId) as totalTransactions, + COALESCE(SUM(s.totalAmount), 0) as totalRevenue, + COALESCE(AVG(s.totalAmount), 0) as avgTransactionValue, + COALESCE(SUM(si.quantity), 0) as totalItemsSold + FROM sale s + LEFT JOIN saleItem si ON s.saleId = si.saleId + """; + + Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery(sql); + + SalesSummary summary = null; + if (rs.next()) { + int totalTransactions = rs.getInt("totalTransactions"); + double totalRevenue = rs.getDouble("totalRevenue"); + double avgTransactionValue = rs.getDouble("avgTransactionValue"); + int totalItemsSold = rs.getInt("totalItemsSold"); + summary = new SalesSummary(totalTransactions, totalRevenue, avgTransactionValue, totalItemsSold); + } + + conn.close(); + return summary; + } } diff --git a/src/main/java/org/example/petshopdesktop/models/analytics/DailySalesData.java b/src/main/java/org/example/petshopdesktop/models/analytics/DailySalesData.java new file mode 100644 index 00000000..1f41b8f2 --- /dev/null +++ b/src/main/java/org/example/petshopdesktop/models/analytics/DailySalesData.java @@ -0,0 +1,21 @@ +package org.example.petshopdesktop.models.analytics; + +import java.time.LocalDate; + +public class DailySalesData { + private final LocalDate date; + private final double revenue; + + public DailySalesData(LocalDate date, double revenue) { + this.date = date; + this.revenue = revenue; + } + + public LocalDate getDate() { + return date; + } + + public double getRevenue() { + return revenue; + } +} diff --git a/src/main/java/org/example/petshopdesktop/models/analytics/EmployeeSalesData.java b/src/main/java/org/example/petshopdesktop/models/analytics/EmployeeSalesData.java new file mode 100644 index 00000000..a954d86b --- /dev/null +++ b/src/main/java/org/example/petshopdesktop/models/analytics/EmployeeSalesData.java @@ -0,0 +1,31 @@ +package org.example.petshopdesktop.models.analytics; + +public class EmployeeSalesData { + private final String employeeName; + private final int transactionCount; + private final double totalRevenue; + private final int totalItemsSold; + + public EmployeeSalesData(String employeeName, int transactionCount, double totalRevenue, int totalItemsSold) { + this.employeeName = employeeName; + this.transactionCount = transactionCount; + this.totalRevenue = totalRevenue; + this.totalItemsSold = totalItemsSold; + } + + public String getEmployeeName() { + return employeeName; + } + + public int getTransactionCount() { + return transactionCount; + } + + public double getTotalRevenue() { + return totalRevenue; + } + + public int getTotalItemsSold() { + return totalItemsSold; + } +} diff --git a/src/main/java/org/example/petshopdesktop/models/analytics/PaymentMethodData.java b/src/main/java/org/example/petshopdesktop/models/analytics/PaymentMethodData.java new file mode 100644 index 00000000..de45c727 --- /dev/null +++ b/src/main/java/org/example/petshopdesktop/models/analytics/PaymentMethodData.java @@ -0,0 +1,25 @@ +package org.example.petshopdesktop.models.analytics; + +public class PaymentMethodData { + private final String paymentMethod; + private final int transactionCount; + private final double totalRevenue; + + public PaymentMethodData(String paymentMethod, int transactionCount, double totalRevenue) { + this.paymentMethod = paymentMethod; + this.transactionCount = transactionCount; + this.totalRevenue = totalRevenue; + } + + public String getPaymentMethod() { + return paymentMethod; + } + + public int getTransactionCount() { + return transactionCount; + } + + public double getTotalRevenue() { + return totalRevenue; + } +} diff --git a/src/main/java/org/example/petshopdesktop/models/analytics/ProductSalesData.java b/src/main/java/org/example/petshopdesktop/models/analytics/ProductSalesData.java new file mode 100644 index 00000000..3723eee6 --- /dev/null +++ b/src/main/java/org/example/petshopdesktop/models/analytics/ProductSalesData.java @@ -0,0 +1,25 @@ +package org.example.petshopdesktop.models.analytics; + +public class ProductSalesData { + private final String productName; + private final int totalQuantity; + private final double totalRevenue; + + public ProductSalesData(String productName, int totalQuantity, double totalRevenue) { + this.productName = productName; + this.totalQuantity = totalQuantity; + this.totalRevenue = totalRevenue; + } + + public String getProductName() { + return productName; + } + + public int getTotalQuantity() { + return totalQuantity; + } + + public double getTotalRevenue() { + return totalRevenue; + } +} diff --git a/src/main/java/org/example/petshopdesktop/models/analytics/SalesSummary.java b/src/main/java/org/example/petshopdesktop/models/analytics/SalesSummary.java new file mode 100644 index 00000000..65e9f9c1 --- /dev/null +++ b/src/main/java/org/example/petshopdesktop/models/analytics/SalesSummary.java @@ -0,0 +1,31 @@ +package org.example.petshopdesktop.models.analytics; + +public class SalesSummary { + private final int totalTransactions; + private final double totalRevenue; + private final double avgTransactionValue; + private final int totalItemsSold; + + public SalesSummary(int totalTransactions, double totalRevenue, double avgTransactionValue, int totalItemsSold) { + this.totalTransactions = totalTransactions; + this.totalRevenue = totalRevenue; + this.avgTransactionValue = avgTransactionValue; + this.totalItemsSold = totalItemsSold; + } + + public int getTotalTransactions() { + return totalTransactions; + } + + public double getTotalRevenue() { + return totalRevenue; + } + + public double getAvgTransactionValue() { + return avgTransactionValue; + } + + public int getTotalItemsSold() { + return totalItemsSold; + } +} diff --git a/src/main/resources/org/example/petshopdesktop/main-layout-view.fxml b/src/main/resources/org/example/petshopdesktop/main-layout-view.fxml index 28f412c5..e6765c1f 100644 --- a/src/main/resources/org/example/petshopdesktop/main-layout-view.fxml +++ b/src/main/resources/org/example/petshopdesktop/main-layout-view.fxml @@ -143,6 +143,15 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 2f653acac653d3c5fcfbda8ec5b0dbd6f1565156 Mon Sep 17 00:00:00 2001 From: Harkamal Randhawa Date: Sat, 28 Feb 2026 14:34:11 -0700 Subject: [PATCH 2/4] Fix FXML errors and make Analytics accessible to all users --- log.txt | 11 ++++++++++ .../controllers/AnalyticsController.java | 7 ------- .../controllers/MainLayoutController.java | 9 ++------- .../petshopdesktop/main-layout-view.fxml | 20 ++++++++++--------- .../modelviews/analytics-view.fxml | 6 +++--- 5 files changed, 27 insertions(+), 26 deletions(-) create mode 100644 log.txt diff --git a/log.txt b/log.txt new file mode 100644 index 00000000..273b4d86 --- /dev/null +++ b/log.txt @@ -0,0 +1,11 @@ +[2026-02-25 11:54:59] [INSERT] DB_INSERT | Table: sale | ID: Sale ID: 22 | Details: Created sale with 1 items, total: $240.00 +[2026-02-25 11:55:37] [INSERT] DB_INSERT | Table: sale | ID: Sale ID: 23 | Details: Created sale with 1 items, total: $240.00 +[2026-02-28 14:29:17] [ERROR] EXCEPTION | Location: ConnectionDB.getConnection | Type: CommunicationsException | Message: Communications link failure + +The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server. | Context: Establishing database connection +[2026-02-28 14:29:17] [ERROR] EXCEPTION | Location: ConnectionDB.getConnection | Type: CommunicationsException | Message: Communications link failure + +The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server. | Context: Establishing database connection +[2026-02-28 14:30:29] [ERROR] EXCEPTION | Location: MainLayoutController.loadView | Type: LoadException | Message: +/home/user/Documents/SAIT/Winter%202026/intellij/group-2-threaded-project-petshop-desktop/target/classes/org/example/petshopdesktop/modelviews/analytics-view.fxml:58 + | Context: Loading view: analytics-view.fxml diff --git a/src/main/java/org/example/petshopdesktop/controllers/AnalyticsController.java b/src/main/java/org/example/petshopdesktop/controllers/AnalyticsController.java index 468c4132..0624effb 100644 --- a/src/main/java/org/example/petshopdesktop/controllers/AnalyticsController.java +++ b/src/main/java/org/example/petshopdesktop/controllers/AnalyticsController.java @@ -55,13 +55,6 @@ public class AnalyticsController { @FXML public void initialize() { - if (!UserSession.getInstance().isAdmin()) { - lblError.setText("Access restricted to administrators only."); - lblError.setVisible(true); - disableAllCharts(); - return; - } - configureCharts(); loadAnalyticsData(); } diff --git a/src/main/java/org/example/petshopdesktop/controllers/MainLayoutController.java b/src/main/java/org/example/petshopdesktop/controllers/MainLayoutController.java index 8de3e6c3..11609366 100644 --- a/src/main/java/org/example/petshopdesktop/controllers/MainLayoutController.java +++ b/src/main/java/org/example/petshopdesktop/controllers/MainLayoutController.java @@ -174,8 +174,8 @@ public class MainLayoutController { public void initialize() { applyRBAC(); - loadView("pet-view.fxml"); - updateButtons(btnPets); + loadView("analytics-view.fxml"); + updateButtons(btnAnalytics); } private void applyRBAC() { @@ -204,11 +204,6 @@ public class MainLayoutController { btnStaffAccounts.setManaged(isAdmin); } - if (btnAnalytics != null) { - btnAnalytics.setVisible(isAdmin); - btnAnalytics.setManaged(isAdmin); - } - btnSalesHistory.setText(isAdmin ? "Sales History" : "Sales"); diff --git a/src/main/resources/org/example/petshopdesktop/main-layout-view.fxml b/src/main/resources/org/example/petshopdesktop/main-layout-view.fxml index e6765c1f..57a14cf1 100644 --- a/src/main/resources/org/example/petshopdesktop/main-layout-view.fxml +++ b/src/main/resources/org/example/petshopdesktop/main-layout-view.fxml @@ -32,6 +32,17 @@ + + + +