Staff analytics #57

Merged
RecentRunner merged 3 commits from staff-self-analytics into main 2026-03-29 23:55:33 -06:00
4 changed files with 104 additions and 45 deletions
Showing only changes of commit d2a6332633 - Show all commits

View File

@@ -8,6 +8,8 @@ public class DashboardResponse {
private InventorySummary inventorySummary;
private List<TopProduct> topProducts;
private List<DailySales> dailySales;
private List<PaymentMethodData> paymentMethods;
private List<EmployeePerformanceData> employeePerformance;
public DashboardResponse() {
}
@@ -44,11 +46,28 @@ public class DashboardResponse {
this.dailySales = dailySales;
}
public List<PaymentMethodData> getPaymentMethods() {
return paymentMethods;
}
public void setPaymentMethods(List<PaymentMethodData> paymentMethods) {
this.paymentMethods = paymentMethods;
}
public List<EmployeePerformanceData> getEmployeePerformance() {
return employeePerformance;
}
public void setEmployeePerformance(List<EmployeePerformanceData> employeePerformance) {
this.employeePerformance = employeePerformance;
}
public static class SalesSummary {
private BigDecimal totalRevenue;
private Long totalSales;
private BigDecimal totalRefunds;
private Long totalRefundCount;
private Long totalItemsSold;
public SalesSummary() {
}
@@ -84,6 +103,14 @@ public class DashboardResponse {
public void setTotalRefundCount(Long totalRefundCount) {
this.totalRefundCount = totalRefundCount;
}
public Long getTotalItemsSold() {
return totalItemsSold;
}
public void setTotalItemsSold(Long totalItemsSold) {
this.totalItemsSold = totalItemsSold;
}
}
public static class InventorySummary {
@@ -118,4 +145,46 @@ public class DashboardResponse {
this.outOfStockProducts = outOfStockProducts;
}
}
public static class PaymentMethodData {
private String paymentMethod;
private Long count;
public String getPaymentMethod() {
return paymentMethod;
}
public void setPaymentMethod(String paymentMethod) {
this.paymentMethod = paymentMethod;
}
public Long getCount() {
return count;
}
public void setCount(Long count) {
this.count = count;
}
}
public static class EmployeePerformanceData {
private String employeeName;
private BigDecimal revenue;
public String getEmployeeName() {
return employeeName;
}
public void setEmployeeName(String employeeName) {
this.employeeName = employeeName;
}
public BigDecimal getRevenue() {
return revenue;
}
public void setRevenue(BigDecimal revenue) {
this.revenue = revenue;
}
}
}

View File

@@ -92,4 +92,8 @@ public class UserSession {
public boolean isAdmin() {
return Role.ADMIN.equals(role);
}
public boolean isStaff() {
return Role.STAFF.equals(role);
}
}

View File

@@ -9,9 +9,8 @@ import javafx.scene.control.Label;
import org.example.petshopdesktop.api.dto.analytics.DailySales;
import org.example.petshopdesktop.api.dto.analytics.DashboardResponse;
import org.example.petshopdesktop.api.dto.analytics.TopProduct;
import org.example.petshopdesktop.api.dto.sale.SaleResponse;
import org.example.petshopdesktop.api.endpoints.AnalyticsApi;
import org.example.petshopdesktop.api.endpoints.SaleApi;
import org.example.petshopdesktop.auth.UserSession;
import org.example.petshopdesktop.util.ActivityLogger;
import java.math.BigDecimal;
@@ -127,16 +126,17 @@ public class AnalyticsController {
new Thread(() -> {
try {
DashboardResponse dashboard = AnalyticsApi.getInstance().getDashboard(30, 10);
List<SaleResponse> sales = SaleApi.getInstance().listSales(0, Integer.MAX_VALUE, null);
Platform.runLater(() -> {
try {
boolean isAdmin = UserSession.getInstance().isAdmin();
loadSummaryData(dashboard);
loadSalesOverTime(dashboard);
loadTopProductsByRevenue(dashboard);
loadTopProductsByQuantity(dashboard);
loadPaymentMethodDistribution(sales);
loadEmployeePerformance(sales);
loadPaymentMethodDistribution(dashboard);
loadEmployeePerformance(dashboard, isAdmin);
applyRoleVisibility(isAdmin);
} catch (Exception e) {
ActivityLogger.getInstance().logException("AnalyticsController.loadAnalyticsData", e, "Loading analytics data");
lblError.setText("Error loading analytics data. Please try again.");
@@ -157,15 +157,12 @@ public class AnalyticsController {
if (dashboard != null) {
BigDecimal totalRevenue = BigDecimal.ZERO;
Long totalSales = 0L;
Long totalProducts = 0L;
if (dashboard.getSalesSummary() != null) {
totalRevenue = dashboard.getSalesSummary().getTotalRevenue() != null ? dashboard.getSalesSummary().getTotalRevenue() : BigDecimal.ZERO;
totalSales = dashboard.getSalesSummary().getTotalSales() != null ? dashboard.getSalesSummary().getTotalSales() : 0L;
}
if (dashboard.getInventorySummary() != null) {
totalProducts = dashboard.getInventorySummary().getTotalProducts() != null ? dashboard.getInventorySummary().getTotalProducts() : 0L;
lblTotalItems.setText(wholeNumber.format(dashboard.getSalesSummary().getTotalItemsSold() != null ? dashboard.getSalesSummary().getTotalItemsSold() : 0L));
} else {
lblTotalItems.setText(wholeNumber.format(0));
}
lblTotalRevenue.setText(currency.format(totalRevenue));
@@ -176,7 +173,6 @@ public class AnalyticsController {
avgTransaction = totalRevenue.divide(BigDecimal.valueOf(totalSales), 2, RoundingMode.HALF_UP);
}
lblAvgTransaction.setText(currency.format(avgTransaction));
lblTotalItems.setText(wholeNumber.format(totalProducts));
}
}
@@ -243,24 +239,14 @@ public class AnalyticsController {
applyBarChartColor(chartTopQuantity, QUANTITY_COLOR);
}
private void loadPaymentMethodDistribution(List<SaleResponse> sales) throws Exception {
Map<String, Long> paymentMethodCount = sales.stream()
.filter(sale -> sale.getIsRefund() == null || !sale.getIsRefund())
.collect(Collectors.groupingBy(
sale -> sale.getPaymentMethod() != null ? sale.getPaymentMethod() : "Unknown",
Collectors.counting()
));
private void loadPaymentMethodDistribution(DashboardResponse dashboard) throws Exception {
chartPaymentMethods.getData().clear();
List<Map.Entry<String, Long>> paymentEntries = paymentMethodCount.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.toList();
for (Map.Entry<String, Long> entry : paymentEntries) {
List<DashboardResponse.PaymentMethodData> paymentEntries = dashboard.getPaymentMethods() != null ? dashboard.getPaymentMethods() : List.of();
for (DashboardResponse.PaymentMethodData entry : paymentEntries) {
PieChart.Data slice = new PieChart.Data(
entry.getKey() + " (" + entry.getValue() + ")",
entry.getValue()
entry.getPaymentMethod() + " (" + entry.getCount() + ")",
entry.getCount()
);
chartPaymentMethods.getData().add(slice);
}
@@ -269,24 +255,14 @@ public class AnalyticsController {
applyPieChartColors();
}
private void loadEmployeePerformance(List<SaleResponse> sales) throws Exception {
Map<String, Double> employeeRevenue = sales.stream()
.filter(sale -> sale.getIsRefund() == null || !sale.getIsRefund())
.filter(sale -> sale.getEmployeeName() != null)
.collect(Collectors.groupingBy(
SaleResponse::getEmployeeName,
Collectors.summingDouble(sale -> sale.getTotalAmount() != null ? sale.getTotalAmount().doubleValue() : 0.0)
));
private void loadEmployeePerformance(DashboardResponse dashboard, boolean isAdmin) throws Exception {
XYChart.Series<String, Number> series = new XYChart.Series<>();
series.setName("Revenue");
series.setName(isAdmin ? "Revenue" : "My Revenue");
List<Map.Entry<String, Double>> employeeEntries = employeeRevenue.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.toList();
for (Map.Entry<String, Double> entry : employeeEntries) {
series.getData().add(new XYChart.Data<>(entry.getKey(), entry.getValue()));
List<DashboardResponse.EmployeePerformanceData> employeeEntries = dashboard.getEmployeePerformance() != null ? dashboard.getEmployeePerformance() : List.of();
for (DashboardResponse.EmployeePerformanceData entry : employeeEntries) {
BigDecimal revenue = entry.getRevenue() != null ? entry.getRevenue() : BigDecimal.ZERO;
series.getData().add(new XYChart.Data<>(entry.getEmployeeName(), revenue));
}
chartEmployeePerformance.getData().clear();
@@ -294,6 +270,15 @@ public class AnalyticsController {
applyBarChartColor(chartEmployeePerformance, EMPLOYEE_COLOR);
}
private void applyRoleVisibility(boolean isAdmin) {
chartEmployeePerformance.setVisible(isAdmin);
chartEmployeePerformance.setManaged(isAdmin);
if (chartEmployeePerformance.getParent() != null) {
chartEmployeePerformance.getParent().setVisible(isAdmin);
chartEmployeePerformance.getParent().setManaged(isAdmin);
}
}
private void applyLineChartColor(LineChart<Number, Number> chart, String color) {
Platform.runLater(() -> {
for (XYChart.Series<Number, Number> series : chart.getData()) {

View File

@@ -358,6 +358,7 @@ public class MainLayoutController {
lblRole.setText("Leon's Petstore");
boolean isAdmin = session.isAdmin();
boolean canViewAnalytics = isAdmin || session.isStaff();
btnInventory.setVisible(isAdmin);
btnInventory.setManaged(isAdmin);
btnSuppliers.setVisible(isAdmin);
@@ -384,8 +385,8 @@ public class MainLayoutController {
}
if (btnAnalytics != null) {
btnAnalytics.setVisible(isAdmin);
btnAnalytics.setManaged(isAdmin);
btnAnalytics.setVisible(canViewAnalytics);
btnAnalytics.setManaged(canViewAnalytics);
}
btnSalesHistory.setText(isAdmin ? "Sales History" : "Sales");