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

@@ -164,6 +164,10 @@ public class PetDetailFragment extends Fragment {
if (!InputValidator.isSpinnerSelected(binding.spinnerCustomer, "Owner")) return; if (!InputValidator.isSpinnerSelected(binding.spinnerCustomer, "Owner")) return;
if (!InputValidator.isSpinnerSelected(binding.spinnerStore, "Store")) return; if (!InputValidator.isSpinnerSelected(binding.spinnerStore, "Store")) return;
} }
if ("Pending".equalsIgnoreCase(status)) {
if (!InputValidator.isSpinnerSelected(binding.spinnerCustomer, "Owner")) return;
if (!InputValidator.isSpinnerSelected(binding.spinnerStore, "Store")) return;
}
PetDTO petDTO = new PetDTO(); PetDTO petDTO = new PetDTO();
petDTO.setPetName(name); petDTO.setPetName(name);

View File

@@ -126,7 +126,7 @@ public class PetProfileFragment extends Fragment {
String status = pet.getPetStatus(); String status = pet.getPetStatus();
if ("Adopted".equalsIgnoreCase(status) || "Owned".equalsIgnoreCase(status)) { if ("Adopted".equalsIgnoreCase(status) || "Owned".equalsIgnoreCase(status) || "Pending".equalsIgnoreCase(status)) {
binding.layoutPetOwner.setVisibility(View.VISIBLE); binding.layoutPetOwner.setVisibility(View.VISIBLE);
if (pet.getCustomerName() != null && !pet.getCustomerName().isEmpty()) { if (pet.getCustomerName() != null && !pet.getCustomerName().isEmpty()) {
binding.tvPetOwner.setText(pet.getCustomerName()); binding.tvPetOwner.setText(pet.getCustomerName());
@@ -137,7 +137,7 @@ public class PetProfileFragment extends Fragment {
binding.layoutPetOwner.setVisibility(View.GONE); binding.layoutPetOwner.setVisibility(View.GONE);
} }
if ("Available".equalsIgnoreCase(status) || "Adopted".equalsIgnoreCase(status)) { if ("Available".equalsIgnoreCase(status) || "Adopted".equalsIgnoreCase(status) || "Pending".equalsIgnoreCase(status)) {
binding.layoutPetStore.setVisibility(View.VISIBLE); binding.layoutPetStore.setVisibility(View.VISIBLE);
if (pet.getStoreName() != null && !pet.getStoreName().isEmpty()) { if (pet.getStoreName() != null && !pet.getStoreName().isEmpty()) {
binding.tvPetStore.setText(pet.getStoreName()); binding.tvPetStore.setText(pet.getStoreName());

View File

@@ -271,6 +271,7 @@ public class AdoptionService {
pet.setStore(null); pet.setStore(null);
} else if (ADOPTION_STATUS_PENDING.equalsIgnoreCase(adoptionStatus)) { } else if (ADOPTION_STATUS_PENDING.equalsIgnoreCase(adoptionStatus)) {
pet.setPetStatus(PET_STATUS_PENDING); pet.setPetStatus(PET_STATUS_PENDING);
pet.setOwner(customer);
} else { } else {
pet.setPetStatus(PET_STATUS_AVAILABLE); pet.setPetStatus(PET_STATUS_AVAILABLE);
pet.setOwner(null); pet.setOwner(null);

View File

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

View File

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

View File

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

View File

@@ -11,7 +11,7 @@
<?import javafx.scene.layout.VBox?> <?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?> <?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> <padding>
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" /> <Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
</padding> </padding>
@@ -25,22 +25,6 @@
</font> </font>
</Label> </Label>
<Region HBox.hgrow="ALWAYS" /> <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"> <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>
<Font name="System Bold" size="14.0" /> <Font name="System Bold" size="14.0" />
@@ -52,12 +36,91 @@
</children> </children>
</HBox> </HBox>
<!-- Customers Section -->
<VBox spacing="10.0" VBox.vgrow="ALWAYS">
<children>
<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;"> <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> <padding>
<Insets bottom="10.0" left="15.0" right="15.0" top="10.0" /> <Insets bottom="10.0" left="15.0" right="15.0" top="10.0" />
</padding> </padding>
<children> <children>
<TextField fx:id="txtSearch" promptText="Search users..." style="-fx-border-width: 0; -fx-background-color: transparent;" HBox.hgrow="ALWAYS"> <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>
</VBox>
<!-- 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>
<Font size="15.0" /> <Font size="15.0" />
</font> </font>
@@ -76,7 +139,8 @@
<TableColumn fx:id="colCreated" prefWidth="150.0" text="Created" /> <TableColumn fx:id="colCreated" prefWidth="150.0" text="Created" />
</columns> </columns>
</TableView> </TableView>
</children>
</VBox>
<Label fx:id="lblStatus" text="" textFill="#64748b" visible="false" managed="true"> <Label fx:id="lblStatus" text="" textFill="#64748b" visible="false" managed="true">
<font> <font>