added filters to desktop
This commit is contained in:
@@ -3,16 +3,27 @@ package org.example.petshopdesktop;
|
|||||||
import javafx.application.Application;
|
import javafx.application.Application;
|
||||||
import javafx.fxml.FXMLLoader;
|
import javafx.fxml.FXMLLoader;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
public class PetShopApplication extends Application {
|
public class PetShopApplication extends Application {
|
||||||
@Override
|
@Override
|
||||||
public void start(Stage stage) throws IOException {
|
public void start(Stage stage) throws IOException {
|
||||||
FXMLLoader fxmlLoader = new FXMLLoader(PetShopApplication.class.getResource("login-view.fxml"));
|
FXMLLoader fxmlLoader = new FXMLLoader(PetShopApplication.class.getResource("login-view.fxml"));
|
||||||
Scene scene = new Scene(fxmlLoader.load());
|
Scene scene = new Scene(fxmlLoader.load());
|
||||||
stage.setTitle("Pet Shop Manager - Login");
|
stage.setTitle("Leon's Pet Store - Login");
|
||||||
|
|
||||||
|
try {
|
||||||
|
stage.getIcons().add(new Image(Objects.requireNonNull(
|
||||||
|
getClass().getResourceAsStream("/org/example/petshopdesktop/images/leons-pet-store-badge.png")
|
||||||
|
)));
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("Could not load application icon: " + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
stage.setScene(scene);
|
stage.setScene(scene);
|
||||||
stage.show();
|
stage.show();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,9 @@ import javafx.scene.control.cell.PropertyValueFactory;
|
|||||||
import javafx.stage.Modality;
|
import javafx.stage.Modality;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import org.example.petshopdesktop.api.dto.adoption.AdoptionResponse;
|
import org.example.petshopdesktop.api.dto.adoption.AdoptionResponse;
|
||||||
|
import org.example.petshopdesktop.api.dto.common.DropdownOption;
|
||||||
import org.example.petshopdesktop.api.endpoints.AdoptionApi;
|
import org.example.petshopdesktop.api.endpoints.AdoptionApi;
|
||||||
|
import org.example.petshopdesktop.api.endpoints.DropdownApi;
|
||||||
import org.example.petshopdesktop.controllers.dialogcontrollers.AdoptionDialogController;
|
import org.example.petshopdesktop.controllers.dialogcontrollers.AdoptionDialogController;
|
||||||
import org.example.petshopdesktop.models.Adoption;
|
import org.example.petshopdesktop.models.Adoption;
|
||||||
import org.example.petshopdesktop.ui.CalendarPane;
|
import org.example.petshopdesktop.ui.CalendarPane;
|
||||||
@@ -76,6 +78,14 @@ public class AdoptionController {
|
|||||||
@FXML
|
@FXML
|
||||||
private TextField txtSearch;
|
private TextField txtSearch;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ComboBox<String> cbStatusFilter;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ComboBox<DropdownOption> cbStoreFilter;
|
||||||
|
|
||||||
|
private final java.util.ArrayList<DropdownOption> storeOptions = new java.util.ArrayList<>();
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private CalendarPane calendarPane;
|
private CalendarPane calendarPane;
|
||||||
private LocalDate selectedCalendarDate = null;
|
private LocalDate selectedCalendarDate = null;
|
||||||
@@ -104,6 +114,17 @@ public class AdoptionController {
|
|||||||
filteredAdoptions = new FilteredList<>(data, a -> true);
|
filteredAdoptions = new FilteredList<>(data, a -> true);
|
||||||
TableViewSupport.bindSortedItems(tvAdoptions, filteredAdoptions);
|
TableViewSupport.bindSortedItems(tvAdoptions, filteredAdoptions);
|
||||||
|
|
||||||
|
cbStatusFilter.setItems(FXCollections.observableArrayList("All Statuses", "Pending", "Completed", "Cancelled"));
|
||||||
|
cbStatusFilter.getSelectionModel().selectFirst();
|
||||||
|
cbStatusFilter.valueProperty().addListener((obs, o, n) -> applyFilterPredicate());
|
||||||
|
|
||||||
|
if (UserSession.getInstance().isAdmin()) {
|
||||||
|
cbStoreFilter.setVisible(true);
|
||||||
|
cbStoreFilter.setManaged(true);
|
||||||
|
loadStoreFilter();
|
||||||
|
}
|
||||||
|
cbStoreFilter.valueProperty().addListener((obs, o, n) -> displayAdoptions());
|
||||||
|
|
||||||
displayAdoptions();
|
displayAdoptions();
|
||||||
TableViewSupport.installDoubleClickAction(tvAdoptions, selected -> openDialog(selected, "Edit"));
|
TableViewSupport.installDoubleClickAction(tvAdoptions, selected -> openDialog(selected, "Edit"));
|
||||||
|
|
||||||
@@ -120,7 +141,7 @@ public class AdoptionController {
|
|||||||
|
|
||||||
calendarPane.setOnDateSelected(date -> {
|
calendarPane.setOnDateSelected(date -> {
|
||||||
selectedCalendarDate = date;
|
selectedCalendarDate = date;
|
||||||
filteredAdoptions.setPredicate(a -> date == null || a.getAdoptionDate().equals(date.toString()));
|
applyFilterPredicate();
|
||||||
});
|
});
|
||||||
|
|
||||||
tvAdoptions.setOnKeyPressed(event -> {
|
tvAdoptions.setOnKeyPressed(event -> {
|
||||||
@@ -135,6 +156,8 @@ public class AdoptionController {
|
|||||||
@FXML
|
@FXML
|
||||||
void btnRefresh(ActionEvent event) {
|
void btnRefresh(ActionEvent event) {
|
||||||
txtSearch.clear();
|
txtSearch.clear();
|
||||||
|
cbStatusFilter.getSelectionModel().selectFirst();
|
||||||
|
cbStoreFilter.getSelectionModel().selectFirst();
|
||||||
selectedCalendarDate = null;
|
selectedCalendarDate = null;
|
||||||
if (filteredAdoptions != null) filteredAdoptions.setPredicate(a -> true);
|
if (filteredAdoptions != null) filteredAdoptions.setPredicate(a -> true);
|
||||||
tvAdoptions.getSortOrder().clear();
|
tvAdoptions.getSortOrder().clear();
|
||||||
@@ -208,7 +231,7 @@ public class AdoptionController {
|
|||||||
} else {
|
} else {
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
try {
|
try {
|
||||||
Long storeId = UserSession.getInstance().isAdmin() ? null : UserSession.getInstance().getStoreId();
|
Long storeId = selectedStoreId();
|
||||||
List<AdoptionResponse> adoptions = AdoptionApi.getInstance().listAdoptions(filter, storeId);
|
List<AdoptionResponse> adoptions = AdoptionApi.getInstance().listAdoptions(filter, storeId);
|
||||||
List<Adoption> adoptionList = adoptions.stream()
|
List<Adoption> adoptionList = adoptions.stream()
|
||||||
.map(this::mapToAdoption)
|
.map(this::mapToAdoption)
|
||||||
@@ -223,9 +246,7 @@ public class AdoptionController {
|
|||||||
.filter(d -> d != null)
|
.filter(d -> d != null)
|
||||||
.collect(java.util.stream.Collectors.toSet());
|
.collect(java.util.stream.Collectors.toSet());
|
||||||
calendarPane.setEventDates(dates);
|
calendarPane.setEventDates(dates);
|
||||||
if (selectedCalendarDate != null) {
|
applyFilterPredicate();
|
||||||
filteredAdoptions.setPredicate(a -> a.getAdoptionDate().equals(selectedCalendarDate.toString()));
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
@@ -307,6 +328,44 @@ public class AdoptionController {
|
|||||||
txtSearch.setText("");
|
txtSearch.setText("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void loadStoreFilter() {
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
List<DropdownOption> stores = DropdownApi.getInstance().getStores();
|
||||||
|
DropdownOption allStores = new DropdownOption();
|
||||||
|
allStores.setLabel("All Stores");
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
storeOptions.clear();
|
||||||
|
storeOptions.addAll(stores);
|
||||||
|
java.util.List<DropdownOption> items = new java.util.ArrayList<>();
|
||||||
|
items.add(allStores);
|
||||||
|
items.addAll(stores);
|
||||||
|
cbStoreFilter.setItems(FXCollections.observableArrayList(items));
|
||||||
|
cbStoreFilter.getSelectionModel().selectFirst();
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
ActivityLogger.getInstance().logException("AdoptionController.loadStoreFilter", e, "Loading store filter");
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Long selectedStoreId() {
|
||||||
|
if (!UserSession.getInstance().isAdmin()) return UserSession.getInstance().getStoreId();
|
||||||
|
DropdownOption selected = cbStoreFilter.getValue();
|
||||||
|
return (selected != null && selected.getId() != null) ? selected.getId() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyFilterPredicate() {
|
||||||
|
String selectedStatus = cbStatusFilter.getValue();
|
||||||
|
filteredAdoptions.setPredicate(a -> {
|
||||||
|
boolean dateMatch = selectedCalendarDate == null
|
||||||
|
|| a.getAdoptionDate().equals(selectedCalendarDate.toString());
|
||||||
|
boolean statusMatch = selectedStatus == null || selectedStatus.equals("All Statuses")
|
||||||
|
|| a.getAdoptionStatus().equalsIgnoreCase(selectedStatus);
|
||||||
|
return dateMatch && statusMatch;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private Adoption mapToAdoption(AdoptionResponse response) {
|
private Adoption mapToAdoption(AdoptionResponse response) {
|
||||||
return new Adoption(
|
return new Adoption(
|
||||||
response.getAdoptionId().intValue(),
|
response.getAdoptionId().intValue(),
|
||||||
|
|||||||
@@ -16,7 +16,9 @@ import javafx.stage.Stage;
|
|||||||
|
|
||||||
import org.example.petshopdesktop.DTOs.AppointmentDTO;
|
import org.example.petshopdesktop.DTOs.AppointmentDTO;
|
||||||
import org.example.petshopdesktop.api.dto.appointment.AppointmentResponse;
|
import org.example.petshopdesktop.api.dto.appointment.AppointmentResponse;
|
||||||
|
import org.example.petshopdesktop.api.dto.common.DropdownOption;
|
||||||
import org.example.petshopdesktop.api.endpoints.AppointmentApi;
|
import org.example.petshopdesktop.api.endpoints.AppointmentApi;
|
||||||
|
import org.example.petshopdesktop.api.endpoints.DropdownApi;
|
||||||
import org.example.petshopdesktop.controllers.dialogcontrollers.AppointmentDialogController;
|
import org.example.petshopdesktop.controllers.dialogcontrollers.AppointmentDialogController;
|
||||||
import org.example.petshopdesktop.ui.CalendarPane;
|
import org.example.petshopdesktop.ui.CalendarPane;
|
||||||
import org.example.petshopdesktop.util.ActivityLogger;
|
import org.example.petshopdesktop.util.ActivityLogger;
|
||||||
@@ -52,6 +54,11 @@ public class AppointmentController {
|
|||||||
|
|
||||||
@FXML private TextField txtSearch;
|
@FXML private TextField txtSearch;
|
||||||
|
|
||||||
|
@FXML private ComboBox<String> cbStatusFilter;
|
||||||
|
@FXML private ComboBox<DropdownOption> cbStoreFilter;
|
||||||
|
|
||||||
|
private final java.util.ArrayList<DropdownOption> storeOptions = new java.util.ArrayList<>();
|
||||||
|
|
||||||
@FXML private CalendarPane calendarPane;
|
@FXML private CalendarPane calendarPane;
|
||||||
private LocalDate selectedCalendarDate = null;
|
private LocalDate selectedCalendarDate = null;
|
||||||
|
|
||||||
@@ -83,6 +90,17 @@ public class AppointmentController {
|
|||||||
btnMyAppointments.setManaged(true);
|
btnMyAppointments.setManaged(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cbStatusFilter.setItems(FXCollections.observableArrayList("All Statuses", "Booked", "Completed", "Missed", "Cancelled"));
|
||||||
|
cbStatusFilter.getSelectionModel().selectFirst();
|
||||||
|
cbStatusFilter.valueProperty().addListener((obs, o, n) -> applyFilterPredicate());
|
||||||
|
|
||||||
|
if (UserSession.getInstance().isAdmin()) {
|
||||||
|
cbStoreFilter.setVisible(true);
|
||||||
|
cbStoreFilter.setManaged(true);
|
||||||
|
loadStoreFilter();
|
||||||
|
}
|
||||||
|
cbStoreFilter.valueProperty().addListener((obs, o, n) -> loadAppointments());
|
||||||
|
|
||||||
if (txtSearch != null) {
|
if (txtSearch != null) {
|
||||||
txtSearch.textProperty().addListener((obs, o, n) -> applyFilter(n));
|
txtSearch.textProperty().addListener((obs, o, n) -> applyFilter(n));
|
||||||
}
|
}
|
||||||
@@ -105,7 +123,7 @@ public class AppointmentController {
|
|||||||
|
|
||||||
calendarPane.setOnDateSelected(date -> {
|
calendarPane.setOnDateSelected(date -> {
|
||||||
selectedCalendarDate = date;
|
selectedCalendarDate = date;
|
||||||
filtered.setPredicate(apt -> date == null || apt.getAppointmentDate().equals(date.toString()));
|
applyFilterPredicate();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,7 +135,7 @@ public class AppointmentController {
|
|||||||
private void loadAppointments(){
|
private void loadAppointments(){
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
try{
|
try{
|
||||||
Long storeId = UserSession.getInstance().isAdmin() ? null : UserSession.getInstance().getStoreId();
|
Long storeId = selectedStoreId();
|
||||||
Long employeeId = btnMyAppointments.isSelected() ? UserSession.getInstance().getEmployeeId() : null;
|
Long employeeId = btnMyAppointments.isSelected() ? UserSession.getInstance().getEmployeeId() : null;
|
||||||
List<AppointmentResponse> responses = AppointmentApi.getInstance().listAppointments(null, storeId, employeeId);
|
List<AppointmentResponse> responses = AppointmentApi.getInstance().listAppointments(null, storeId, employeeId);
|
||||||
List<AppointmentDTO> appointmentDTOs = responses.stream()
|
List<AppointmentDTO> appointmentDTOs = responses.stream()
|
||||||
@@ -133,9 +151,7 @@ public class AppointmentController {
|
|||||||
.filter(d -> d != null)
|
.filter(d -> d != null)
|
||||||
.collect(java.util.stream.Collectors.toSet());
|
.collect(java.util.stream.Collectors.toSet());
|
||||||
calendarPane.setEventDates(dates);
|
calendarPane.setEventDates(dates);
|
||||||
if (selectedCalendarDate != null) {
|
applyFilterPredicate();
|
||||||
filtered.setPredicate(apt -> apt.getAppointmentDate().equals(selectedCalendarDate.toString()));
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
@@ -153,7 +169,7 @@ public class AppointmentController {
|
|||||||
String query = text == null || text.trim().isEmpty() ? null : text.trim();
|
String query = text == null || text.trim().isEmpty() ? null : text.trim();
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
try {
|
try {
|
||||||
Long storeId = UserSession.getInstance().isAdmin() ? null : UserSession.getInstance().getStoreId();
|
Long storeId = selectedStoreId();
|
||||||
Long employeeId = btnMyAppointments.isSelected() ? UserSession.getInstance().getEmployeeId() : null;
|
Long employeeId = btnMyAppointments.isSelected() ? UserSession.getInstance().getEmployeeId() : null;
|
||||||
List<AppointmentResponse> responses = AppointmentApi.getInstance().listAppointments(query, storeId, employeeId);
|
List<AppointmentResponse> responses = AppointmentApi.getInstance().listAppointments(query, storeId, employeeId);
|
||||||
List<AppointmentDTO> appointmentDTOs = responses.stream()
|
List<AppointmentDTO> appointmentDTOs = responses.stream()
|
||||||
@@ -169,9 +185,7 @@ public class AppointmentController {
|
|||||||
.filter(d -> d != null)
|
.filter(d -> d != null)
|
||||||
.collect(java.util.stream.Collectors.toSet());
|
.collect(java.util.stream.Collectors.toSet());
|
||||||
calendarPane.setEventDates(dates);
|
calendarPane.setEventDates(dates);
|
||||||
if (selectedCalendarDate != null) {
|
applyFilterPredicate();
|
||||||
filtered.setPredicate(apt -> apt.getAppointmentDate().equals(selectedCalendarDate.toString()));
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
@@ -188,6 +202,8 @@ public class AppointmentController {
|
|||||||
@FXML
|
@FXML
|
||||||
void btnRefresh(ActionEvent event) {
|
void btnRefresh(ActionEvent event) {
|
||||||
txtSearch.clear();
|
txtSearch.clear();
|
||||||
|
cbStatusFilter.getSelectionModel().selectFirst();
|
||||||
|
cbStoreFilter.getSelectionModel().selectFirst();
|
||||||
selectedCalendarDate = null;
|
selectedCalendarDate = null;
|
||||||
filtered.setPredicate(a -> true);
|
filtered.setPredicate(a -> true);
|
||||||
tvAppointments.getSortOrder().clear();
|
tvAppointments.getSortOrder().clear();
|
||||||
@@ -291,6 +307,44 @@ public class AppointmentController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void loadStoreFilter() {
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
List<DropdownOption> stores = DropdownApi.getInstance().getStores();
|
||||||
|
DropdownOption allStores = new DropdownOption();
|
||||||
|
allStores.setLabel("All Stores");
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
storeOptions.clear();
|
||||||
|
storeOptions.addAll(stores);
|
||||||
|
java.util.List<DropdownOption> items = new java.util.ArrayList<>();
|
||||||
|
items.add(allStores);
|
||||||
|
items.addAll(stores);
|
||||||
|
cbStoreFilter.setItems(FXCollections.observableArrayList(items));
|
||||||
|
cbStoreFilter.getSelectionModel().selectFirst();
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
ActivityLogger.getInstance().logException("AppointmentController.loadStoreFilter", e, "Loading store filter");
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Long selectedStoreId() {
|
||||||
|
if (!UserSession.getInstance().isAdmin()) return UserSession.getInstance().getStoreId();
|
||||||
|
DropdownOption selected = cbStoreFilter.getValue();
|
||||||
|
return (selected != null && selected.getId() != null) ? selected.getId() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyFilterPredicate() {
|
||||||
|
String selectedStatus = cbStatusFilter.getValue();
|
||||||
|
filtered.setPredicate(apt -> {
|
||||||
|
boolean dateMatch = selectedCalendarDate == null
|
||||||
|
|| apt.getAppointmentDate().equals(selectedCalendarDate.toString());
|
||||||
|
boolean statusMatch = selectedStatus == null || selectedStatus.equals("All Statuses")
|
||||||
|
|| apt.getAppointmentStatus().equalsIgnoreCase(selectedStatus);
|
||||||
|
return dateMatch && statusMatch;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void showAlert(String title, String msg){
|
private void showAlert(String title, String msg){
|
||||||
Alert alert = new Alert(Alert.AlertType.INFORMATION);
|
Alert alert = new Alert(Alert.AlertType.INFORMATION);
|
||||||
alert.setTitle(title);
|
alert.setTitle(title);
|
||||||
|
|||||||
@@ -49,6 +49,9 @@ public class CustomerAccountsController {
|
|||||||
@FXML
|
@FXML
|
||||||
private TextField txtSearchCustomer;
|
private TextField txtSearchCustomer;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ComboBox<String> cbStatusFilter;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private Button btnEditCustomer;
|
private Button btnEditCustomer;
|
||||||
|
|
||||||
@@ -82,6 +85,10 @@ public class CustomerAccountsController {
|
|||||||
btnEditCustomer.setDisable(newVal == null));
|
btnEditCustomer.setDisable(newVal == null));
|
||||||
btnEditCustomer.setDisable(true);
|
btnEditCustomer.setDisable(true);
|
||||||
|
|
||||||
|
cbStatusFilter.setItems(FXCollections.observableArrayList("All Statuses", "Active", "Inactive"));
|
||||||
|
cbStatusFilter.getSelectionModel().selectFirst();
|
||||||
|
cbStatusFilter.valueProperty().addListener((obs, o, n) -> applyCustomerFilter(txtSearchCustomer.getText()));
|
||||||
|
|
||||||
txtSearchCustomer.textProperty().addListener((obs, o, n) -> applyCustomerFilter(n));
|
txtSearchCustomer.textProperty().addListener((obs, o, n) -> applyCustomerFilter(n));
|
||||||
|
|
||||||
refresh();
|
refresh();
|
||||||
@@ -90,6 +97,7 @@ public class CustomerAccountsController {
|
|||||||
@FXML
|
@FXML
|
||||||
void btnRefreshClicked(ActionEvent event) {
|
void btnRefreshClicked(ActionEvent event) {
|
||||||
txtSearchCustomer.clear();
|
txtSearchCustomer.clear();
|
||||||
|
cbStatusFilter.getSelectionModel().selectFirst();
|
||||||
TableViewSupport.clearSort(tvCustomers);
|
TableViewSupport.clearSort(tvCustomers);
|
||||||
refresh();
|
refresh();
|
||||||
TableViewSupport.flashStatus(lblStatus, "Refreshed");
|
TableViewSupport.flashStatus(lblStatus, "Refreshed");
|
||||||
@@ -154,16 +162,19 @@ public class CustomerAccountsController {
|
|||||||
|
|
||||||
private void applyCustomerFilter(String text) {
|
private void applyCustomerFilter(String text) {
|
||||||
String q = text == null ? "" : text.trim().toLowerCase();
|
String q = text == null ? "" : text.trim().toLowerCase();
|
||||||
if (q.isEmpty()) {
|
String selectedStatus = cbStatusFilter.getValue();
|
||||||
filteredCustomers.setPredicate(a -> true);
|
filteredCustomers.setPredicate(a -> {
|
||||||
return;
|
boolean textMatch = q.isEmpty()
|
||||||
}
|
|| safe(a.getUsername()).contains(q)
|
||||||
filteredCustomers.setPredicate(a ->
|
|
||||||
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)
|
||||||
|| safe(a.getPhone()).contains(q)
|
|| safe(a.getPhone()).contains(q);
|
||||||
);
|
boolean active = Boolean.TRUE.equals(a.getActive());
|
||||||
|
boolean statusMatch = selectedStatus == null || selectedStatus.equals("All Statuses")
|
||||||
|
|| (selectedStatus.equals("Active") && active)
|
||||||
|
|| (selectedStatus.equals("Inactive") && !active);
|
||||||
|
return textMatch && statusMatch;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String safe(String v) {
|
private static String safe(String v) {
|
||||||
|
|||||||
@@ -12,7 +12,9 @@ import javafx.scene.control.cell.PropertyValueFactory;
|
|||||||
import javafx.stage.Modality;
|
import javafx.stage.Modality;
|
||||||
import org.example.petshopdesktop.auth.UserSession;
|
import org.example.petshopdesktop.auth.UserSession;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
|
import org.example.petshopdesktop.api.dto.common.DropdownOption;
|
||||||
import org.example.petshopdesktop.api.dto.inventory.InventoryResponse;
|
import org.example.petshopdesktop.api.dto.inventory.InventoryResponse;
|
||||||
|
import org.example.petshopdesktop.api.endpoints.DropdownApi;
|
||||||
import org.example.petshopdesktop.api.endpoints.InventoryApi;
|
import org.example.petshopdesktop.api.endpoints.InventoryApi;
|
||||||
import org.example.petshopdesktop.controllers.dialogcontrollers.InventoryDialogController;
|
import org.example.petshopdesktop.controllers.dialogcontrollers.InventoryDialogController;
|
||||||
import org.example.petshopdesktop.models.Inventory;
|
import org.example.petshopdesktop.models.Inventory;
|
||||||
@@ -63,6 +65,11 @@ public class InventoryController {
|
|||||||
@FXML
|
@FXML
|
||||||
private TextField txtSearch;
|
private TextField txtSearch;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ComboBox<DropdownOption> cbStoreFilter;
|
||||||
|
|
||||||
|
private final java.util.ArrayList<DropdownOption> storeOptions = new java.util.ArrayList<>();
|
||||||
|
|
||||||
private ObservableList<Inventory> data = FXCollections.observableArrayList();
|
private ObservableList<Inventory> data = FXCollections.observableArrayList();
|
||||||
|
|
||||||
//Determines if in add/edit mode
|
//Determines if in add/edit mode
|
||||||
@@ -81,6 +88,13 @@ public class InventoryController {
|
|||||||
colQuantity.setCellValueFactory(new PropertyValueFactory<>("quantity"));
|
colQuantity.setCellValueFactory(new PropertyValueFactory<>("quantity"));
|
||||||
colStoreName.setCellValueFactory(new PropertyValueFactory<>("storeName"));
|
colStoreName.setCellValueFactory(new PropertyValueFactory<>("storeName"));
|
||||||
|
|
||||||
|
if (UserSession.getInstance().isAdmin()) {
|
||||||
|
cbStoreFilter.setVisible(true);
|
||||||
|
cbStoreFilter.setManaged(true);
|
||||||
|
loadStoreFilter();
|
||||||
|
}
|
||||||
|
cbStoreFilter.valueProperty().addListener((obs, o, n) -> displayInventory());
|
||||||
|
|
||||||
displayInventory();
|
displayInventory();
|
||||||
TableViewSupport.installDoubleClickAction(tvInventory, selected -> openDialog(selected, "Edit"));
|
TableViewSupport.installDoubleClickAction(tvInventory, selected -> openDialog(selected, "Edit"));
|
||||||
|
|
||||||
@@ -108,6 +122,7 @@ public class InventoryController {
|
|||||||
@FXML
|
@FXML
|
||||||
void btnRefresh(ActionEvent event) {
|
void btnRefresh(ActionEvent event) {
|
||||||
txtSearch.clear();
|
txtSearch.clear();
|
||||||
|
cbStoreFilter.getSelectionModel().selectFirst();
|
||||||
tvInventory.getSortOrder().clear();
|
tvInventory.getSortOrder().clear();
|
||||||
displayInventory();
|
displayInventory();
|
||||||
TableViewSupport.flashStatus(lblStatus, "Refreshed");
|
TableViewSupport.flashStatus(lblStatus, "Refreshed");
|
||||||
@@ -172,7 +187,7 @@ public class InventoryController {
|
|||||||
} else {
|
} else {
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
try {
|
try {
|
||||||
Long storeId = UserSession.getInstance().isAdmin() ? null : UserSession.getInstance().getStoreId();
|
Long storeId = selectedStoreId();
|
||||||
List<InventoryResponse> inventories = InventoryApi.getInstance().listInventory(filter, storeId);
|
List<InventoryResponse> inventories = InventoryApi.getInstance().listInventory(filter, storeId);
|
||||||
List<Inventory> inventoryList = inventories.stream()
|
List<Inventory> inventoryList = inventories.stream()
|
||||||
.map(this::mapToInventory)
|
.map(this::mapToInventory)
|
||||||
@@ -198,7 +213,7 @@ public class InventoryController {
|
|||||||
private void displayInventory() {
|
private void displayInventory() {
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
try {
|
try {
|
||||||
Long storeId = UserSession.getInstance().isAdmin() ? null : UserSession.getInstance().getStoreId();
|
Long storeId = selectedStoreId();
|
||||||
List<InventoryResponse> inventories = InventoryApi.getInstance().listInventory(null, storeId);
|
List<InventoryResponse> inventories = InventoryApi.getInstance().listInventory(null, storeId);
|
||||||
List<Inventory> inventoryList = inventories.stream()
|
List<Inventory> inventoryList = inventories.stream()
|
||||||
.map(this::mapToInventory)
|
.map(this::mapToInventory)
|
||||||
@@ -253,6 +268,33 @@ public class InventoryController {
|
|||||||
txtSearch.setText("");
|
txtSearch.setText("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void loadStoreFilter() {
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
List<DropdownOption> stores = DropdownApi.getInstance().getStores();
|
||||||
|
DropdownOption allStores = new DropdownOption();
|
||||||
|
allStores.setLabel("All Stores");
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
storeOptions.clear();
|
||||||
|
storeOptions.addAll(stores);
|
||||||
|
java.util.List<DropdownOption> items = new java.util.ArrayList<>();
|
||||||
|
items.add(allStores);
|
||||||
|
items.addAll(stores);
|
||||||
|
cbStoreFilter.setItems(FXCollections.observableArrayList(items));
|
||||||
|
cbStoreFilter.getSelectionModel().selectFirst();
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
ActivityLogger.getInstance().logException("InventoryController.loadStoreFilter", e, "Loading store filter");
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Long selectedStoreId() {
|
||||||
|
if (!UserSession.getInstance().isAdmin()) return UserSession.getInstance().getStoreId();
|
||||||
|
DropdownOption selected = cbStoreFilter.getValue();
|
||||||
|
return (selected != null && selected.getId() != null) ? selected.getId() : null;
|
||||||
|
}
|
||||||
|
|
||||||
private Inventory mapToInventory(InventoryResponse response) {
|
private Inventory mapToInventory(InventoryResponse response) {
|
||||||
return new Inventory(
|
return new Inventory(
|
||||||
response.getInventoryId().intValue(),
|
response.getInventoryId().intValue(),
|
||||||
|
|||||||
@@ -113,6 +113,9 @@ public class MainLayoutController {
|
|||||||
@FXML
|
@FXML
|
||||||
private Button btnRemoveAvatar;
|
private Button btnRemoveAvatar;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Button btnRefreshAvatar;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private Label lblUsername;
|
private Label lblUsername;
|
||||||
|
|
||||||
@@ -253,6 +256,29 @@ public class MainLayoutController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
void btnRefreshAvatarClicked(ActionEvent event) {
|
||||||
|
btnRefreshAvatar.setDisable(true);
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
UserInfoResponse userInfo = AuthApi.getInstance().getCurrentUser();
|
||||||
|
String displayName = userInfo.getFullName() == null || userInfo.getFullName().isBlank()
|
||||||
|
? UserSession.getInstance().getUsername()
|
||||||
|
: userInfo.getFullName();
|
||||||
|
Image avatarImage = loadAvatarImage(userInfo.getAvatarUrl());
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
UserSession.getInstance().setAvatarUrl(userInfo.getAvatarUrl());
|
||||||
|
renderAvatar(displayName, avatarImage);
|
||||||
|
btnRemoveAvatar.setDisable(userInfo.getAvatarUrl() == null || userInfo.getAvatarUrl().isBlank());
|
||||||
|
btnRefreshAvatar.setDisable(false);
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
Platform.runLater(() -> btnRefreshAvatar.setDisable(false));
|
||||||
|
ActivityLogger.getInstance().logException("MainLayoutController.btnRefreshAvatarClicked", e, "Refreshing avatar");
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
void btnRemoveAvatarClicked(ActionEvent event) {
|
void btnRemoveAvatarClicked(ActionEvent event) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -86,18 +86,20 @@ public class PetController {
|
|||||||
@FXML
|
@FXML
|
||||||
private ComboBox<String> cbStatusFilter;
|
private ComboBox<String> cbStatusFilter;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ComboBox<DropdownOption> cbStoreFilter;
|
||||||
|
|
||||||
|
private final java.util.ArrayList<DropdownOption> storeOptions = new java.util.ArrayList<>();
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private TextField txtSearch;
|
private TextField txtSearch;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
void btnRefresh(ActionEvent event) {
|
void btnRefresh(ActionEvent event) {
|
||||||
txtSearch.clear();
|
txtSearch.clear();
|
||||||
if (cbSpeciesFilter != null) {
|
if (cbSpeciesFilter != null) cbSpeciesFilter.getSelectionModel().selectFirst();
|
||||||
cbSpeciesFilter.getSelectionModel().selectFirst();
|
if (cbStatusFilter != null) cbStatusFilter.getSelectionModel().selectFirst();
|
||||||
}
|
cbStoreFilter.getSelectionModel().selectFirst();
|
||||||
if (cbStatusFilter != null) {
|
|
||||||
cbStatusFilter.getSelectionModel().selectFirst();
|
|
||||||
}
|
|
||||||
tvPets.getSortOrder().clear();
|
tvPets.getSortOrder().clear();
|
||||||
displayPets();
|
displayPets();
|
||||||
TableViewSupport.flashStatus(lblStatus, "Refreshed");
|
TableViewSupport.flashStatus(lblStatus, "Refreshed");
|
||||||
@@ -194,6 +196,13 @@ public class PetController {
|
|||||||
cbStatusFilter.setItems(FXCollections.observableArrayList("All Statuses", "Available", "Adopted", "Owned", "Pending"));
|
cbStatusFilter.setItems(FXCollections.observableArrayList("All Statuses", "Available", "Adopted", "Owned", "Pending"));
|
||||||
cbStatusFilter.getSelectionModel().selectFirst();
|
cbStatusFilter.getSelectionModel().selectFirst();
|
||||||
|
|
||||||
|
if (UserSession.getInstance().isAdmin()) {
|
||||||
|
cbStoreFilter.setVisible(true);
|
||||||
|
cbStoreFilter.setManaged(true);
|
||||||
|
loadStoreFilter();
|
||||||
|
}
|
||||||
|
cbStoreFilter.valueProperty().addListener((obs, o, n) -> applyFilters());
|
||||||
|
|
||||||
displayPets();
|
displayPets();
|
||||||
TableViewSupport.installDoubleClickAction(tvPets, selected -> openDialog(selected, "Edit"));
|
TableViewSupport.installDoubleClickAction(tvPets, selected -> openDialog(selected, "Edit"));
|
||||||
|
|
||||||
@@ -229,7 +238,7 @@ public class PetController {
|
|||||||
} else {
|
} else {
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
try {
|
try {
|
||||||
Long storeId = UserSession.getInstance().isAdmin() ? null : UserSession.getInstance().getStoreId();
|
Long storeId = selectedStoreId();
|
||||||
List<PetResponse> pets = PetApi.getInstance().listPets(filter, species, status, storeId);
|
List<PetResponse> pets = PetApi.getInstance().listPets(filter, species, status, storeId);
|
||||||
List<Pet> petList = pets.stream()
|
List<Pet> petList = pets.stream()
|
||||||
.map(this::mapToPet)
|
.map(this::mapToPet)
|
||||||
@@ -255,7 +264,7 @@ public class PetController {
|
|||||||
private void displayPets() {
|
private void displayPets() {
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
try {
|
try {
|
||||||
Long storeId = UserSession.getInstance().isAdmin() ? null : UserSession.getInstance().getStoreId();
|
Long storeId = selectedStoreId();
|
||||||
List<PetResponse> pets = PetApi.getInstance().listPets(null, selectedSpecies(), selectedStatus(), storeId);
|
List<PetResponse> pets = PetApi.getInstance().listPets(null, selectedSpecies(), selectedStatus(), storeId);
|
||||||
List<Pet> petList = pets.stream()
|
List<Pet> petList = pets.stream()
|
||||||
.map(this::mapToPet)
|
.map(this::mapToPet)
|
||||||
@@ -281,6 +290,33 @@ public class PetController {
|
|||||||
displayFilteredPet(txtSearch.getText());
|
displayFilteredPet(txtSearch.getText());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void loadStoreFilter() {
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
List<DropdownOption> stores = DropdownApi.getInstance().getStores();
|
||||||
|
DropdownOption allStores = new DropdownOption();
|
||||||
|
allStores.setLabel("All Stores");
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
storeOptions.clear();
|
||||||
|
storeOptions.addAll(stores);
|
||||||
|
java.util.List<DropdownOption> items = new java.util.ArrayList<>();
|
||||||
|
items.add(allStores);
|
||||||
|
items.addAll(stores);
|
||||||
|
cbStoreFilter.setItems(FXCollections.observableArrayList(items));
|
||||||
|
cbStoreFilter.getSelectionModel().selectFirst();
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
ActivityLogger.getInstance().logException("PetController.loadStoreFilter", e, "Loading store filter");
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Long selectedStoreId() {
|
||||||
|
if (!UserSession.getInstance().isAdmin()) return UserSession.getInstance().getStoreId();
|
||||||
|
DropdownOption selected = cbStoreFilter.getValue();
|
||||||
|
return (selected != null && selected.getId() != null) ? selected.getId() : null;
|
||||||
|
}
|
||||||
|
|
||||||
private void loadSpeciesFilter() {
|
private void loadSpeciesFilter() {
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -146,6 +146,20 @@ public class SaleController {
|
|||||||
@FXML
|
@FXML
|
||||||
private TextField txtSearch;
|
private TextField txtSearch;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ComboBox<String> cbFilterPayment;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ComboBox<String> cbFilterRefundStatus;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ComboBox<DropdownOption> cbFilterCustomer;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ComboBox<DropdownOption> cbStoreFilter;
|
||||||
|
|
||||||
|
private final java.util.ArrayList<DropdownOption> storeOptions = new java.util.ArrayList<>();
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private ComboBox<DropdownOption> cbCustomer;
|
private ComboBox<DropdownOption> cbCustomer;
|
||||||
|
|
||||||
@@ -249,6 +263,24 @@ public class SaleController {
|
|||||||
TableViewSupport.bindSortedItems(tvSales, filteredSales);
|
TableViewSupport.bindSortedItems(tvSales, filteredSales);
|
||||||
TableViewSupport.installDoubleClickAction(tvSales, selected -> openSaleDetailDialog(selected.getSaleId()));
|
TableViewSupport.installDoubleClickAction(tvSales, selected -> openSaleDetailDialog(selected.getSaleId()));
|
||||||
|
|
||||||
|
if (UserSession.getInstance().isAdmin()) {
|
||||||
|
cbStoreFilter.setVisible(true);
|
||||||
|
cbStoreFilter.setManaged(true);
|
||||||
|
loadStoreFilter();
|
||||||
|
}
|
||||||
|
cbStoreFilter.valueProperty().addListener((obs, o, n) -> refreshSales(false));
|
||||||
|
|
||||||
|
cbFilterPayment.setItems(FXCollections.observableArrayList("All Payments", "Cash", "Card"));
|
||||||
|
cbFilterPayment.getSelectionModel().selectFirst();
|
||||||
|
cbFilterPayment.valueProperty().addListener((obs, o, n) -> applySalesFilter(txtSearch.getText()));
|
||||||
|
|
||||||
|
cbFilterRefundStatus.setItems(FXCollections.observableArrayList("All Status", "Sales Only", "Refunds Only"));
|
||||||
|
cbFilterRefundStatus.getSelectionModel().selectFirst();
|
||||||
|
cbFilterRefundStatus.valueProperty().addListener((obs, o, n) -> applySalesFilter(txtSearch.getText()));
|
||||||
|
|
||||||
|
loadCustomerFilter();
|
||||||
|
cbFilterCustomer.valueProperty().addListener((obs, o, n) -> applySalesFilter(txtSearch.getText()));
|
||||||
|
|
||||||
txtSearch.textProperty().addListener((obs, oldVal, newVal) -> applySalesFilter(newVal));
|
txtSearch.textProperty().addListener((obs, oldVal, newVal) -> applySalesFilter(newVal));
|
||||||
tvSales.widthProperty().addListener((obs, oldWidth, newWidth) -> updateSalesColumnWidths(newWidth.doubleValue()));
|
tvSales.widthProperty().addListener((obs, oldWidth, newWidth) -> updateSalesColumnWidths(newWidth.doubleValue()));
|
||||||
tvCart.widthProperty().addListener((obs, oldWidth, newWidth) -> updateCartColumnWidths(newWidth.doubleValue()));
|
tvCart.widthProperty().addListener((obs, oldWidth, newWidth) -> updateCartColumnWidths(newWidth.doubleValue()));
|
||||||
@@ -404,8 +436,7 @@ public class SaleController {
|
|||||||
Task<List<SaleLineItem>> task = new Task<List<SaleLineItem>>() {
|
Task<List<SaleLineItem>> task = new Task<List<SaleLineItem>>() {
|
||||||
@Override
|
@Override
|
||||||
protected List<SaleLineItem> call() throws Exception {
|
protected List<SaleLineItem> call() throws Exception {
|
||||||
Long storeId = UserSession.getInstance().isAdmin() ? null : UserSession.getInstance().getStoreId();
|
List<SaleResponse> sales = SaleApi.getInstance().listAllSales(null, selectedStoreId());
|
||||||
List<SaleResponse> sales = SaleApi.getInstance().listAllSales(null, storeId);
|
|
||||||
sales.sort(Comparator.comparing(SaleResponse::getSaleDate, Comparator.nullsLast(Comparator.reverseOrder()))
|
sales.sort(Comparator.comparing(SaleResponse::getSaleDate, Comparator.nullsLast(Comparator.reverseOrder()))
|
||||||
.thenComparing(SaleResponse::getSaleId, Comparator.nullsLast(Comparator.reverseOrder())));
|
.thenComparing(SaleResponse::getSaleId, Comparator.nullsLast(Comparator.reverseOrder())));
|
||||||
List<SaleLineItem> lineItems = new ArrayList<>();
|
List<SaleLineItem> lineItems = new ArrayList<>();
|
||||||
@@ -465,6 +496,10 @@ public class SaleController {
|
|||||||
@FXML
|
@FXML
|
||||||
void btnRefresh(ActionEvent event) {
|
void btnRefresh(ActionEvent event) {
|
||||||
txtSearch.clear();
|
txtSearch.clear();
|
||||||
|
cbFilterPayment.getSelectionModel().selectFirst();
|
||||||
|
cbFilterRefundStatus.getSelectionModel().selectFirst();
|
||||||
|
cbFilterCustomer.getSelectionModel().selectFirst();
|
||||||
|
cbStoreFilter.getSelectionModel().selectFirst();
|
||||||
TableViewSupport.clearSort(tvSales);
|
TableViewSupport.clearSort(tvSales);
|
||||||
refreshSales(false);
|
refreshSales(false);
|
||||||
TableViewSupport.flashStatus(lblStatus, "Refreshed");
|
TableViewSupport.flashStatus(lblStatus, "Refreshed");
|
||||||
@@ -942,25 +977,83 @@ public class SaleController {
|
|||||||
|
|
||||||
private void applySalesFilter(String filter) {
|
private void applySalesFilter(String filter) {
|
||||||
String f = filter == null ? "" : filter.trim().toLowerCase();
|
String f = filter == null ? "" : filter.trim().toLowerCase();
|
||||||
if (f.isEmpty()) {
|
String selectedPayment = cbFilterPayment.getValue();
|
||||||
filteredSales.setPredicate(s -> true);
|
String selectedStatus = cbFilterRefundStatus.getValue();
|
||||||
return;
|
DropdownOption selectedCustomer = cbFilterCustomer.getValue();
|
||||||
}
|
|
||||||
|
|
||||||
filteredSales.setPredicate(s ->
|
filteredSales.setPredicate(s -> {
|
||||||
String.valueOf(s.getSaleId()).contains(f)
|
boolean textMatch = f.isEmpty()
|
||||||
|
|| String.valueOf(s.getSaleId()).contains(f)
|
||||||
|| safe(s.getSaleDate()).contains(f)
|
|| safe(s.getSaleDate()).contains(f)
|
||||||
|| safe(s.getEmployeeName()).contains(f)
|
|| safe(s.getEmployeeName()).contains(f)
|
||||||
|| safe(s.getCustomerName()).contains(f)
|
|| safe(s.getCustomerName()).contains(f)
|
||||||
|| safe(s.getItemName()).contains(f)
|
|| safe(s.getItemName()).contains(f)
|
||||||
|| safe(s.getPaymentMethod()).contains(f)
|
|| safe(s.getPaymentMethod()).contains(f);
|
||||||
);
|
|
||||||
|
boolean paymentMatch = selectedPayment == null || selectedPayment.equals("All Payments")
|
||||||
|
|| safe(s.getPaymentMethod()).equals(selectedPayment.toLowerCase());
|
||||||
|
|
||||||
|
boolean refundMatch = selectedStatus == null || selectedStatus.equals("All Status")
|
||||||
|
|| (selectedStatus.equals("Sales Only") && !s.isRefund())
|
||||||
|
|| (selectedStatus.equals("Refunds Only") && s.isRefund());
|
||||||
|
|
||||||
|
boolean customerMatch = selectedCustomer == null || selectedCustomer.getId() == null
|
||||||
|
|| safe(s.getCustomerName()).equals(safe(selectedCustomer.getLabel()));
|
||||||
|
|
||||||
|
return textMatch && paymentMatch && refundMatch && customerMatch;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String safe(String v) {
|
private static String safe(String v) {
|
||||||
return v == null ? "" : v.toLowerCase();
|
return v == null ? "" : v.toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void loadCustomerFilter() {
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
List<DropdownOption> customers = DropdownApi.getInstance().getCustomers();
|
||||||
|
DropdownOption allCustomers = new DropdownOption();
|
||||||
|
allCustomers.setLabel("All Customers");
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
java.util.List<DropdownOption> items = new java.util.ArrayList<>();
|
||||||
|
items.add(allCustomers);
|
||||||
|
items.addAll(customers);
|
||||||
|
cbFilterCustomer.setItems(FXCollections.observableArrayList(items));
|
||||||
|
cbFilterCustomer.getSelectionModel().selectFirst();
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
ActivityLogger.getInstance().logException("SaleController.loadCustomerFilter", e, "Loading customer filter");
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadStoreFilter() {
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
List<DropdownOption> stores = DropdownApi.getInstance().getStores();
|
||||||
|
DropdownOption allStores = new DropdownOption();
|
||||||
|
allStores.setLabel("All Stores");
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
storeOptions.clear();
|
||||||
|
storeOptions.addAll(stores);
|
||||||
|
java.util.List<DropdownOption> items = new java.util.ArrayList<>();
|
||||||
|
items.add(allStores);
|
||||||
|
items.addAll(stores);
|
||||||
|
cbStoreFilter.setItems(FXCollections.observableArrayList(items));
|
||||||
|
cbStoreFilter.getSelectionModel().selectFirst();
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
ActivityLogger.getInstance().logException("SaleController.loadStoreFilter", e, "Loading store filter");
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Long selectedStoreId() {
|
||||||
|
if (!UserSession.getInstance().isAdmin()) return UserSession.getInstance().getStoreId();
|
||||||
|
DropdownOption selected = cbStoreFilter.getValue();
|
||||||
|
return (selected != null && selected.getId() != null) ? selected.getId() : null;
|
||||||
|
}
|
||||||
|
|
||||||
private void showError(String title, String message) {
|
private void showError(String title, String message) {
|
||||||
Alert alert = new Alert(Alert.AlertType.ERROR);
|
Alert alert = new Alert(Alert.AlertType.ERROR);
|
||||||
alert.setTitle(title);
|
alert.setTitle(title);
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ public class StaffAccountsController {
|
|||||||
@FXML private TableColumn<EmployeeResponse, String> colStatus;
|
@FXML private TableColumn<EmployeeResponse, String> colStatus;
|
||||||
@FXML private TableColumn<EmployeeResponse, Object> colCreated;
|
@FXML private TableColumn<EmployeeResponse, Object> colCreated;
|
||||||
@FXML private TextField txtSearch;
|
@FXML private TextField txtSearch;
|
||||||
|
@FXML private ComboBox<String> cbRoleFilter;
|
||||||
|
@FXML private ComboBox<String> cbStatusFilter;
|
||||||
@FXML private Button btnRefresh;
|
@FXML private Button btnRefresh;
|
||||||
@FXML private Button btnCreateAccount;
|
@FXML private Button btnCreateAccount;
|
||||||
@FXML private Button btnEditAccount;
|
@FXML private Button btnEditAccount;
|
||||||
@@ -66,6 +68,14 @@ public class StaffAccountsController {
|
|||||||
btnEditAccount.setDisable(newVal == null));
|
btnEditAccount.setDisable(newVal == null));
|
||||||
btnEditAccount.setDisable(true);
|
btnEditAccount.setDisable(true);
|
||||||
|
|
||||||
|
cbRoleFilter.setItems(FXCollections.observableArrayList("All Roles", "Admin", "Staff"));
|
||||||
|
cbRoleFilter.getSelectionModel().selectFirst();
|
||||||
|
cbRoleFilter.valueProperty().addListener((obs, o, n) -> applyStaffFilter(txtSearch.getText()));
|
||||||
|
|
||||||
|
cbStatusFilter.setItems(FXCollections.observableArrayList("All Statuses", "Active", "Inactive"));
|
||||||
|
cbStatusFilter.getSelectionModel().selectFirst();
|
||||||
|
cbStatusFilter.valueProperty().addListener((obs, o, n) -> applyStaffFilter(txtSearch.getText()));
|
||||||
|
|
||||||
txtSearch.textProperty().addListener((obs, o, n) -> applyStaffFilter(n));
|
txtSearch.textProperty().addListener((obs, o, n) -> applyStaffFilter(n));
|
||||||
|
|
||||||
refresh();
|
refresh();
|
||||||
@@ -74,6 +84,8 @@ public class StaffAccountsController {
|
|||||||
@FXML
|
@FXML
|
||||||
void btnRefreshClicked(ActionEvent event) {
|
void btnRefreshClicked(ActionEvent event) {
|
||||||
txtSearch.clear();
|
txtSearch.clear();
|
||||||
|
cbRoleFilter.getSelectionModel().selectFirst();
|
||||||
|
cbStatusFilter.getSelectionModel().selectFirst();
|
||||||
TableViewSupport.clearSort(tvStaff);
|
TableViewSupport.clearSort(tvStaff);
|
||||||
refresh();
|
refresh();
|
||||||
TableViewSupport.flashStatus(lblStatus, "Refreshed");
|
TableViewSupport.flashStatus(lblStatus, "Refreshed");
|
||||||
@@ -166,18 +178,24 @@ public class StaffAccountsController {
|
|||||||
|
|
||||||
private void applyStaffFilter(String text) {
|
private void applyStaffFilter(String text) {
|
||||||
String q = text == null ? "" : text.trim().toLowerCase();
|
String q = text == null ? "" : text.trim().toLowerCase();
|
||||||
if (q.isEmpty()) {
|
String selectedRole = cbRoleFilter.getValue();
|
||||||
filteredStaff.setPredicate(a -> true);
|
String selectedStatus = cbStatusFilter.getValue();
|
||||||
return;
|
filteredStaff.setPredicate(a -> {
|
||||||
}
|
boolean textMatch = q.isEmpty()
|
||||||
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)
|
||||||
|| safe(a.getPhone()).contains(q)
|
|| safe(a.getPhone()).contains(q)
|
||||||
|| safe(a.getRole()).contains(q)
|
|| safe(a.getRole()).contains(q)
|
||||||
|| safe(a.getStaffRole()).contains(q)
|
|| safe(a.getStaffRole()).contains(q);
|
||||||
);
|
boolean active = Boolean.TRUE.equals(a.getActive());
|
||||||
|
boolean statusMatch = selectedStatus == null || selectedStatus.equals("All Statuses")
|
||||||
|
|| (selectedStatus.equals("Active") && active)
|
||||||
|
|| (selectedStatus.equals("Inactive") && !active);
|
||||||
|
boolean roleMatch = selectedRole == null || selectedRole.equals("All Roles")
|
||||||
|
|| safe(a.getRole()).equals(selectedRole.toLowerCase());
|
||||||
|
return textMatch && statusMatch && roleMatch;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String safe(String v) {
|
private static String safe(String v) {
|
||||||
|
|||||||
@@ -59,6 +59,8 @@
|
|||||||
<Insets bottom="6.0" left="10.0" right="10.0" top="6.0" />
|
<Insets bottom="6.0" left="10.0" right="10.0" top="6.0" />
|
||||||
</padding>
|
</padding>
|
||||||
</Button>
|
</Button>
|
||||||
|
<HBox spacing="6.0">
|
||||||
|
<children>
|
||||||
<Button fx:id="btnRemoveAvatar" mnemonicParsing="false" onAction="#btnRemoveAvatarClicked" style="-fx-background-color: transparent; -fx-border-color: rgba(226,232,240,0.35); -fx-border-radius: 8; -fx-background-radius: 8; -fx-cursor: hand; -fx-focus-color: transparent; -fx-faint-focus-color: transparent;" text="Remove" textFill="#cbd5e1">
|
<Button fx:id="btnRemoveAvatar" mnemonicParsing="false" onAction="#btnRemoveAvatarClicked" style="-fx-background-color: transparent; -fx-border-color: rgba(226,232,240,0.35); -fx-border-radius: 8; -fx-background-radius: 8; -fx-cursor: hand; -fx-focus-color: transparent; -fx-faint-focus-color: transparent;" text="Remove" textFill="#cbd5e1">
|
||||||
<font>
|
<font>
|
||||||
<Font name="System" size="11.0" />
|
<Font name="System" size="11.0" />
|
||||||
@@ -67,6 +69,16 @@
|
|||||||
<Insets bottom="5.0" left="10.0" right="10.0" top="5.0" />
|
<Insets bottom="5.0" left="10.0" right="10.0" top="5.0" />
|
||||||
</padding>
|
</padding>
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button fx:id="btnRefreshAvatar" mnemonicParsing="false" onAction="#btnRefreshAvatarClicked" style="-fx-background-color: transparent; -fx-border-color: rgba(226,232,240,0.35); -fx-border-radius: 8; -fx-background-radius: 8; -fx-cursor: hand; -fx-focus-color: transparent; -fx-faint-focus-color: transparent;" text="↻" textFill="#cbd5e1">
|
||||||
|
<font>
|
||||||
|
<Font name="System" size="13.0" />
|
||||||
|
</font>
|
||||||
|
<padding>
|
||||||
|
<Insets bottom="5.0" left="8.0" right="8.0" top="5.0" />
|
||||||
|
</padding>
|
||||||
|
</Button>
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
</children>
|
</children>
|
||||||
</VBox>
|
</VBox>
|
||||||
</children>
|
</children>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
<?import javafx.geometry.Insets?>
|
<?import javafx.geometry.Insets?>
|
||||||
<?import javafx.scene.control.Button?>
|
<?import javafx.scene.control.Button?>
|
||||||
|
<?import javafx.scene.control.ComboBox?>
|
||||||
<?import javafx.scene.control.Label?>
|
<?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?>
|
||||||
@@ -72,6 +73,8 @@
|
|||||||
<Font size="15.0" />
|
<Font size="15.0" />
|
||||||
</font>
|
</font>
|
||||||
</TextField>
|
</TextField>
|
||||||
|
<ComboBox fx:id="cbStatusFilter" prefWidth="150.0" promptText="All Statuses" />
|
||||||
|
<ComboBox fx:id="cbStoreFilter" prefWidth="150.0" promptText="All Stores" visible="false" managed="false" />
|
||||||
</children>
|
</children>
|
||||||
</HBox>
|
</HBox>
|
||||||
<CalendarPane fx:id="calendarPane" maxWidth="Infinity" />
|
<CalendarPane fx:id="calendarPane" maxWidth="Infinity" />
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
<?import javafx.geometry.Insets?>
|
<?import javafx.geometry.Insets?>
|
||||||
<?import javafx.scene.control.Button?>
|
<?import javafx.scene.control.Button?>
|
||||||
|
<?import javafx.scene.control.ComboBox?>
|
||||||
<?import javafx.scene.control.Label?>
|
<?import javafx.scene.control.Label?>
|
||||||
<?import javafx.scene.control.ToggleButton?>
|
<?import javafx.scene.control.ToggleButton?>
|
||||||
<?import javafx.scene.control.TableColumn?>
|
<?import javafx.scene.control.TableColumn?>
|
||||||
@@ -81,6 +82,8 @@
|
|||||||
<Font size="15.0" />
|
<Font size="15.0" />
|
||||||
</font>
|
</font>
|
||||||
</TextField>
|
</TextField>
|
||||||
|
<ComboBox fx:id="cbStatusFilter" prefWidth="150.0" promptText="All Statuses" />
|
||||||
|
<ComboBox fx:id="cbStoreFilter" prefWidth="150.0" promptText="All Stores" visible="false" managed="false" />
|
||||||
</children>
|
</children>
|
||||||
</HBox>
|
</HBox>
|
||||||
<CalendarPane fx:id="calendarPane" maxWidth="Infinity" />
|
<CalendarPane fx:id="calendarPane" maxWidth="Infinity" />
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
<?import javafx.geometry.Insets?>
|
<?import javafx.geometry.Insets?>
|
||||||
<?import javafx.scene.control.Button?>
|
<?import javafx.scene.control.Button?>
|
||||||
|
<?import javafx.scene.control.ComboBox?>
|
||||||
<?import javafx.scene.control.Label?>
|
<?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?>
|
||||||
@@ -62,6 +63,7 @@
|
|||||||
<Font size="15.0" />
|
<Font size="15.0" />
|
||||||
</font>
|
</font>
|
||||||
</TextField>
|
</TextField>
|
||||||
|
<ComboBox fx:id="cbStatusFilter" prefWidth="140.0" promptText="All Statuses" />
|
||||||
</children>
|
</children>
|
||||||
</HBox>
|
</HBox>
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
<?import javafx.geometry.Insets?>
|
<?import javafx.geometry.Insets?>
|
||||||
<?import javafx.scene.control.Button?>
|
<?import javafx.scene.control.Button?>
|
||||||
|
<?import javafx.scene.control.ComboBox?>
|
||||||
<?import javafx.scene.control.Label?>
|
<?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?>
|
||||||
@@ -71,6 +72,7 @@
|
|||||||
<Font size="15.0" />
|
<Font size="15.0" />
|
||||||
</font>
|
</font>
|
||||||
</TextField>
|
</TextField>
|
||||||
|
<ComboBox fx:id="cbStoreFilter" prefWidth="150.0" promptText="All Stores" visible="false" managed="false" />
|
||||||
</children>
|
</children>
|
||||||
</HBox>
|
</HBox>
|
||||||
<Label fx:id="lblStatus" text="" textFill="#64748b" visible="false" managed="true">
|
<Label fx:id="lblStatus" text="" textFill="#64748b" visible="false" managed="true">
|
||||||
|
|||||||
@@ -75,6 +75,7 @@
|
|||||||
</TextField>
|
</TextField>
|
||||||
<ComboBox fx:id="cbSpeciesFilter" prefWidth="150.0" promptText="Species" />
|
<ComboBox fx:id="cbSpeciesFilter" prefWidth="150.0" promptText="Species" />
|
||||||
<ComboBox fx:id="cbStatusFilter" prefWidth="150.0" promptText="Status" />
|
<ComboBox fx:id="cbStatusFilter" prefWidth="150.0" promptText="Status" />
|
||||||
|
<ComboBox fx:id="cbStoreFilter" prefWidth="150.0" promptText="All Stores" visible="false" managed="false" />
|
||||||
</children>
|
</children>
|
||||||
</HBox>
|
</HBox>
|
||||||
<Label fx:id="lblStatus" text="" textFill="#64748b" visible="false" managed="true">
|
<Label fx:id="lblStatus" text="" textFill="#64748b" visible="false" managed="true">
|
||||||
|
|||||||
@@ -210,6 +210,10 @@
|
|||||||
<Font size="15.0" />
|
<Font size="15.0" />
|
||||||
</font>
|
</font>
|
||||||
</TextField>
|
</TextField>
|
||||||
|
<ComboBox fx:id="cbFilterPayment" prefWidth="140.0" promptText="All Payments" />
|
||||||
|
<ComboBox fx:id="cbFilterRefundStatus" prefWidth="140.0" promptText="All Status" />
|
||||||
|
<ComboBox fx:id="cbFilterCustomer" prefWidth="160.0" promptText="All Customers" />
|
||||||
|
<ComboBox fx:id="cbStoreFilter" prefWidth="150.0" promptText="All Stores" visible="false" managed="false" />
|
||||||
</children>
|
</children>
|
||||||
</HBox>
|
</HBox>
|
||||||
<TableView fx:id="tvSales" prefHeight="270.0" style="-fx-background-color: white; -fx-background-radius: 12; -fx-padding: 2;" VBox.vgrow="ALWAYS">
|
<TableView fx:id="tvSales" prefHeight="270.0" style="-fx-background-color: white; -fx-background-radius: 12; -fx-padding: 2;" VBox.vgrow="ALWAYS">
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
<?import javafx.geometry.Insets?>
|
<?import javafx.geometry.Insets?>
|
||||||
<?import javafx.scene.control.Button?>
|
<?import javafx.scene.control.Button?>
|
||||||
|
<?import javafx.scene.control.ComboBox?>
|
||||||
<?import javafx.scene.control.Label?>
|
<?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?>
|
||||||
@@ -70,6 +71,8 @@
|
|||||||
<Font size="15.0" />
|
<Font size="15.0" />
|
||||||
</font>
|
</font>
|
||||||
</TextField>
|
</TextField>
|
||||||
|
<ComboBox fx:id="cbRoleFilter" prefWidth="120.0" promptText="All Roles" />
|
||||||
|
<ComboBox fx:id="cbStatusFilter" prefWidth="130.0" promptText="All Statuses" />
|
||||||
</children>
|
</children>
|
||||||
</HBox>
|
</HBox>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user