fixed the issues with main

This commit is contained in:
2026-02-25 11:23:44 -07:00
parent 4dc5cae199
commit 8f77fea963
10 changed files with 680 additions and 2 deletions

View File

@@ -190,8 +190,10 @@ public class MainLayoutController {
btnPurchaseOrders.setVisible(isAdmin);
btnPurchaseOrders.setManaged(isAdmin);
btnStaffAccounts.setVisible(isAdmin);
btnStaffAccounts.setManaged(isAdmin);
if (btnStaffAccounts != null) {
btnStaffAccounts.setVisible(isAdmin);
btnStaffAccounts.setManaged(isAdmin);
}
btnSalesHistory.setText(isAdmin ? "Sales History" : "Sales");

View File

@@ -0,0 +1,117 @@
package org.example.petshopdesktop.controllers.dialogcontrollers;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import javafx.stage.Stage;
import org.example.petshopdesktop.database.UserDB;
import org.example.petshopdesktop.util.ActivityLogger;
import java.sql.SQLException;
public class StaffRegisterDialogController {
@FXML
private TextField txtFirstName;
@FXML
private TextField txtLastName;
@FXML
private TextField txtEmail;
@FXML
private TextField txtPhone;
@FXML
private TextField txtUsername;
@FXML
private PasswordField txtPassword;
@FXML
private PasswordField txtPasswordConfirm;
@FXML
private Label lblError;
@FXML
private Button btnCreate;
@FXML
void btnCreateClicked(ActionEvent event) {
lblError.setText("");
String firstName = value(txtFirstName);
String lastName = value(txtLastName);
String email = value(txtEmail);
String phone = value(txtPhone);
String username = value(txtUsername);
String password = txtPassword.getText() == null ? "" : txtPassword.getText();
String confirm = txtPasswordConfirm.getText() == null ? "" : txtPasswordConfirm.getText();
if (firstName.isBlank() || lastName.isBlank()) {
lblError.setText("First name and last name are required.");
return;
}
if (email.isBlank()) {
lblError.setText("Email is required.");
return;
}
if (phone.isBlank()) {
lblError.setText("Phone is required.");
return;
}
if (username.isBlank()) {
lblError.setText("Username is required.");
return;
}
if (password.isBlank()) {
lblError.setText("Password is required.");
return;
}
if (!password.equals(confirm)) {
lblError.setText("Passwords do not match.");
return;
}
try {
UserDB.createStaffAccount(firstName, lastName, email, phone, username, password);
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle("Staff Account");
alert.setHeaderText(null);
alert.setContentText("Staff account created. You can log in now.");
alert.showAndWait();
close();
} catch (SQLException e) {
ActivityLogger.getInstance().logException("StaffRegisterDialogController.btnCreateClicked", e, "Creating staff account");
String msg = e.getMessage() == null ? "Could not create staff account." : e.getMessage();
if (msg.toLowerCase().contains("duplicate") || msg.toLowerCase().contains("unique")) {
lblError.setText("Username already exists.");
} else {
lblError.setText(msg);
}
} catch (RuntimeException e) {
ActivityLogger.getInstance().logException("StaffRegisterDialogController.btnCreateClicked", e, "Database connection");
lblError.setText("Database is not connected.");
}
}
@FXML
void btnCancelClicked(ActionEvent event) {
close();
}
private void close() {
Stage stage = (Stage) btnCreate.getScene().getWindow();
stage.close();
}
private static String value(TextField tf) {
return tf.getText() == null ? "" : tf.getText().trim();
}
}

View File

@@ -0,0 +1,132 @@
package org.example.petshopdesktop.database;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class EmployeeDB {
public static int ensureDefaultEmployee(String firstName, String lastName, String email, String phone, String role, boolean isActive) throws SQLException {
Integer existingId = findEmployeeIdByEmail(email);
if (existingId != null) {
return existingId;
}
try (Connection conn = ConnectionDB.getConnection()) {
conn.setAutoCommit(false);
try {
int storeId = getDefaultStoreId(conn);
int employeeId = createEmployee(conn, firstName, lastName, email, phone, role, isActive);
assignEmployeeToStore(conn, employeeId, storeId);
conn.commit();
return employeeId;
} catch (SQLException e) {
conn.rollback();
throw e;
} finally {
conn.setAutoCommit(true);
}
}
}
public static Integer findEmployeeIdByEmail(String email) throws SQLException {
String sql = "SELECT employeeId FROM employee WHERE email = ? LIMIT 1";
try (Connection conn = ConnectionDB.getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, email);
try (ResultSet rs = ps.executeQuery()) {
if (rs.next()) {
return rs.getInt("employeeId");
}
}
}
return null;
}
public static int createEmployee(Connection conn, String firstName, String lastName, String email, String phone, String role, boolean isActive) throws SQLException {
String sql = "INSERT INTO employee (firstName, lastName, email, phone, role, isActive) VALUES (?, ?, ?, ?, ?, ?)";
try (PreparedStatement ps = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
ps.setString(1, firstName);
ps.setString(2, lastName);
ps.setString(3, email);
ps.setString(4, phone);
ps.setString(5, role);
ps.setBoolean(6, isActive);
ps.executeUpdate();
try (ResultSet keys = ps.getGeneratedKeys()) {
if (keys.next()) {
return keys.getInt(1);
}
}
}
throw new SQLException("Could not create employee.");
}
public static void assignEmployeeToStore(Connection conn, int employeeId, int storeId) throws SQLException {
String sql = "INSERT IGNORE INTO employeeStore (employeeId, storeId) VALUES (?, ?)";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setInt(1, employeeId);
ps.setInt(2, storeId);
ps.executeUpdate();
}
}
public static int getDefaultStoreId() throws SQLException {
try (Connection conn = ConnectionDB.getConnection()) {
return getDefaultStoreId(conn);
}
}
public static int getDefaultStoreId(Connection conn) throws SQLException {
Integer existing = firstStoreId(conn);
if (existing != null) {
return existing;
}
String insert = "INSERT INTO storeLocation (storeName, address, phone, email) VALUES ('Main Store', 'N/A', '000-000-0000', 'main@petshop.com')";
try (PreparedStatement ps = conn.prepareStatement(insert, Statement.RETURN_GENERATED_KEYS)) {
ps.executeUpdate();
try (ResultSet keys = ps.getGeneratedKeys()) {
if (keys.next()) {
return keys.getInt(1);
}
}
}
Integer after = firstStoreId(conn);
if (after != null) {
return after;
}
return 1;
}
public static Integer getPrimaryStoreId(int employeeId) throws SQLException {
String sql = "SELECT storeId FROM employeeStore WHERE employeeId = ? ORDER BY storeId ASC LIMIT 1";
try (Connection conn = ConnectionDB.getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setInt(1, employeeId);
try (ResultSet rs = ps.executeQuery()) {
if (rs.next()) {
return rs.getInt("storeId");
}
}
}
return null;
}
private static Integer firstStoreId(Connection conn) throws SQLException {
String sql = "SELECT storeId FROM storeLocation ORDER BY storeId ASC LIMIT 1";
try (PreparedStatement ps = conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery()) {
if (rs.next()) {
return rs.getInt("storeId");
}
}
return null;
}
}

