made admin analyics able to select store

This commit is contained in:
Alex
2026-04-14 23:17:12 -06:00
parent 1b4a96c923
commit c5a59e8de3
5 changed files with 118 additions and 1 deletions

View File

@@ -57,11 +57,13 @@ public class AnalyticsFragment extends Fragment {
binding.btnMyAnalytics.setOnClickListener(v -> {
viewModel.setViewMode("mine");
updateViewModeButtonStyles("mine");
updateStoreFilterVisibility("mine");
});
binding.btnStoreAnalytics.setOnClickListener(v -> {
viewModel.setViewMode("store");
updateViewModeButtonStyles("store");
updateStoreFilterVisibility("store");
});
}
@@ -72,6 +74,13 @@ public class AnalyticsFragment extends Fragment {
android.content.res.ColorStateList.valueOf(mode.equals("store") ? COLOR_SELECTED : COLOR_UNSELECTED));
}
private void updateStoreFilterVisibility(String mode) {
boolean isAdmin = "ADMIN".equalsIgnoreCase(tokenManager.getRole());
int vis = (isAdmin && mode.equals("store")) ? View.VISIBLE : View.GONE;
binding.tvStoreFilterLabel.setVisibility(vis);
binding.spinnerFilterStore.setVisibility(vis);
}
// Filter Panel
private void setupFilterPanel() {
@@ -126,6 +135,9 @@ public class AnalyticsFragment extends Fragment {
int topNPos = binding.spinnerTopN.getSelectedItemPosition();
filter.topN = (topNPos >= 0 && topNPos < TOP_N_VALUES.length) ? TOP_N_VALUES[topNPos] : 5;
Object store = binding.spinnerFilterStore.getSelectedItem();
viewModel.setStoreFilter(store != null ? store.toString() : "All Stores");
updateFilterSummary();
viewModel.applyFilter(filter);
}
@@ -134,8 +146,8 @@ public class AnalyticsFragment extends Fragment {
binding.etFilterStartDate.setText("");
binding.etFilterEndDate.setText("");
binding.spinnerTopN.setSelection(0);
// Reset payment method to "All"
SpinnerUtils.setSelectionByValue(binding.spinnerFilterPayment, "All");
SpinnerUtils.setSelectionByValue(binding.spinnerFilterStore, "All Stores");
updateFilterSummary();
viewModel.resetFilter();
}
@@ -192,6 +204,16 @@ public class AnalyticsFragment extends Fragment {
methods.toArray(new String[0]));
SpinnerUtils.setSelectionByValue(binding.spinnerFilterPayment, currentSelection);
});
viewModel.getAvailableStores().observe(getViewLifecycleOwner(), stores -> {
if (stores == null || stores.isEmpty()) return;
String currentSelection = binding.spinnerFilterStore.getSelectedItem() != null
? binding.spinnerFilterStore.getSelectedItem().toString() : "All Stores";
SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerFilterStore,
stores.toArray(new String[0]));
SpinnerUtils.setSelectionByValue(binding.spinnerFilterStore, currentSelection);
updateStoreFilterVisibility(viewModel.getViewMode());
});
}
@Override

View File

