Staff analytics #57
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,4 +92,8 @@ public class UserSession {
|
||||
public boolean isAdmin() {
|
||||
return Role.ADMIN.equals(role);
|
||||
}
|
||||
|
||||
public boolean isStaff() {
|
||||
return Role.STAFF.equals(role);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user