Merge pull request #31 from RecentRunner/analytics-screen

Analytics screen also added the blank chat screen for later
This commit is contained in:
2026-03-01 02:07:49 +00:00
committed by GitHub
16 changed files with 975 additions and 174 deletions

14
log.txt Normal file
View File

@@ -0,0 +1,14 @@
[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
[2026-02-28 14:35:14] [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

View File

@@ -0,0 +1,176 @@
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<String, Number> chartSalesOverTime;
@FXML
private BarChart<Number, String> chartTopRevenue;
@FXML
private BarChart<Number, String> chartTopQuantity;
@FXML
private PieChart chartPaymentMethods;
@FXML
private BarChart<String, Number> chartEmployeePerformance;
private final NumberFormat currency = NumberFormat.getCurrencyInstance(Locale.CANADA);
private final NumberFormat wholeNumber = NumberFormat.getIntegerInstance();
@FXML
public void initialize() {
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<DailySalesData> data = SaleDB.getDailySalesRevenue();
XYChart.Series<String, Number> 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<ProductSalesData> data = SaleDB.getTopProductsByRevenue(10);
XYChart.Series<Number, String> 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<ProductSalesData> data = SaleDB.getTopProductsByQuantity(10);
XYChart.Series<Number, String> 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<PaymentMethodData> 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<EmployeeSalesData> data = SaleDB.getEmployeeSalesPerformance();
XYChart.Series<String, Number> 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();
}
}

View File

@@ -29,9 +29,6 @@ public class LoginController {
@FXML
private Label lblError;
@FXML
private Button btnCreateStaff;
@FXML
public void initialize() {
lblError.setText("");
@@ -93,24 +90,6 @@ public class LoginController {
}
}
@FXML
void btnCreateStaffClicked(ActionEvent event) {
lblError.setText("");
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/org/example/petshopdesktop/dialogviews/staff-register-dialog-view.fxml"));
Stage dialog = new Stage();
dialog.initOwner(txtUsername.getScene().getWindow());
dialog.initModality(Modality.APPLICATION_MODAL);
dialog.setTitle("Create Staff Account");
dialog.setScene(new Scene(loader.load()));
dialog.setResizable(false);
dialog.showAndWait();
} catch (Exception e) {
ActivityLogger.getInstance().logException("LoginController.btnCreateStaffClicked", e, "Opening staff register dialog");
lblError.setText("Could not open staff account creation.");
}
}
private void openMainLayout() {
try {
FXMLLoader loader = new FXMLLoader(

View File

@@ -8,6 +8,7 @@ import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.Separator;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import org.example.petshopdesktop.auth.UserSession;
@@ -66,12 +67,24 @@ public class MainLayoutController {
@FXML
private Button btnStaffAccounts;
@FXML
private Button btnAnalytics;
@FXML
private Button btnChat;
@FXML
private Label lblUsername;
@FXML
private Label lblRole;
@FXML
private Label lblAdminSection;
@FXML
private Separator separatorAdmin;
@FXML
private StackPane spContentArea;
@@ -129,6 +142,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");
@@ -141,6 +160,12 @@ public class MainLayoutController {
updateButtons(btnSuppliers);
}
@FXML
void btnChatClicked(ActionEvent event) {
loadView("chat-view.fxml");
updateButtons(btnChat);
}
@FXML
void btnLogoutClicked(ActionEvent event) {
UserSession.getInstance().logout();
@@ -165,8 +190,8 @@ public class MainLayoutController {
public void initialize() {
applyRBAC();
loadView("pet-view.fxml");
updateButtons(btnPets);
loadView("analytics-view.fxml");
updateButtons(btnAnalytics);
}
private void applyRBAC() {
@@ -195,6 +220,16 @@ public class MainLayoutController {
btnStaffAccounts.setManaged(isAdmin);
}
if (lblAdminSection != null) {
lblAdminSection.setVisible(isAdmin);
lblAdminSection.setManaged(isAdmin);
}
if (separatorAdmin != null) {
separatorAdmin.setVisible(isAdmin);
separatorAdmin.setManaged(isAdmin);
}
btnSalesHistory.setText(isAdmin ? "Sales History" : "Sales");
@@ -234,7 +269,9 @@ public class MainLayoutController {
btnProductSuppliers,
btnProducts,
btnPurchaseOrders,
btnStaffAccounts
btnStaffAccounts,
btnAnalytics,
btnChat
};
for (Button button : buttons) {

View File

@@ -5,11 +5,16 @@ import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.stage.Modality;
import javafx.stage.Stage;
import org.example.petshopdesktop.auth.UserSession;
import org.example.petshopdesktop.database.UserDB;
import org.example.petshopdesktop.models.StaffAccount;
@@ -46,6 +51,9 @@ public class StaffAccountsController {
@FXML
private Label lblError;
@FXML
private Button btnCreateAccount;
private final ObservableList<StaffAccount> staffAccounts = FXCollections.observableArrayList();
private FilteredList<StaffAccount> filtered;
@@ -66,6 +74,7 @@ public class StaffAccountsController {
if (!UserSession.getInstance().isAdmin()) {
lblError.setText("Access restricted.");
tvStaff.setDisable(true);
btnCreateAccount.setDisable(true);
return;
}
@@ -77,6 +86,25 @@ public class StaffAccountsController {
refresh();
}
@FXML
void btnCreateAccountClicked(ActionEvent event) {
lblError.setText("");
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/org/example/petshopdesktop/dialogviews/staff-register-dialog-view.fxml"));
Stage dialog = new Stage();
dialog.initOwner(tvStaff.getScene().getWindow());
dialog.initModality(Modality.APPLICATION_MODAL);
dialog.setTitle("Create Staff Account");
dialog.setScene(new Scene(loader.load()));
dialog.setResizable(false);
dialog.showAndWait();
refresh();
} catch (Exception e) {
ActivityLogger.getInstance().logException("StaffAccountsController.btnCreateAccountClicked", e, "Opening staff register dialog");
lblError.setText("Could not open staff account creation.");
}
}
private void refresh() {
lblError.setText("");
try {

View File

@@ -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<DailySalesData> getDailySalesRevenue() throws SQLException {
ObservableList<DailySalesData> 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<ProductSalesData> getTopProductsByRevenue(int limit) throws SQLException {
ObservableList<ProductSalesData> 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<ProductSalesData> getTopProductsByQuantity(int limit) throws SQLException {
ObservableList<ProductSalesData> 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<PaymentMethodData> getPaymentMethodDistribution() throws SQLException {
ObservableList<PaymentMethodData> 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<EmployeeSalesData> getEmployeeSalesPerformance() throws SQLException {
ObservableList<EmployeeSalesData> 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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -76,17 +76,5 @@
<Insets top="8.0" />
</VBox.margin>
</Button>
<Button fx:id="btnCreateStaff" mnemonicParsing="false" onAction="#btnCreateStaffClicked"
prefWidth="280.0"
style="-fx-background-color: transparent; -fx-text-fill: #D5DDE6; -fx-cursor: hand; -fx-underline: true;"
text="Create Staff Account">
<font>
<Font name="System Bold" size="13.0" />
</font>
<VBox.margin>
<Insets top="4.0" />
</VBox.margin>
</Button>
</children>
</VBox>

View File

@@ -3,6 +3,7 @@
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.control.Separator?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.Region?>
@@ -12,152 +13,181 @@
<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="10.0" style="-fx-background-color: #2C3E50;" BorderPane.alignment="CENTER">
<padding>
<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="System Bold" size="24.0" />
</font>
</Label>
<Label fx:id="lblRole" text="Leon's Petstore" textFill="#ffe66d">
<font>
<Font name="System" size="14.0" />
</font>
<padding>
<Insets bottom="8.0" />
</padding>
</Label>
<Separator prefWidth="200.0" style="-fx-background-color: #444444; -fx-opacity: 0.35;" />
<ScrollPane fitToWidth="true" hbarPolicy="NEVER" style="-fx-background-color: #2C3E50; -fx-background: #2C3E50;" BorderPane.alignment="CENTER">
<VBox prefWidth="200.0" spacing="6.0" style="-fx-background-color: #2C3E50;">
<padding>
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0" />
</padding>
<children>
<Label fx:id="lblUsername" text="Name" textFill="WHITE">
<font>
<Font name="System Bold" size="18.0" />
</font>
</Label>
<Label fx:id="lblRole" text="Leon's Petstore" textFill="#ffe66d">
<font>
<Font name="System" size="12.0" />
</font>
<padding>
<Insets bottom="4.0" />
</padding>
</Label>
<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 top="8.0" />
</padding>
</Label>
<Label text="BUSINESS" textFill="#94a3b8">
<font>
<Font name="System Bold" size="10.0" />
</font>
<padding>
<Insets top="4.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="System" size="13.0" />
</font>
</Button>
<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="System" size="13.0" />
</font>
<padding>
<Insets bottom="10.0" left="14.0" right="14.0" top="10.0" />
</padding>
</Button>
<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="System" size="13.0" />
</font>
<padding>
<Insets bottom="10.0" left="14.0" right="14.0" top="10.0" />
</padding>
</Button>
<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="System" size="13.0" />
</font>
<padding>
<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; -fx-opacity: 0.35;" />
<Button fx:id="btnAnalytics" alignment="CENTER_LEFT" mnemonicParsing="false" onAction="#btnAnalyticsClicked" maxWidth="Infinity" style="-fx-background-color: transparent; -fx-background-radius: 8; -fx-cursor: hand; -fx-focus-color: transparent; -fx-faint-focus-color: transparent;" text="Analytics" textFill="#cbd5e1">
<font>
<Font name="System" size="12.0" />
</font>
<padding>
<Insets bottom="8.0" left="10.0" right="10.0" top="8.0" />
</padding>
</Button>
<Button fx:id="btnSalesHistory" alignment="CENTER_LEFT" mnemonicParsing="false" onAction="#btnSalesHistoryClicked" maxWidth="Infinity" style="-fx-background-color: transparent; -fx-background-radius: 8; -fx-cursor: hand; -fx-focus-color: transparent; -fx-faint-focus-color: transparent;" text="Sales History" textFill="#cbd5e1">
<font>
<Font name="System" size="12.0" />
</font>
<padding>
<Insets bottom="8.0" left="10.0" right="10.0" top="8.0" />
</padding>
</Button>
<Button fx:id="btnAppointments" alignment="CENTER_LEFT" mnemonicParsing="false" onAction="#btnAppointmentsClicked" maxWidth="Infinity" style="-fx-background-color: transparent; -fx-background-radius: 8; -fx-cursor: hand; -fx-focus-color: transparent; -fx-faint-focus-color: transparent;" text="Appointments" textFill="#cbd5e1">
<font>
<Font name="System" size="12.0" />
</font>
<padding>
<Insets bottom="8.0" left="10.0" right="10.0" top="8.0" />
</padding>
</Button>
<Button fx:id="btnServices" alignment="CENTER_LEFT" mnemonicParsing="false" onAction="#btnServicesClicked" maxWidth="Infinity" style="-fx-background-color: transparent; -fx-background-radius: 8; -fx-cursor: hand; -fx-focus-color: transparent; -fx-faint-focus-color: transparent;" text="Services" textFill="#cbd5e1">
<font>
<Font name="System" size="12.0" />
</font>
<padding>
<Insets bottom="8.0" left="10.0" right="10.0" top="8.0" />
</padding>
</Button>
<Button fx:id="btnChat" alignment="CENTER_LEFT" mnemonicParsing="false" onAction="#btnChatClicked" maxWidth="Infinity" style="-fx-background-color: transparent; -fx-background-radius: 8; -fx-cursor: hand; -fx-focus-color: transparent; -fx-faint-focus-color: transparent;" text="Chat" textFill="#cbd5e1">
<font>
<Font name="System" size="12.0" />
</font>
<padding>
<Insets bottom="8.0" left="10.0" right="10.0" top="8.0" />
</padding>
</Button>
<Label text="PRODUCTS" textFill="#94a3b8">
<font>
<Font name="System Bold" size="12.0" />
</font>
<padding>
<Insets top="8.0" />
</padding>
</Label>
<Separator prefWidth="200.0" style="-fx-background-color: #444444; -fx-opacity: 0.35;" />
<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" 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="System" size="13.0" />
</font>
<padding>
<Insets bottom="10.0" left="14.0" right="14.0" top="10.0" />
</padding>
</Button>
<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="System" size="13.0" />
</font>
<padding>
<Insets bottom="10.0" left="14.0" right="14.0" top="10.0" />
</padding>
</Button>
<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="System" size="13.0" />
</font>
<padding>
<Insets bottom="10.0" left="14.0" right="14.0" top="10.0" />
</padding>
</Button>
<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="System" size="13.0" />
</font>
<padding>
<Insets bottom="10.0" left="14.0" right="14.0" top="10.0" />
</padding>
</Button>
<Label text="STORE" textFill="#94a3b8">
<font>
<Font name="System Bold" size="10.0" />
</font>
<padding>
<Insets top="4.0" />
</padding>
</Label>
<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="System" size="13.0" />
</font>
<padding>
<Insets bottom="10.0" left="14.0" right="14.0" top="10.0" />
</padding>
</Button>
<Button fx:id="btnPets" alignment="CENTER_LEFT" mnemonicParsing="false" onAction="#btnPetsClicked" maxWidth="Infinity" style="-fx-background-color: transparent; -fx-background-radius: 8; -fx-cursor: hand; -fx-focus-color: transparent; -fx-faint-focus-color: transparent;" text="Pets" textFill="#cbd5e1">
<font>
<Font name="System" size="12.0" />
</font>
<padding>
<Insets bottom="8.0" left="10.0" right="10.0" top="8.0" />
</padding>
</Button>
<Button fx:id="btnAdoptions" alignment="CENTER_LEFT" mnemonicParsing="false" onAction="#btnAdoptionsClicked" maxWidth="Infinity" style="-fx-background-color: transparent; -fx-background-radius: 8; -fx-cursor: hand; -fx-focus-color: transparent; -fx-faint-focus-color: transparent;" text="Adoptions" textFill="#cbd5e1">
<font>
<Font name="System" size="12.0" />
</font>
<padding>
<Insets bottom="8.0" left="10.0" right="10.0" top="8.0" />
</padding>
</Button>
<Button fx:id="btnProducts" alignment="CENTER_LEFT" mnemonicParsing="false" onAction="#btnProductsClicked" maxWidth="Infinity" style="-fx-background-color: transparent; -fx-background-radius: 8; -fx-cursor: hand; -fx-focus-color: transparent; -fx-faint-focus-color: transparent;" text="Products" textFill="#cbd5e1">
<font>
<Font name="System" size="12.0" />
</font>
<padding>
<Insets bottom="8.0" left="10.0" right="10.0" top="8.0" />
</padding>
</Button>
<Button fx:id="btnStaffAccounts" alignment="CENTER_LEFT" mnemonicParsing="false" onAction="#btnStaffAccountsClicked" 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="Staff Accounts" 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>
<Separator fx:id="separatorAdmin" prefWidth="200.0" style="-fx-background-color: #444444; -fx-opacity: 0.35;" />
<Region VBox.vgrow="ALWAYS" />
<Label fx:id="lblAdminSection" text="ADMIN" textFill="#94a3b8">
<font>
<Font name="System Bold" size="10.0" />
</font>
<padding>
<Insets top="4.0" />
</padding>
</Label>
<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="System" size="13.0" />
</font>
<padding>
<Insets bottom="10.0" left="14.0" right="14.0" top="10.0" />
</padding>
<VBox.margin>
<Insets top="12.0" />
</VBox.margin>
</Button>
</children>
</VBox>
<Button fx:id="btnInventory" alignment="CENTER_LEFT" mnemonicParsing="false" onAction="#btnInventoryClicked" maxWidth="Infinity" style="-fx-background-color: transparent; -fx-background-radius: 8; -fx-cursor: hand; -fx-focus-color: transparent; -fx-faint-focus-color: transparent;" text="Inventory" textFill="#cbd5e1">
<font>
<Font name="System" size="12.0" />
</font>
<padding>
<Insets bottom="8.0" left="10.0" right="10.0" top="8.0" />
</padding>
</Button>
<Button fx:id="btnSuppliers" alignment="CENTER_LEFT" mnemonicParsing="false" onAction="#btnSuppliersClicked" maxWidth="Infinity" style="-fx-background-color: transparent; -fx-background-radius: 8; -fx-cursor: hand; -fx-focus-color: transparent; -fx-faint-focus-color: transparent;" text="Suppliers" textFill="#cbd5e1">
<font>
<Font name="System" size="12.0" />
</font>
<padding>
<Insets bottom="8.0" left="10.0" right="10.0" top="8.0" />
</padding>
</Button>
<Button fx:id="btnProductSuppliers" alignment="CENTER_LEFT" mnemonicParsing="false" onAction="#btnProductSuppliersClicked" maxWidth="Infinity" style="-fx-background-color: transparent; -fx-background-radius: 8; -fx-cursor: hand; -fx-focus-color: transparent; -fx-faint-focus-color: transparent;" text="Product-Suppliers" textFill="#cbd5e1">
<font>
<Font name="System" size="12.0" />
</font>
<padding>
<Insets bottom="8.0" left="10.0" right="10.0" top="8.0" />
</padding>
</Button>
<Button fx:id="btnPurchaseOrders" alignment="CENTER_LEFT" mnemonicParsing="false" onAction="#btnPurchaseOrdersClicked" maxWidth="Infinity" style="-fx-background-color: transparent; -fx-background-radius: 8; -fx-cursor: hand; -fx-focus-color: transparent; -fx-faint-focus-color: transparent;" text="Purchase Orders" textFill="#cbd5e1">
<font>
<Font name="System" size="12.0" />
</font>
<padding>
<Insets bottom="8.0" left="10.0" right="10.0" top="8.0" />
</padding>
</Button>
<Button fx:id="btnStaffAccounts" alignment="CENTER_LEFT" mnemonicParsing="false" onAction="#btnStaffAccountsClicked" maxWidth="Infinity" style="-fx-background-color: transparent; -fx-background-radius: 8; -fx-cursor: hand; -fx-focus-color: transparent; -fx-faint-focus-color: transparent;" text="Staff Accounts" textFill="#cbd5e1">
<font>
<Font name="System" size="12.0" />
</font>
<padding>
<Insets bottom="8.0" left="10.0" right="10.0" top="8.0" />
</padding>
</Button>
<Region VBox.vgrow="ALWAYS" />
<Button fx:id="btnLogout" alignment="CENTER_LEFT" mnemonicParsing="false" onAction="#btnLogoutClicked" maxWidth="Infinity" style="-fx-background-color: rgba(255,255,255,0.08); -fx-background-radius: 8; -fx-cursor: hand; -fx-focus-color: transparent; -fx-faint-focus-color: transparent;" text="Logout" textFill="#e2e8f0">
<font>
<Font name="System" size="12.0" />
</font>
<padding>
<Insets bottom="8.0" left="10.0" right="10.0" top="8.0" />
</padding>
<VBox.margin>
<Insets top="8.0" />
</VBox.margin>
</Button>
</children>
</VBox>
</ScrollPane>
</left>
<center>
<StackPane fx:id="spContentArea" prefHeight="150.0" prefWidth="200.0" BorderPane.alignment="CENTER" />

View File

@@ -0,0 +1,223 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.chart.BarChart?>
<?import javafx.scene.chart.CategoryAxis?>
<?import javafx.scene.chart.LineChart?>
<?import javafx.scene.chart.NumberAxis?>
<?import javafx.scene.chart.PieChart?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.Tab?>
<?import javafx.scene.control.TabPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>
<VBox spacing="15.0" style="-fx-background-color: #f8fafc;" xmlns="http://javafx.com/javafx/21" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.example.petshopdesktop.controllers.AnalyticsController">
<padding>
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
</padding>
<HBox alignment="CENTER_LEFT" spacing="20.0">
<Label text="Sales Analytics" textFill="#2c3e50">
<font>
<Font name="System Bold" size="24.0" />
</font>
</Label>
<Button fx:id="btnRefresh" onAction="#handleRefresh" style="-fx-background-color: #4ECDC4; -fx-text-fill: white; -fx-background-radius: 5; -fx-cursor: hand;" text="Refresh">
<font>
<Font size="13.0" />
</font>
<padding>
<Insets bottom="8.0" left="16.0" right="16.0" top="8.0" />
</padding>
</Button>
</HBox>
<Label fx:id="lblError" textFill="#FF6B6B" visible="false">
<font>
<Font size="13.0" />
</font>
</Label>
<TabPane VBox.vgrow="ALWAYS">
<Tab text="Overview" closable="false">
<VBox spacing="15.0">
<padding>
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0" />
</padding>
<HBox spacing="15.0">
<VBox alignment="CENTER" spacing="5.0" style="-fx-background-color: white; -fx-background-radius: 8; -fx-border-color: #e2e8f0; -fx-border-radius: 8; -fx-border-width: 1;" HBox.hgrow="ALWAYS">
<padding>
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0" />
</padding>
<Label text="Total Revenue" textFill="#64748b">
<font>
<Font size="11.0" />
</font>
</Label>
<Label fx:id="lblTotalRevenue" text="0.00" textFill="#2c3e50">
<font>
<Font name="System Bold" size="20.0" />
</font>
</Label>
</VBox>
<VBox alignment="CENTER" spacing="5.0" style="-fx-background-color: white; -fx-background-radius: 8; -fx-border-color: #e2e8f0; -fx-border-radius: 8; -fx-border-width: 1;" HBox.hgrow="ALWAYS">
<padding>
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0" />
</padding>
<Label text="Total Transactions" textFill="#64748b">
<font>
<Font size="11.0" />
</font>
</Label>
<Label fx:id="lblTotalTransactions" text="0" textFill="#2c3e50">
<font>
<Font name="System Bold" size="20.0" />
</font>
</Label>
</VBox>
<VBox alignment="CENTER" spacing="5.0" style="-fx-background-color: white; -fx-background-radius: 8; -fx-border-color: #e2e8f0; -fx-border-radius: 8; -fx-border-width: 1;" HBox.hgrow="ALWAYS">
<padding>
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0" />
</padding>
<Label text="Avg Transaction" textFill="#64748b">
<font>
<Font size="11.0" />
</font>
</Label>
<Label fx:id="lblAvgTransaction" text="0.00" textFill="#2c3e50">
<font>
<Font name="System Bold" size="20.0" />
</font>
</Label>
</VBox>
<VBox alignment="CENTER" spacing="5.0" style="-fx-background-color: white; -fx-background-radius: 8; -fx-border-color: #e2e8f0; -fx-border-radius: 8; -fx-border-width: 1;" HBox.hgrow="ALWAYS">
<padding>
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0" />
</padding>
<Label text="Items Sold" textFill="#64748b">
<font>
<Font size="11.0" />
</font>
</Label>
<Label fx:id="lblTotalItems" text="0" textFill="#2c3e50">
<font>
<Font name="System Bold" size="20.0" />
</font>
</Label>
</VBox>
</HBox>
<VBox spacing="8.0" style="-fx-background-color: white; -fx-background-radius: 8; -fx-border-color: #e2e8f0; -fx-border-radius: 8; -fx-border-width: 1;">
<padding>
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0" />
</padding>
<Label text="Sales Over Time" textFill="#2c3e50">
<font>
<Font name="System Bold" size="14.0" />
</font>
</Label>
<LineChart fx:id="chartSalesOverTime" animated="true" legendSide="BOTTOM" prefHeight="380.0">
<xAxis>
<CategoryAxis label="Date" side="BOTTOM" />
</xAxis>
<yAxis>
<NumberAxis label="Revenue" side="LEFT" />
</yAxis>
</LineChart>
</VBox>
</VBox>
</Tab>
<Tab text="Products" closable="false">
<HBox spacing="15.0">
<padding>
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0" />
</padding>
<VBox spacing="8.0" style="-fx-background-color: white; -fx-background-radius: 8; -fx-border-color: #e2e8f0; -fx-border-radius: 8; -fx-border-width: 1;" minHeight="450.0" prefHeight="450.0" maxHeight="450.0" HBox.hgrow="ALWAYS">
<padding>
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0" />
</padding>
<Label text="Top Products by Revenue" textFill="#2c3e50">
<font>
<Font name="System Bold" size="14.0" />
</font>
</Label>
<BarChart fx:id="chartTopRevenue" animated="true" legendSide="BOTTOM" prefHeight="380.0">
<xAxis>
<NumberAxis label="Revenue" side="BOTTOM" />
</xAxis>
<yAxis>
<CategoryAxis label="Product" side="LEFT" />
</yAxis>
</BarChart>
</VBox>
<VBox spacing="8.0" style="-fx-background-color: white; -fx-background-radius: 8; -fx-border-color: #e2e8f0; -fx-border-radius: 8; -fx-border-width: 1;" minHeight="450.0" prefHeight="450.0" maxHeight="450.0" HBox.hgrow="ALWAYS">
<padding>
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0" />
</padding>
<Label text="Top Products by Quantity" textFill="#2c3e50">
<font>
<Font name="System Bold" size="14.0" />
</font>
</Label>
<BarChart fx:id="chartTopQuantity" animated="true" legendSide="BOTTOM" prefHeight="380.0">
<xAxis>
<NumberAxis label="Quantity Sold" side="BOTTOM" />
</xAxis>
<yAxis>
<CategoryAxis label="Product" side="LEFT" />
</yAxis>
</BarChart>
</VBox>
</HBox>
</Tab>
<Tab text="Operations" closable="false">
<HBox spacing="15.0">
<padding>
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0" />
</padding>
<VBox spacing="8.0" style="-fx-background-color: white; -fx-background-radius: 8; -fx-border-color: #e2e8f0; -fx-border-radius: 8; -fx-border-width: 1;" minHeight="450.0" prefHeight="450.0" maxHeight="450.0" HBox.hgrow="ALWAYS">
<padding>
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0" />
</padding>
<Label text="Payment Method Distribution" textFill="#2c3e50">
<font>
<Font name="System Bold" size="14.0" />
</font>
</Label>
<PieChart fx:id="chartPaymentMethods" animated="true" legendSide="BOTTOM" prefHeight="380.0" />
</VBox>
<VBox spacing="8.0" style="-fx-background-color: white; -fx-background-radius: 8; -fx-border-color: #e2e8f0; -fx-border-radius: 8; -fx-border-width: 1;" minHeight="450.0" prefHeight="450.0" maxHeight="450.0" HBox.hgrow="ALWAYS">
<padding>
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0" />
</padding>
<Label text="Employee Performance" textFill="#2c3e50">
<font>
<Font name="System Bold" size="14.0" />
</font>
</Label>
<BarChart fx:id="chartEmployeePerformance" animated="true" legendSide="BOTTOM" prefHeight="380.0">
<xAxis>
<CategoryAxis label="Employee" side="BOTTOM" />
</xAxis>
<yAxis>
<NumberAxis label="Revenue" side="LEFT" />
</yAxis>
</BarChart>
</VBox>
</HBox>
</Tab>
</TabPane>
</VBox>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>
<VBox spacing="20.0" style="-fx-background-color: #f8fafc;" xmlns="http://javafx.com/javafx/21" xmlns:fx="http://javafx.com/fxml/1">
<padding>
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
</padding>
<Label text="Customer Chat" textFill="#2c3e50">
<font>
<Font name="System Bold" size="30.0" />
</font>
</Label>
</VBox>

View File

@@ -25,6 +25,14 @@
</font>
</Label>
<Region HBox.hgrow="ALWAYS" />
<Button fx:id="btnCreateAccount" mnemonicParsing="false" onAction="#btnCreateAccountClicked" prefHeight="44.0" style="-fx-background-color: #FF6B6B; -fx-cursor: hand; -fx-background-radius: 8;" text="Create Account" textFill="WHITE">
<font>
<Font name="System Bold" size="14.0" />
</font>
<padding>
<Insets bottom="12.0" left="24.0" right="24.0" top="12.0" />
</padding>
</Button>
<Button fx:id="btnRefresh" mnemonicParsing="false" onAction="#btnRefreshClicked" prefHeight="44.0" prefWidth="118.0" style="-fx-background-color: #4ECDC4; -fx-cursor: hand; -fx-background-radius: 8;" text="Refresh" textFill="WHITE">
<font>
<Font name="System Bold" size="14.0" />