@@ -37,10 +37,12 @@ public class AnalyticsViewModel extends ViewModel {
private final MutableLiveData<Boolean> isLoading = new MutableLiveData<>(false);
private final MutableLiveData<String> errorMessage = new MutableLiveData<>();
private final MutableLiveData<List<String>> availablePaymentMethods = new MutableLiveData<>(new ArrayList<>());
private final MutableLiveData<List<String>> availableStores = new MutableLiveData<>(new ArrayList<>());
private List<SaleDTO> cachedSales = new ArrayList<>();
private FilterState currentFilter = new FilterState();
private String viewMode = "store";
private String storeFilter = "All Stores";
@Inject
public AnalyticsViewModel(SaleRepository saleRepository, TokenManager tokenManager) {
@@ -52,6 +54,7 @@ public class AnalyticsViewModel extends ViewModel {
public LiveData<Boolean> getIsLoading() { return isLoading; }
public LiveData<String> getErrorMessage() { return errorMessage; }
public LiveData<List<String>> getAvailablePaymentMethods() { return availablePaymentMethods; }
public LiveData<List<String>> getAvailableStores() { return availableStores; }
public void loadAnalytics() {
isLoading.setValue(true);
@@ -61,6 +64,7 @@ public class AnalyticsViewModel extends ViewModel {
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
cachedSales = resource.data.getContent();
derivePaymentMethods();
deriveStores();
applyCurrentFilter();
isLoading.setValue(false);
} else if (resource.status == Resource.Status.ERROR) {
@@ -78,6 +82,7 @@ public class AnalyticsViewModel extends ViewModel {
public void resetFilter() {
currentFilter = new FilterState();
storeFilter = "All Stores";
applyCurrentFilter();
}
@@ -90,6 +95,15 @@ public class AnalyticsViewModel extends ViewModel {
return viewMode;
}
public void setStoreFilter(String store) {
storeFilter = (store != null && !store.isEmpty()) ? store : "All Stores";
applyCurrentFilter();
}
public String getStoreFilter() {
return storeFilter;
}
private void applyCurrentFilter() {
List<SaleDTO> salesForMode;
if (viewMode.equals("mine")) {
@@ -100,10 +114,29 @@ public class AnalyticsViewModel extends ViewModel {
} else {
salesForMode = cachedSales;
}
if (!storeFilter.equals("All Stores") && !storeFilter.isEmpty()) {
final String sf = storeFilter;
salesForMode = salesForMode.stream()
.filter(s -> sf.equalsIgnoreCase(s.getStoreName() != null ? s.getStoreName() : ""))
.collect(Collectors.toList());
}
List<SaleDTO> filtered = filterSales(salesForMode, currentFilter);
computeAnalytics(filtered, currentFilter);
}
private void deriveStores() {
java.util.Set<String> stores = new java.util.TreeSet<>();
for (SaleDTO s : cachedSales) {
if (s.getStoreName() != null && !s.getStoreName().isEmpty()) {
stores.add(s.getStoreName());
}
}
List<String> result = new ArrayList<>();
result.add("All Stores");
result.addAll(stores);
availableStores.setValue(result);
}
private void derivePaymentMethods() {
java.util.Set<String> methods = new java.util.TreeSet<>();
for (SaleDTO s : cachedSales) {

View File

@@ -136,6 +136,23 @@
android:layout_marginTop="12dp"
android:visibility="gone">
<TextView
android:id="@+id/tvStoreFilterLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Store"
android:textColor="@color/text_light"
android:textSize="11sp"
android:layout_marginBottom="4dp"
android:visibility="gone"/>
<Spinner
android:id="@+id/spinnerFilterStore"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="12dp"
android:visibility="gone"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"

View File

@@ -101,6 +101,9 @@ public class AnalyticsController {
@FXML
private ComboBox<String> cbTopN;
@FXML
private ComboBox<String> cbStoreFilter;
@FXML
private HBox hbViewToggle;
@@ -138,6 +141,9 @@ public class AnalyticsController {
cbPaymentFilter.setItems(FXCollections.observableArrayList("All"));
cbPaymentFilter.getSelectionModel().selectFirst();
cbStoreFilter.setItems(FXCollections.observableArrayList("All Stores"));
cbStoreFilter.getSelectionModel().selectFirst();
lblFilterSummary.setText("All time");
ToggleGroup tgViewMode = new ToggleGroup();
@@ -151,6 +157,7 @@ public class AnalyticsController {
}
viewMode = (newVal == tbnMyAnalytics) ? "mine" : "store";
updateViewModeStyles();
updateStoreFilterVisibility();
applyCurrentFilter();
});
@@ -213,6 +220,8 @@ public class AnalyticsController {
Platform.runLater(() -> {
cachedSales = sales;
derivePaymentMethods();
deriveStores();
updateStoreFilterVisibility();
applyCurrentFilter();
btnRefresh.setDisable(false);
});
@@ -250,6 +259,12 @@ public class AnalyticsController {
} else {
salesForMode = cachedSales;
}
String storeFilter = currentFilter.storeFilter;
if (!storeFilter.equals("All Stores") && !storeFilter.isBlank()) {
salesForMode = salesForMode.stream()
.filter(s -> storeFilter.equalsIgnoreCase(s.getStoreName() != null ? s.getStoreName() : ""))
.collect(Collectors.toList());
}
List<SaleResponse> filtered = filterSales(salesForMode, currentFilter);
String start = currentFilter.startDate.isEmpty() ? LocalDate.now().minusDays(6).toString() : currentFilter.startDate;
String end = currentFilter.endDate.isEmpty() ? LocalDate.now().toString() : currentFilter.endDate;
@@ -308,6 +323,31 @@ public class AnalyticsController {
}
}
private void deriveStores() {
Set<String> stores = new TreeSet<>();
for (SaleResponse s : cachedSales) {
if (s.getStoreName() != null && !s.getStoreName().isBlank()) {
stores.add(s.getStoreName());
}
}
List<String> items = new ArrayList<>();
items.add("All Stores");
items.addAll(stores);
String current = cbStoreFilter.getValue();
cbStoreFilter.setItems(FXCollections.observableArrayList(items));
if (current != null && items.contains(current)) {
cbStoreFilter.setValue(current);
} else {
cbStoreFilter.getSelectionModel().selectFirst();
}
}
private void updateStoreFilterVisibility() {
boolean show = UserSession.getInstance().isAdmin() && viewMode.equals("store");
cbStoreFilter.setVisible(show);
cbStoreFilter.setManaged(show);
}
private void updateFilterSummary() {
String start = currentFilter.startDate;
String end = currentFilter.endDate;
@@ -665,6 +705,7 @@ public class AnalyticsController {
dpEndDate.setValue(null);
cbPaymentFilter.getSelectionModel().selectFirst();
cbTopN.getSelectionModel().selectFirst();
cbStoreFilter.getSelectionModel().selectFirst();
currentFilter = new FilterState();
applyCurrentFilter();
}
@@ -683,6 +724,8 @@ public class AnalyticsController {
currentFilter.paymentMethod = pm != null ? pm : "All";
int topNPos = cbTopN.getSelectionModel().getSelectedIndex();
currentFilter.topN = (topNPos >= 0 && topNPos < TOP_N_VALUES.length) ? TOP_N_VALUES[topNPos] : 5;
String sf = cbStoreFilter.getValue();
currentFilter.storeFilter = (sf != null && !sf.isBlank()) ? sf : "All Stores";
applyCurrentFilter();
}
@@ -691,5 +734,6 @@ public class AnalyticsController {
String endDate = "";
String paymentMethod = "All";
int topN = 5;
String storeFilter = "All Stores";
}
}

View File

@@ -102,6 +102,7 @@
</FlowPane>
<HBox alignment="CENTER_LEFT" spacing="8.0">
<children>
<ComboBox fx:id="cbStoreFilter" prefWidth="145.0" promptText="All Stores" visible="false" managed="false" />
<ComboBox fx:id="cbPaymentFilter" prefWidth="145.0" promptText="All Payments" />
<ComboBox fx:id="cbTopN" prefWidth="110.0" />
<Region HBox.hgrow="ALWAYS" />