made it so staff cannot change the status of pets for desktop for adopted or owned

This commit is contained in:
Alex
2026-04-13 20:49:22 -06:00
parent 572895efa9
commit 044e9ba7b2
7 changed files with 292 additions and 123 deletions

View File

@@ -10,6 +10,7 @@ public class UserResponse {
private String phone;
private String role;
private Boolean active;
private Integer loyaltyPoints;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
@@ -72,6 +73,14 @@ public class UserResponse {
this.active = active;
}
public Integer getLoyaltyPoints() {
return loyaltyPoints;
}
public void setLoyaltyPoints(Integer loyaltyPoints) {
this.loyaltyPoints = loyaltyPoints;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}

View File

@@ -13,21 +13,55 @@ import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Modality;
import javafx.stage.Stage;
import org.example.petshopdesktop.api.dto.user.UserResponse;
import org.example.petshopdesktop.api.endpoints.UserApi;
import org.example.petshopdesktop.api.endpoints.CustomerApi;
import org.example.petshopdesktop.api.endpoints.UserApi;
import org.example.petshopdesktop.auth.UserSession;
import org.example.petshopdesktop.util.ActivityLogger;
import org.example.petshopdesktop.util.TableViewSupport;
import java.util.List;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
public class StaffAccountsController {
@FXML
private VBox staffSection;
@FXML
private TableView<UserResponse> tvCustomers;
@FXML
private TableColumn<UserResponse, String> colCustomerUsername;
@FXML
private TableColumn<UserResponse, String> colCustomerName;
@FXML
private TableColumn<UserResponse, String> colCustomerEmail;
@FXML
private TableColumn<UserResponse, String> colCustomerPhone;
@FXML
private TableColumn<UserResponse, Object> colCustomerLoyaltyPoints;
@FXML
private TableColumn<UserResponse, String> colCustomerStatus;
@FXML
private TableColumn<UserResponse, Object> colCustomerCreated;
@FXML
private TextField txtSearchCustomer;
@FXML
private Button btnEditCustomer;
@FXML
private TableView<UserResponse> tvStaff;
@@ -55,12 +89,6 @@ public class StaffAccountsController {
@FXML
private TextField txtSearch;
@FXML
private Label lblError;
@FXML
private Label lblStatus;
@FXML
private Button btnRefresh;
@@ -70,11 +98,38 @@ public class StaffAccountsController {
@FXML
private Button btnEditAccount;
@FXML
private Label lblError;
@FXML
private Label lblStatus;
private final ObservableList<UserResponse> customerAccounts = FXCollections.observableArrayList();
private FilteredList<UserResponse> filteredCustomers;
private final ObservableList<UserResponse> staffAccounts = FXCollections.observableArrayList();
private FilteredList<UserResponse> filtered;
private FilteredList<UserResponse> filteredStaff;
@FXML
public void initialize() {
colCustomerUsername.setCellValueFactory(data -> new javafx.beans.property.SimpleStringProperty(data.getValue().getUsername()));
colCustomerName.setCellValueFactory(data -> new javafx.beans.property.SimpleStringProperty(data.getValue().getFullName()));
colCustomerEmail.setCellValueFactory(data -> new javafx.beans.property.SimpleStringProperty(data.getValue().getEmail()));
colCustomerPhone.setCellValueFactory(data -> new javafx.beans.property.SimpleStringProperty(data.getValue().getPhone()));
colCustomerLoyaltyPoints.setCellValueFactory(data -> new javafx.beans.property.SimpleObjectProperty<>(data.getValue().getLoyaltyPoints()));
colCustomerStatus.setCellValueFactory(data -> new javafx.beans.property.SimpleStringProperty(data.getValue().getActive() != null && data.getValue().getActive() ? "Active" : "Inactive"));
colCustomerCreated.setCellValueFactory(data -> new javafx.beans.property.SimpleObjectProperty<>(data.getValue().getCreatedAt()));
filteredCustomers = new FilteredList<>(customerAccounts, a -> true);
TableViewSupport.bindSortedItems(tvCustomers, filteredCustomers);
TableViewSupport.installDoubleClickAction(tvCustomers, this::openEditDialog);
tvCustomers.getSelectionModel().selectedItemProperty().addListener((obs, oldVal, newVal) ->
btnEditCustomer.setDisable(newVal == null));
btnEditCustomer.setDisable(true);
txtSearchCustomer.textProperty().addListener((obs, o, n) -> applyCustomerFilter(n));
colUsername.setCellValueFactory(data -> new javafx.beans.property.SimpleStringProperty(data.getValue().getUsername()));
colName.setCellValueFactory(data -> new javafx.beans.property.SimpleStringProperty(data.getValue().getFullName()));
colEmail.setCellValueFactory(data -> new javafx.beans.property.SimpleStringProperty(data.getValue().getEmail()));
@@ -83,33 +138,39 @@ public class StaffAccountsController {
colStatus.setCellValueFactory(data -> new javafx.beans.property.SimpleStringProperty(data.getValue().getActive() != null && data.getValue().getActive() ? "Active" : "Inactive"));
colCreated.setCellValueFactory(data -> new javafx.beans.property.SimpleObjectProperty<>(data.getValue().getCreatedAt()));
filtered = new FilteredList<>(staffAccounts, a -> true);
TableViewSupport.bindSortedItems(tvStaff, filtered);
filteredStaff = new FilteredList<>(staffAccounts, a -> true);
TableViewSupport.bindSortedItems(tvStaff, filteredStaff);
TableViewSupport.installDoubleClickAction(tvStaff, this::openEditDialog);
txtSearch.textProperty().addListener((obs, o, n) -> applyFilter(n));
tvStaff.getSelectionModel().selectedItemProperty().addListener((obs, oldVal, newVal) ->
btnEditAccount.setDisable(newVal == null));
btnEditAccount.setDisable(true);
tvStaff.getSelectionModel().selectedItemProperty().addListener((obs, oldValue, newValue) -> {
if (btnEditAccount != null) {
btnEditAccount.setDisable(newValue == null);
}
});
txtSearch.textProperty().addListener((obs, o, n) -> applyStaffFilter(n));
if (btnEditAccount != null) {
btnEditAccount.setDisable(true);
}
boolean isAdmin = UserSession.getInstance().isAdmin();
staffSection.setVisible(isAdmin);
staffSection.setManaged(isAdmin);
refresh();
}
@FXML
void btnRefreshClicked(ActionEvent event) {
txtSearchCustomer.clear();
txtSearch.clear();
TableViewSupport.clearSort(tvCustomers);
TableViewSupport.clearSort(tvStaff);
refresh();
TableViewSupport.flashStatus(lblStatus, "Refreshed");
}
@FXML
void btnEditCustomerClicked(ActionEvent event) {
lblError.setText("");
openEditDialog(tvCustomers.getSelectionModel().getSelectedItem());
}
@FXML
void btnCreateAccountClicked(ActionEvent event) {
lblError.setText("");
@@ -132,8 +193,7 @@ public class StaffAccountsController {
@FXML
void btnEditAccountClicked(ActionEvent event) {
lblError.setText("");
UserResponse selected = tvStaff.getSelectionModel().getSelectedItem();
openEditDialog(selected);
openEditDialog(tvStaff.getSelectionModel().getSelectedItem());
}
private void openEditDialog(UserResponse selected) {
@@ -154,7 +214,8 @@ public class StaffAccountsController {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/org/example/petshopdesktop/dialogviews/staff-edit-dialog-view.fxml"));
Stage dialog = new Stage();
dialog.initOwner(tvStaff.getScene().getWindow());
Stage owner = (tvStaff.getScene() != null) ? (Stage) tvStaff.getScene().getWindow() : (Stage) tvCustomers.getScene().getWindow();
dialog.initOwner(owner);
dialog.initModality(Modality.APPLICATION_MODAL);
dialog.setTitle("Edit User Account");
dialog.setScene(new Scene(loader.load()));
@@ -164,31 +225,45 @@ public class StaffAccountsController {
dialog.showAndWait();
refresh();
} catch (Exception e) {
ActivityLogger.getInstance().logException("StaffAccountsController.btnEditAccountClicked", e, "Opening user edit dialog");
ActivityLogger.getInstance().logException("StaffAccountsController.openEditDialog", e, "Opening user edit dialog");
lblError.setText("Could not open user account editor.");
}
}
private void refresh() {
lblError.setText("");
tvCustomers.setDisable(true);
tvStaff.setDisable(true);
new Thread(() -> {
try {
UserSession session = UserSession.getInstance();
List<UserResponse> users;
if (session.isAdmin()) {
users = UserApi.getInstance().listUsers(null);
Comparator<UserResponse> byCreated = Comparator.comparing(
UserResponse::getCreatedAt, Comparator.nullsLast(Comparator.reverseOrder()));
final List<UserResponse> customers;
final List<UserResponse> staff;
if (UserSession.getInstance().isAdmin()) {
List<UserResponse> allUsers = UserApi.getInstance().listUsers(null);
customers = allUsers.stream()
.filter(u -> "CUSTOMER".equalsIgnoreCase(u.getRole()))
.sorted(byCreated)
.collect(Collectors.toList());
staff = allUsers.stream()
.filter(u -> !"CUSTOMER".equalsIgnoreCase(u.getRole()))
.sorted(byCreated)
.collect(Collectors.toList());
} else {
users = CustomerApi.getInstance().listCustomers(null);
customers = CustomerApi.getInstance().listCustomers(null).stream()
.sorted(byCreated)
.collect(Collectors.toList());
staff = List.of();
}
List<UserResponse> sortedUsers = users.stream()
.sorted(Comparator.comparing(UserResponse::getCreatedAt, Comparator.nullsLast(Comparator.reverseOrder())))
.collect(Collectors.toList());
Platform.runLater(() -> {
staffAccounts.setAll(sortedUsers);
customerAccounts.setAll(customers);
staffAccounts.setAll(staff);
tvCustomers.setDisable(false);
tvStaff.setDisable(false);
});
} catch (Exception e) {
@@ -196,27 +271,41 @@ public class StaffAccountsController {
Platform.runLater(() -> {
String message = e.getMessage();
lblError.setText(message == null || message.isBlank()
? "Could not load user accounts."
: "Could not load user accounts: " + message);
? "Could not load user accounts."
: "Could not load user accounts: " + message);
tvCustomers.setDisable(false);
tvStaff.setDisable(false);
});
}
}).start();
}
private void applyFilter(String text) {
private void applyCustomerFilter(String text) {
String q = text == null ? "" : text.trim().toLowerCase();
if (q.isEmpty()) {
filtered.setPredicate(a -> true);
filteredCustomers.setPredicate(a -> true);
return;
}
filteredCustomers.setPredicate(a ->
safe(a.getUsername()).contains(q)
|| safe(a.getFullName()).contains(q)
|| safe(a.getEmail()).contains(q)
|| safe(a.getPhone()).contains(q)
);
}
filtered.setPredicate(a ->
safe(a.getUsername()).contains(q)
|| safe(a.getFullName()).contains(q)
|| safe(a.getEmail()).contains(q)
|| safe(a.getPhone()).contains(q)
|| safe(a.getRole()).contains(q)
private void applyStaffFilter(String text) {
String q = text == null ? "" : text.trim().toLowerCase();
if (q.isEmpty()) {
filteredStaff.setPredicate(a -> true);
return;
}
filteredStaff.setPredicate(a ->
safe(a.getUsername()).contains(q)
|| safe(a.getFullName()).contains(q)
|| safe(a.getEmail()).contains(q)
|| safe(a.getPhone()).contains(q)
|| safe(a.getRole()).contains(q)
);
}

View File

@@ -132,9 +132,7 @@ public class PetDialogController {
}
});
setFieldVisibility(vbCustomerField, false);
setFieldVisibility(vbStoreField, false);
setFieldVisibility(vbPriceField, true);
updateStatusFieldVisibility(null);
loadSpecies();
@@ -174,18 +172,18 @@ public class PetDialogController {
String speciesValue = cbPetSpecies.getValue() != null ? cbPetSpecies.getValue().trim() : "";
if (speciesValue.isEmpty()) errorMsg += "Species is required\n";
String selectedStatus = cbPetStatus.getValue();
boolean needsPrice = !("Owned".equalsIgnoreCase(selectedStatus) || "Adopted".equalsIgnoreCase(selectedStatus));
if (needsPrice) {
errorMsg += Validator.isPresent(txtPetPrice.getText(), "Price");
}
errorMsg += Validator.isPresent(txtPetPrice.getText(), "Price");
if (cbPetStatus.getSelectionModel().getSelectedItem() == null){
errorMsg += "Status is required";
}
if ("Owned".equalsIgnoreCase(selectedStatus) && cbCustomer.getValue() == null && UserSession.getInstance().isAdmin()) {
errorMsg += "Customer is required for Owned status\n";
boolean needsCustomer = "Owned".equalsIgnoreCase(selectedStatus)
|| "Adopted".equalsIgnoreCase(selectedStatus)
|| "Pending".equalsIgnoreCase(selectedStatus);
boolean needsStore = requiresStore(selectedStatus);
if (needsCustomer && cbCustomer.getValue() == null && UserSession.getInstance().isAdmin()) {
errorMsg += "Customer is required for " + selectedStatus + " status\n";
}
boolean storeRequired = requiresStore(selectedStatus) && !"Adopted".equalsIgnoreCase(selectedStatus);
if (storeRequired && cbStore.getValue() == null) {
if (needsStore && cbStore.getValue() == null) {
errorMsg += "Store is required for " + selectedStatus + " status\n";
}
@@ -193,15 +191,11 @@ public class PetDialogController {
errorMsg += Validator.isLessThanVarChars(txtPetName.getText(), "Pet Name", 50);
errorMsg += Validator.isLessThanVarChars(speciesValue, "Species", 50);
errorMsg += Validator.isLessThanVarChars(txtPetBreed.getText(), "Breed", 50);
if (needsPrice) {
errorMsg += Validator.isLessThanVarChars(txtPetPrice.getText(), "Price", 12);
}
errorMsg += Validator.isLessThanVarChars(txtPetPrice.getText(), "Price", 12);
errorMsg += Validator.isLessThanVarChars(txtPetAge.getText(), "Age", 11);
//Check validation (format)
if (needsPrice) {
errorMsg += Validator.isNonNegativeDouble(txtPetPrice.getText(), "Price");
}
errorMsg += Validator.isNonNegativeDouble(txtPetPrice.getText(), "Price");
errorMsg += Validator.isPositiveInteger(txtPetAge.getText(), "Age");
if(errorMsg.isEmpty()){
@@ -263,9 +257,7 @@ public class PetDialogController {
request.setPetSpecies(cbPetSpecies.getValue() != null ? cbPetSpecies.getValue().trim() : "");
request.setPetBreed(txtPetBreed.getText());
request.setPetStatus(cbPetStatus.getValue());
String buildStatus = cbPetStatus.getValue();
boolean buildNeedsPrice = !("Owned".equalsIgnoreCase(buildStatus) || "Adopted".equalsIgnoreCase(buildStatus));
if (buildNeedsPrice && txtPetPrice.getText() != null && !txtPetPrice.getText().isBlank()) {
if (txtPetPrice.getText() != null && !txtPetPrice.getText().isBlank()) {
try {
request.setPetPrice(new BigDecimal(txtPetPrice.getText()));
} catch (NumberFormatException e) {
@@ -282,7 +274,10 @@ public class PetDialogController {
request.setPetAge(age);
String status = cbPetStatus.getValue();
if (("Owned".equalsIgnoreCase(status) || "Adopted".equalsIgnoreCase(status)) && cbCustomer.getValue() != null) {
boolean customerApplicable = "Owned".equalsIgnoreCase(status)
|| "Adopted".equalsIgnoreCase(status)
|| "Pending".equalsIgnoreCase(status);
if (customerApplicable && cbCustomer.getValue() != null) {
request.setCustomerId(cbCustomer.getValue().getId());
}
if (requiresStore(status) && cbStore.getValue() != null) {
@@ -407,9 +402,17 @@ public class PetDialogController {
}
}
updateStatusFieldVisibility(cbPetStatus.getValue());
applyStatusLock();
}
}
private void applyStatusLock() {
String status = cbPetStatus.getValue();
boolean isRestricted = "Adopted".equalsIgnoreCase(status) || "Owned".equalsIgnoreCase(status);
boolean isStaff = !UserSession.getInstance().isAdmin();
cbPetStatus.setDisable(isRestricted && isStaff);
}
public void setMode(String mode) {
this.mode = mode;
lblMode.setText(mode + " Pet");
@@ -489,28 +492,27 @@ public class PetDialogController {
}
private void updateStatusFieldVisibility(String status) {
boolean statusNeedsCustomer = "Owned".equalsIgnoreCase(status) || "Adopted".equalsIgnoreCase(status);
boolean needsCustomer = statusNeedsCustomer && UserSession.getInstance().isAdmin();
boolean storeBased = requiresStore(status);
boolean needsPrice = !statusNeedsCustomer;
setFieldVisibility(vbCustomerField, needsCustomer);
setFieldVisibility(vbStoreField, storeBased);
setFieldVisibility(vbPriceField, needsPrice);
if (status == null) {
vbPriceField.setDisable(false);
vbCustomerField.setDisable(true);
vbStoreField.setDisable(false);
return;
}
boolean isAdmin = UserSession.getInstance().isAdmin();
boolean customerApplicable = "Owned".equalsIgnoreCase(status)
|| "Adopted".equalsIgnoreCase(status)
|| "Pending".equalsIgnoreCase(status);
boolean isOwned = "Owned".equalsIgnoreCase(status);
vbPriceField.setDisable(false);
vbCustomerField.setDisable(!customerApplicable || !isAdmin);
vbStoreField.setDisable(isOwned);
}
private boolean requiresStore(String status) {
return "Available".equalsIgnoreCase(status)
|| "Pending".equalsIgnoreCase(status)
|| "Unadopted".equalsIgnoreCase(status)
|| "Adopted".equalsIgnoreCase(status);
}
private void setFieldVisibility(VBox field, boolean visible) {
if (field == null) {
return;
}
field.setVisible(visible);
field.setManaged(visible);
|| "Pending".equalsIgnoreCase(status)
|| "Adopted".equalsIgnoreCase(status);
}
}

View File

@@ -11,7 +11,7 @@
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>
<VBox 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.StaffAccountsController">
<VBox spacing="16.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.StaffAccountsController">
<padding>
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
</padding>
@@ -25,22 +25,6 @@
</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="btnEditAccount" mnemonicParsing="false" onAction="#btnEditAccountClicked" prefHeight="44.0" style="-fx-background-color: #F4A261; -fx-cursor: hand; -fx-background-radius: 8;" text="Edit 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" />
@@ -52,31 +36,111 @@
</children>
</HBox>
<HBox alignment="CENTER_LEFT" spacing="10.0" style="-fx-background-color: white; -fx-background-radius: 14; -fx-border-width: 1; -fx-border-radius: 14; -fx-border-color: #e6e6e6;">
<padding>
<Insets bottom="10.0" left="15.0" right="15.0" top="10.0" />
</padding>
<!-- Customers Section -->
<VBox spacing="10.0" VBox.vgrow="ALWAYS">
<children>
<TextField fx:id="txtSearch" promptText="Search users..." style="-fx-border-width: 0; -fx-background-color: transparent;" HBox.hgrow="ALWAYS">
<font>
<Font size="15.0" />
</font>
</TextField>
<HBox alignment="CENTER_LEFT" spacing="12.0">
<children>
<Label text="Customers" textFill="#2c3e50">
<font>
<Font name="System Bold" size="20.0" />
</font>
</Label>
<Region HBox.hgrow="ALWAYS" />
<Button fx:id="btnEditCustomer" mnemonicParsing="false" onAction="#btnEditCustomerClicked" prefHeight="40.0" style="-fx-background-color: #F4A261; -fx-cursor: hand; -fx-background-radius: 8;" text="Edit Customer" textFill="WHITE">
<font>
<Font name="System Bold" size="14.0" />
</font>
<padding>
<Insets bottom="10.0" left="20.0" right="20.0" top="10.0" />
</padding>
</Button>
</children>
</HBox>
<HBox alignment="CENTER_LEFT" spacing="10.0" style="-fx-background-color: white; -fx-background-radius: 14; -fx-border-width: 1; -fx-border-radius: 14; -fx-border-color: #e6e6e6;">
<padding>
<Insets bottom="10.0" left="15.0" right="15.0" top="10.0" />
</padding>
<children>
<TextField fx:id="txtSearchCustomer" promptText="Search customers..." style="-fx-border-width: 0; -fx-background-color: transparent;" HBox.hgrow="ALWAYS">
<font>
<Font size="15.0" />
</font>
</TextField>
</children>
</HBox>
<TableView fx:id="tvCustomers" style="-fx-background-color: white; -fx-background-radius: 12;" VBox.vgrow="ALWAYS">
<columns>
<TableColumn fx:id="colCustomerUsername" prefWidth="130.0" text="Username" />
<TableColumn fx:id="colCustomerName" prefWidth="160.0" text="Name" />
<TableColumn fx:id="colCustomerEmail" prefWidth="200.0" text="Email" />
<TableColumn fx:id="colCustomerPhone" prefWidth="130.0" text="Phone" />
<TableColumn fx:id="colCustomerLoyaltyPoints" prefWidth="120.0" text="Loyalty Points" />
<TableColumn fx:id="colCustomerStatus" prefWidth="90.0" text="Status" />
<TableColumn fx:id="colCustomerCreated" prefWidth="150.0" text="Created" />
</columns>
</TableView>
</children>
</HBox>
</VBox>
<TableView fx:id="tvStaff" style="-fx-background-color: white; -fx-background-radius: 12;" VBox.vgrow="ALWAYS">
<columns>
<TableColumn fx:id="colUsername" prefWidth="140.0" text="Username" />
<TableColumn fx:id="colName" prefWidth="170.0" text="Name" />
<TableColumn fx:id="colEmail" prefWidth="210.0" text="Email" />
<TableColumn fx:id="colPhone" prefWidth="130.0" text="Phone" />
<TableColumn fx:id="colRole" prefWidth="100.0" text="Role" />
<TableColumn fx:id="colStatus" prefWidth="90.0" text="Status" />
<TableColumn fx:id="colCreated" prefWidth="150.0" text="Created" />
</columns>
</TableView>
<!-- Staff Section (admin only) -->
<VBox fx:id="staffSection" spacing="10.0" managed="false" visible="false" VBox.vgrow="ALWAYS">
<children>
<HBox alignment="CENTER_LEFT" spacing="12.0">
<children>
<Label text="Staff" textFill="#2c3e50">
<font>
<Font name="System Bold" size="20.0" />
</font>
</Label>
<Region HBox.hgrow="ALWAYS" />
<Button fx:id="btnCreateAccount" mnemonicParsing="false" onAction="#btnCreateAccountClicked" prefHeight="40.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="10.0" left="20.0" right="20.0" top="10.0" />
</padding>
</Button>
<Button fx:id="btnEditAccount" mnemonicParsing="false" onAction="#btnEditAccountClicked" prefHeight="40.0" style="-fx-background-color: #F4A261; -fx-cursor: hand; -fx-background-radius: 8;" text="Edit Account" textFill="WHITE">
<font>
<Font name="System Bold" size="14.0" />
</font>
<padding>
<Insets bottom="10.0" left="20.0" right="20.0" top="10.0" />
</padding>
</Button>
</children>
</HBox>
<HBox alignment="CENTER_LEFT" spacing="10.0" style="-fx-background-color: white; -fx-background-radius: 14; -fx-border-width: 1; -fx-border-radius: 14; -fx-border-color: #e6e6e6;">
<padding>
<Insets bottom="10.0" left="15.0" right="15.0" top="10.0" />
</padding>
<children>
<TextField fx:id="txtSearch" promptText="Search staff..." style="-fx-border-width: 0; -fx-background-color: transparent;" HBox.hgrow="ALWAYS">
<font>
<Font size="15.0" />
</font>
</TextField>
</children>
</HBox>
<TableView fx:id="tvStaff" style="-fx-background-color: white; -fx-background-radius: 12;" VBox.vgrow="ALWAYS">
<columns>
<TableColumn fx:id="colUsername" prefWidth="140.0" text="Username" />
<TableColumn fx:id="colName" prefWidth="170.0" text="Name" />
<TableColumn fx:id="colEmail" prefWidth="210.0" text="Email" />
<TableColumn fx:id="colPhone" prefWidth="130.0" text="Phone" />
<TableColumn fx:id="colRole" prefWidth="100.0" text="Role" />
<TableColumn fx:id="colStatus" prefWidth="90.0" text="Status" />
<TableColumn fx:id="colCreated" prefWidth="150.0" text="Created" />
</columns>
</TableView>
</children>
</VBox>
<Label fx:id="lblStatus" text="" textFill="#64748b" visible="false" managed="true">
<font>