Show staff analytics
This commit is contained in:
@@ -8,6 +8,8 @@ public class DashboardResponse {
|
|||||||
private InventorySummary inventorySummary;
|
private InventorySummary inventorySummary;
|
||||||
private List<TopProduct> topProducts;
|
private List<TopProduct> topProducts;
|
||||||
private List<DailySales> dailySales;
|
private List<DailySales> dailySales;
|
||||||
|
private List<PaymentMethodData> paymentMethods;
|
||||||
|
private List<EmployeePerformanceData> employeePerformance;
|
||||||
|
|
||||||
public DashboardResponse() {
|
public DashboardResponse() {
|
||||||
}
|
}
|
||||||
@@ -44,11 +46,28 @@ public class DashboardResponse {
|
|||||||
this.dailySales = dailySales;
|
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 {
|
public static class SalesSummary {
|
||||||
private BigDecimal totalRevenue;
|
private BigDecimal totalRevenue;
|
||||||
private Long totalSales;
|
private Long totalSales;
|
||||||
private BigDecimal totalRefunds;
|
private BigDecimal totalRefunds;
|
||||||
private Long totalRefundCount;
|
private Long totalRefundCount;
|
||||||
|
private Long totalItemsSold;
|
||||||
|
|
||||||
public SalesSummary() {
|
public SalesSummary() {
|
||||||
}
|
}
|
||||||
@@ -84,6 +103,14 @@ public class DashboardResponse {
|
|||||||
public void setTotalRefundCount(Long totalRefundCount) {
|
public void setTotalRefundCount(Long totalRefundCount) {
|
||||||
this.totalRefundCount = totalRefundCount;
|
this.totalRefundCount = totalRefundCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Long getTotalItemsSold() {
|
||||||
|
return totalItemsSold;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTotalItemsSold(Long totalItemsSold) {
|
||||||
|
this.totalItemsSold = totalItemsSold;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class InventorySummary {
|
public static class InventorySummary {
|
||||||
@@ -118,4 +145,46 @@ public class DashboardResponse {
|
|||||||
this.outOfStockProducts = outOfStockProducts;
|
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() {
|
public boolean isAdmin() {
|
||||||
return Role.ADMIN.equals(role);
|
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.DailySales;
|
||||||
import org.example.petshopdesktop.api.dto.analytics.DashboardResponse;
|
import org.example.petshopdesktop.api.dto.analytics.DashboardResponse;
|
||||||
import org.example.petshopdesktop.api.dto.analytics.TopProduct;
|
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.AnalyticsApi;
|
||||||
import org.example.petshopdesktop.api.endpoints.SaleApi;
|
import org.example.petshopdesktop.auth.UserSession;
|
||||||
import org.example.petshopdesktop.util.ActivityLogger;
|
import org.example.petshopdesktop.util.ActivityLogger;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
@@ -127,16 +126,17 @@ public class AnalyticsController {
|
|||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
try {
|
try {
|
||||||
DashboardResponse dashboard = AnalyticsApi.getInstance().getDashboard(30, 10);
|
DashboardResponse dashboard = AnalyticsApi.getInstance().getDashboard(30, 10);
|
||||||
List<SaleResponse> sales = SaleApi.getInstance().listSales(0, Integer.MAX_VALUE, null);
|
|
||||||
|
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
try {
|
try {
|
||||||
|
boolean isAdmin = UserSession.getInstance().isAdmin();
|
||||||
loadSummaryData(dashboard);
|
loadSummaryData(dashboard);
|
||||||
loadSalesOverTime(dashboard);
|
loadSalesOverTime(dashboard);
|
||||||
loadTopProductsByRevenue(dashboard);
|
loadTopProductsByRevenue(dashboard);
|
||||||
loadTopProductsByQuantity(dashboard);
|
loadTopProductsByQuantity(dashboard);
|
||||||
loadPaymentMethodDistribution(sales);
|
loadPaymentMethodDistribution(dashboard);
|
||||||
loadEmployeePerformance(sales);
|
loadEmployeePerformance(dashboard, isAdmin);
|
||||||
|
applyRoleVisibility(isAdmin);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
ActivityLogger.getInstance().logException("AnalyticsController.loadAnalyticsData", e, "Loading analytics data");
|
ActivityLogger.getInstance().logException("AnalyticsController.loadAnalyticsData", e, "Loading analytics data");
|
||||||
lblError.setText("Error loading analytics data. Please try again.");
|
lblError.setText("Error loading analytics data. Please try again.");
|
||||||
@@ -157,15 +157,12 @@ public class AnalyticsController {
|
|||||||
if (dashboard != null) {
|
if (dashboard != null) {
|
||||||
BigDecimal totalRevenue = BigDecimal.ZERO;
|
BigDecimal totalRevenue = BigDecimal.ZERO;
|
||||||
Long totalSales = 0L;
|
Long totalSales = 0L;
|
||||||
Long totalProducts = 0L;
|
|
||||||
|
|
||||||
if (dashboard.getSalesSummary() != null) {
|
if (dashboard.getSalesSummary() != null) {
|
||||||
totalRevenue = dashboard.getSalesSummary().getTotalRevenue() != null ? dashboard.getSalesSummary().getTotalRevenue() : BigDecimal.ZERO;
|
totalRevenue = dashboard.getSalesSummary().getTotalRevenue() != null ? dashboard.getSalesSummary().getTotalRevenue() : BigDecimal.ZERO;
|
||||||
totalSales = dashboard.getSalesSummary().getTotalSales() != null ? dashboard.getSalesSummary().getTotalSales() : 0L;
|
totalSales = dashboard.getSalesSummary().getTotalSales() != null ? dashboard.getSalesSummary().getTotalSales() : 0L;
|
||||||
}
|
lblTotalItems.setText(wholeNumber.format(dashboard.getSalesSummary().getTotalItemsSold() != null ? dashboard.getSalesSummary().getTotalItemsSold() : 0L));
|
||||||
|
} else {
|
||||||
if (dashboard.getInventorySummary() != null) {
|
lblTotalItems.setText(wholeNumber.format(0));
|
||||||
totalProducts = dashboard.getInventorySummary().getTotalProducts() != null ? dashboard.getInventorySummary().getTotalProducts() : 0L;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lblTotalRevenue.setText(currency.format(totalRevenue));
|
lblTotalRevenue.setText(currency.format(totalRevenue));
|
||||||
@@ -176,7 +173,6 @@ public class AnalyticsController {
|
|||||||
avgTransaction = totalRevenue.divide(BigDecimal.valueOf(totalSales), 2, RoundingMode.HALF_UP);
|
avgTransaction = totalRevenue.divide(BigDecimal.valueOf(totalSales), 2, RoundingMode.HALF_UP);
|
||||||
}
|
}
|
||||||
lblAvgTransaction.setText(currency.format(avgTransaction));
|
lblAvgTransaction.setText(currency.format(avgTransaction));
|
||||||
lblTotalItems.setText(wholeNumber.format(totalProducts));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,24 +239,14 @@ public class AnalyticsController {
|
|||||||
applyBarChartColor(chartTopQuantity, QUANTITY_COLOR);
|
applyBarChartColor(chartTopQuantity, QUANTITY_COLOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadPaymentMethodDistribution(List<SaleResponse> sales) throws Exception {
|
private void loadPaymentMethodDistribution(DashboardResponse dashboard) 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()
|
|
||||||
));
|
|
||||||
|
|
||||||
chartPaymentMethods.getData().clear();
|
chartPaymentMethods.getData().clear();
|
||||||
|
|
||||||
List<Map.Entry<String, Long>> paymentEntries = paymentMethodCount.entrySet().stream()
|
List<DashboardResponse.PaymentMethodData> paymentEntries = dashboard.getPaymentMethods() != null ? dashboard.getPaymentMethods() : List.of();
|
||||||
.sorted(Map.Entry.comparingByKey())
|
for (DashboardResponse.PaymentMethodData entry : paymentEntries) {
|
||||||
.toList();
|
|
||||||
|
|
||||||
for (Map.Entry<String, Long> entry : paymentEntries) {
|
|
||||||
PieChart.Data slice = new PieChart.Data(
|
PieChart.Data slice = new PieChart.Data(
|
||||||
entry.getKey() + " (" + entry.getValue() + ")",
|
entry.getPaymentMethod() + " (" + entry.getCount() + ")",
|
||||||
entry.getValue()
|
entry.getCount()
|
||||||
);
|
);
|
||||||
chartPaymentMethods.getData().add(slice);
|
chartPaymentMethods.getData().add(slice);
|
||||||
}
|
}
|
||||||
@@ -269,24 +255,14 @@ public class AnalyticsController {
|
|||||||
applyPieChartColors();
|
applyPieChartColors();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadEmployeePerformance(List<SaleResponse> sales) throws Exception {
|
private void loadEmployeePerformance(DashboardResponse dashboard, boolean isAdmin) 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)
|
|
||||||
));
|
|
||||||
|
|
||||||
XYChart.Series<String, Number> series = new XYChart.Series<>();
|
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()
|
List<DashboardResponse.EmployeePerformanceData> employeeEntries = dashboard.getEmployeePerformance() != null ? dashboard.getEmployeePerformance() : List.of();
|
||||||
.sorted(Map.Entry.comparingByKey())
|
for (DashboardResponse.EmployeePerformanceData entry : employeeEntries) {
|
||||||
.toList();
|
BigDecimal revenue = entry.getRevenue() != null ? entry.getRevenue() : BigDecimal.ZERO;
|
||||||
|
series.getData().add(new XYChart.Data<>(entry.getEmployeeName(), revenue));
|
||||||
for (Map.Entry<String, Double> entry : employeeEntries) {
|
|
||||||
series.getData().add(new XYChart.Data<>(entry.getKey(), entry.getValue()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
chartEmployeePerformance.getData().clear();
|
chartEmployeePerformance.getData().clear();
|
||||||
@@ -294,6 +270,15 @@ public class AnalyticsController {
|
|||||||
applyBarChartColor(chartEmployeePerformance, EMPLOYEE_COLOR);
|
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) {
|
private void applyLineChartColor(LineChart<Number, Number> chart, String color) {
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
for (XYChart.Series<Number, Number> series : chart.getData()) {
|
for (XYChart.Series<Number, Number> series : chart.getData()) {
|
||||||
|
|||||||
@@ -358,6 +358,7 @@ public class MainLayoutController {
|
|||||||
lblRole.setText("Leon's Petstore");
|
lblRole.setText("Leon's Petstore");
|
||||||
|
|
||||||
boolean isAdmin = session.isAdmin();
|
boolean isAdmin = session.isAdmin();
|
||||||
|
boolean canViewAnalytics = isAdmin || session.isStaff();
|
||||||
btnInventory.setVisible(isAdmin);
|
btnInventory.setVisible(isAdmin);
|
||||||
btnInventory.setManaged(isAdmin);
|
btnInventory.setManaged(isAdmin);
|
||||||
btnSuppliers.setVisible(isAdmin);
|
btnSuppliers.setVisible(isAdmin);
|
||||||
@@ -384,8 +385,8 @@ public class MainLayoutController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (btnAnalytics != null) {
|
if (btnAnalytics != null) {
|
||||||
btnAnalytics.setVisible(isAdmin);
|
btnAnalytics.setVisible(canViewAnalytics);
|
||||||
btnAnalytics.setManaged(isAdmin);
|
btnAnalytics.setManaged(canViewAnalytics);
|
||||||
}
|
}
|
||||||
|
|
||||||
btnSalesHistory.setText(isAdmin ? "Sales History" : "Sales");
|
btnSalesHistory.setText(isAdmin ? "Sales History" : "Sales");
|
||||||
|
|||||||
Reference in New Issue
Block a user