View File

@@ -0,0 +1,39 @@
package org.example.petshopdesktop.models;
public class SaleCartItem {
private final int prodId;
private final String prodName;
private int quantity;
private final double unitPrice;
public SaleCartItem(int prodId, String prodName, int quantity, double unitPrice) {
this.prodId = prodId;
this.prodName = prodName;
this.quantity = quantity;
this.unitPrice = unitPrice;
}
public int getProdId() {
return prodId;
}
public String getProdName() {
return prodName;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
public double getUnitPrice() {
return unitPrice;
}
public double getTotal() {
return unitPrice * quantity;
}
}

View File

@@ -0,0 +1,55 @@
package org.example.petshopdesktop.models;
public class SaleLineItem {
private final int saleId;
private final String saleDate;
private final String employeeName;
private final String itemName;
private final int quantity;
private final double unitPrice;
private final double total;
private final String paymentMethod;
public SaleLineItem(int saleId, String saleDate, String employeeName, String itemName, int quantity, double unitPrice, double total, String paymentMethod) {
this.saleId = saleId;
this.saleDate = saleDate;
this.employeeName = employeeName;
this.itemName = itemName;
this.quantity = quantity;
this.unitPrice = unitPrice;
this.total = total;
this.paymentMethod = paymentMethod;
}
public int getSaleId() {
return saleId;
}
public String getSaleDate() {
return saleDate;
}
public String getEmployeeName() {
return employeeName;
}
public String getItemName() {
return itemName;
}
public int getQuantity() {
return quantity;
}
public double getUnitPrice() {
return unitPrice;
}
public double getTotal() {
return total;
}
public String getPaymentMethod() {
return paymentMethod;
}
}

View File

@@ -0,0 +1,73 @@
package org.example.petshopdesktop.models;
import java.sql.Timestamp;
public class StaffAccount {
private final int userId;
private final int employeeId;
private final String username;
private final String firstName;
private final String lastName;
private final String email;
private final String phone;
private final boolean active;
private final Timestamp createdAt;
public StaffAccount(int userId, int employeeId, String username, String firstName, String lastName, String email, String phone, boolean active, Timestamp createdAt) {
this.userId = userId;
this.employeeId = employeeId;
this.username = username;
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
this.phone = phone;
this.active = active;
this.createdAt = createdAt;
}
public int getUserId() {
return userId;
}
public int getEmployeeId() {
return employeeId;
}
public String getUsername() {
return username;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public String getFullName() {
String fn = firstName == null ? "" : firstName.trim();
String ln = lastName == null ? "" : lastName.trim();
return (fn + " " + ln).trim();
}
public String getEmail() {
return email;
}
public String getPhone() {
return phone;
}
public boolean isActive() {
return active;
}
public Timestamp getCreatedAt() {
return createdAt;
}
public String getStatus() {
return active ? "Active" : "Inactive";
}
}

View File

@@ -0,0 +1,90 @@
package org.example.petshopdesktop.util;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public final class ActivityLogger {
private static final ActivityLogger INSTANCE = new ActivityLogger();
private static final String LOG_FILE_NAME = "log.txt";
private static final DateTimeFormatter TS = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
private final Path logFilePath;
private ActivityLogger() {
this.logFilePath = resolveProjectRoot().resolve(LOG_FILE_NAME);
}
public static ActivityLogger getInstance() {
return INSTANCE;
}
public void logInsert(String tableName, String recordId, String details) {
write("INSERT", String.format("DB_INSERT | Table: %s | ID: %s | Details: %s", tableName, recordId, details));
}
public void logUpdate(String tableName, String recordId, String details) {
write("UPDATE", String.format("DB_UPDATE | Table: %s | ID: %s | Details: %s", tableName, recordId, details));
}
public void logDelete(String tableName, String recordId, String details) {
write("DELETE", String.format("DB_DELETE | Table: %s | ID: %s | Details: %s", tableName, recordId, details));
}
public void logException(String location, Exception exception, String context) {
write("ERROR", String.format(
"EXCEPTION | Location: %s | Type: %s | Message: %s | Context: %s",
location,
exception.getClass().getSimpleName(),
String.valueOf(exception.getMessage()),
context
));
}
public void logInfo(String category, String message) {
write(category, message);
}
public Path getLogFilePath() {
return logFilePath;
}
private static Path resolveProjectRoot() {
Path start = Paths.get(System.getProperty("user.dir")).toAbsolutePath();
Path current = start;
for (int i = 0; i < 6 && current != null; i++) {
if (Files.exists(current.resolve("pom.xml"))
|| Files.exists(current.resolve("mvnw"))
|| Files.exists(current.resolve("connectionpetstore.properties.example"))) {
return current;
}
current = current.getParent();
}
return start;
}
private synchronized void write(String level, String message) {
String timestamp = LocalDateTime.now().format(TS);
String logEntry = String.format("[%s] [%s] %s%n", timestamp, level, message);
try {
Files.writeString(
logFilePath,
logEntry,
StandardCharsets.UTF_8,
StandardOpenOption.CREATE,
StandardOpenOption.APPEND
);
} catch (Exception e) {
System.err.println("Failed to write log entry: " + e.getMessage());
}
}
}