Skip to content

Commit

Permalink
Create SupplierPerformances view displaying scores
Browse files Browse the repository at this point in the history
  • Loading branch information
TudorOrban committed Jul 21, 2024
1 parent f648df7 commit a7628b3
Show file tree
Hide file tree
Showing 6 changed files with 256 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ public NavigationServiceImpl(FXMLLoaderService fxmlLoaderService,
Map.entry("Update-Supplier", "/org/chainoptim/desktop/features/supplier/UpdateSupplierView.fxml"),
Map.entry("Supplier-Orders", "/org/chainoptim/desktop/features/supplier/SupplierOrdersView.fxml"),
Map.entry("Supplier-Shipments", "/org/chainoptim/desktop/features/supplier/SupplierShipmentsView.fxml"),
Map.entry("Supplier-Performances", "/org/chainoptim/desktop/features/supplier/SupplierPerformancesView.fxml"),

Map.entry("Clients", "/org/chainoptim/desktop/features/client/ClientsView.fxml"),
Map.entry("Client", "/org/chainoptim/desktop/features/client/ClientView.fxml"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ public class SupplierController implements Initializable {
private final SupplierService supplierService;
private final NavigationService navigationService;
private final CurrentSelectionService currentSelectionService;
private final FXMLLoaderService fxmlLoaderService;
private final ControllerFactory controllerFactory;
private final FallbackManager fallbackManager;

Expand All @@ -58,13 +57,11 @@ public class SupplierController implements Initializable {
public SupplierController(SupplierService supplierService,
NavigationService navigationService,
CurrentSelectionService currentSelectionService,
FXMLLoaderService fxmlLoaderService,
ControllerFactory controllerFactory,
FallbackManager fallbackManager) {
this.supplierService = supplierService;
this.navigationService = navigationService;
this.currentSelectionService = currentSelectionService;
this.fxmlLoaderService = fxmlLoaderService;
this.controllerFactory = controllerFactory;
this.fallbackManager = fallbackManager;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
package org.chainoptim.desktop.features.supplier.controller;

import org.chainoptim.desktop.core.context.TenantContext;
import org.chainoptim.desktop.core.main.service.CurrentSelectionService;
import org.chainoptim.desktop.core.main.service.NavigationService;
import org.chainoptim.desktop.core.user.model.User;
import org.chainoptim.desktop.features.supplier.model.Supplier;
import org.chainoptim.desktop.features.supplier.service.SupplierService;
import org.chainoptim.desktop.shared.common.uielements.performance.ScoreDisplay;
import org.chainoptim.desktop.shared.enums.Feature;
import org.chainoptim.desktop.shared.fallback.FallbackManager;
import org.chainoptim.desktop.shared.httphandling.Result;
import org.chainoptim.desktop.shared.search.controller.ListHeaderController;
import org.chainoptim.desktop.shared.search.controller.PageSelectorController;
import org.chainoptim.desktop.shared.search.model.ListHeaderParams;
import org.chainoptim.desktop.shared.search.model.PaginatedResults;
import org.chainoptim.desktop.shared.search.model.SearchParams;
import org.chainoptim.desktop.shared.util.resourceloader.CommonViewsLoader;
import com.google.inject.Inject;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.*;

import java.net.URL;
import java.util.Map;
import java.util.ResourceBundle;

public class SupplierPerformancesController implements Initializable {

// Services
private final SupplierService supplierService;
private final NavigationService navigationService;
private final CurrentSelectionService currentSelectionService;
private final CommonViewsLoader commonViewsLoader;

// State
private final FallbackManager fallbackManager;
private final SearchParams searchParams;
private long totalCount;
private final Map<String, String> sortOptions = Map.of(
"createdAt", "Created At",
"updatedAt", "Updated At"
);

// Controllers
private ListHeaderController headerController;
private PageSelectorController pageSelectorController;

// FXML
@FXML
private ScrollPane suppliersScrollPane;
@FXML
private VBox suppliersVBox;
@FXML
private StackPane headerContainer;
@FXML
private StackPane fallbackContainer;
@FXML
private StackPane pageSelectorContainer;

@Inject
public SupplierPerformancesController(SupplierService supplierService,
NavigationService navigationService,
CurrentSelectionService currentSelectionService,
CommonViewsLoader commonViewsLoader,
FallbackManager fallbackManager,
SearchParams searchParams) {
this.supplierService = supplierService;
this.navigationService = navigationService;
this.currentSelectionService = currentSelectionService;
this.commonViewsLoader = commonViewsLoader;
this.fallbackManager = fallbackManager;
this.searchParams = searchParams;
}

@Override
public void initialize(URL location, ResourceBundle resources) {
headerController = commonViewsLoader.loadListHeader(headerContainer);
headerController.initializeHeader(new ListHeaderParams(null, searchParams, "Suppliers", "/img/truck-arrow-right-solid.png", Feature.SUPPLIER, sortOptions, null, this::loadSuppliers, "Supplier", "Create-Supplier"));
commonViewsLoader.loadFallbackManager(fallbackContainer);
setUpListeners();
loadSuppliers();
pageSelectorController = commonViewsLoader.loadPageSelector(pageSelectorContainer);
}

private void setUpListeners() {
searchParams.getSearchQueryProperty().addListener((observable, oldValue, newValue) -> loadSuppliers());
searchParams.getAscendingProperty().addListener((observable, oldValue, newValue) -> loadSuppliers());
searchParams.getSortOptionProperty().addListener((observable, oldValue, newValue) -> loadSuppliers());
searchParams.getPageProperty().addListener((observable, oldPage, newPage) -> loadSuppliers());

// Listen to empty fallback state
fallbackManager.isEmptyProperty().addListener((observable, oldValue, newValue) -> {
suppliersScrollPane.setVisible(newValue);
suppliersScrollPane.setManaged(newValue);
fallbackContainer.setVisible(!newValue);
fallbackContainer.setManaged(!newValue);
});
}

private void loadSuppliers() {
fallbackManager.reset();
fallbackManager.setLoading(true);

User currentUser = TenantContext.getCurrentUser();
if (currentUser == null) {
Platform.runLater(() -> fallbackManager.setLoading(false));
return;
}
Integer organizationId = currentUser.getOrganization().getId();

supplierService.getSuppliersByOrganizationIdAdvanced(organizationId, searchParams)
.thenApply(this::handleSupplierResponse)
.exceptionally(this::handleSupplierException);
}

private Result<PaginatedResults<Supplier>> handleSupplierResponse(Result<PaginatedResults<Supplier>> result) {
Platform.runLater(() -> {
if (result.getError() != null) {
fallbackManager.setErrorMessage("Failed to load suppliers.");
return;
}
PaginatedResults<Supplier> paginatedResults = result.getData();
fallbackManager.setLoading(false);

totalCount = paginatedResults.getTotalCount();
pageSelectorController.initialize(searchParams, totalCount);
int suppliersLimit = TenantContext.getCurrentUser().getOrganization().getSubscriptionPlan().getMaxSuppliers();
headerController.disableCreateButton(suppliersLimit != -1 && totalCount >= suppliersLimit, "You have reached the limit of suppliers allowed by your current subscription plan.");

suppliersVBox.getChildren().clear();
if (paginatedResults.results.isEmpty()) {
fallbackManager.setNoResults(true);
return;
}

for (Supplier supplier : paginatedResults.results) {
drawSupplierCardUI(supplier);
}
fallbackManager.setNoResults(false);
});
return result;
}

private Result<PaginatedResults<Supplier>> handleSupplierException(Throwable ex) {
Platform.runLater(() -> fallbackManager.setErrorMessage("Failed to load suppliers."));
return new Result<>();
}

private void drawSupplierCardUI(Supplier supplier) {
HBox cardHBox = new HBox();
cardHBox.setAlignment(Pos.CENTER);

// Left side
VBox supplierBox = new VBox();
cardHBox.getChildren().add(supplierBox);

Label supplierName = new Label(supplier.getName());
supplierName.getStyleClass().add("entity-name-label");
supplierBox.getChildren().add(supplierName);
Label supplierLocation = new Label();
if (supplier.getLocation() != null) {
supplierLocation.setText(supplier.getLocation().getFormattedLocation());
} else {
supplierLocation.setText("");
}
supplierLocation.getStyleClass().add("entity-description-label");
supplierBox.getChildren().add(supplierLocation);

// Separator
Region separator = new Region();
HBox.setHgrow(separator, Priority.ALWAYS);
cardHBox.getChildren().add(separator);

// Right side
HBox supplierScoresHBox = new HBox(8);
cardHBox.getChildren().add(supplierScoresHBox);
drawSupplierScores(supplier, supplierScoresHBox);

Button supplierButton = new Button();
supplierButton.getStyleClass().add("entity-card");
supplierButton.setGraphic(cardHBox);
supplierButton.setMaxWidth(Double.MAX_VALUE);
supplierButton.prefWidthProperty().bind(suppliersVBox.widthProperty());
supplierButton.setOnAction(event -> openSupplierDetails(supplier.getId()));

suppliersVBox.getChildren().add(supplierButton);
}

private void drawSupplierScores(Supplier supplier, HBox supplierScoresHBox) {
addScore(supplier.getOverallScore(), supplierScoresHBox, "Overall Score");
addScore(supplier.getTimelinessScore(), supplierScoresHBox, "Timeliness Score");
addScore(supplier.getAvailabilityScore(), supplierScoresHBox, "Availability Score");
addScore(supplier.getQualityScore(), supplierScoresHBox, "Quality Score");
}

private void addScore(Float score, HBox supplierScoresHBox, String tooltipText) {
ScoreDisplay scoreDisplay = new ScoreDisplay();
int scoreValue = score != null ? Math.round(score) : 0;
scoreDisplay.setScore(scoreValue);
scoreDisplay.setTooltipText(tooltipText);
supplierScoresHBox.getChildren().add(scoreDisplay);
}

private void openSupplierDetails(Integer supplierId) {
currentSelectionService.setSelectedId(supplierId);
currentSelectionService.setSelectedPage("Supplier");

navigationService.switchView("Supplier?id=" + supplierId, true, null);
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,11 @@ public class Supplier {
private LocalDateTime updatedAt;
private Integer organizationId;
private Location location;

// Performance
private Float overallScore;
private Float timelinessScore;
private Float quantityPerTimeScore;
private Float availabilityScore;
private Float qualityScore;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.chainoptim.desktop.shared.common.uielements.performance;

import javafx.scene.control.Label;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Arc;
Expand Down Expand Up @@ -46,4 +47,9 @@ public void setScore(int score) {
arcDisplay.getStyleClass().add("good-arc");
}
}

public void setTooltipText(String text) {
Tooltip tooltip = new Tooltip(text);
Tooltip.install(this, tooltip);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.*?>

<?import javafx.scene.control.ScrollPane?>
<VBox xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
styleClass="main-container"
fx:controller="org.chainoptim.desktop.features.supplier.controller.SupplierPerformancesController">

<StackPane fx:id="headerContainer"/>

<StackPane VBox.vgrow="ALWAYS">
<ScrollPane fx:id="suppliersScrollPane" VBox.vgrow="ALWAYS" fitToWidth="true" fitToHeight="true">
<VBox>
<VBox fx:id="suppliersVBox" styleClass="items-list"/>
<StackPane fx:id="pageSelectorContainer"/>
</VBox>
</ScrollPane>

<StackPane fx:id="fallbackContainer" VBox.vgrow="ALWAYS" styleClass="page-fallback-container"/>
</StackPane>

</VBox>

0 comments on commit a7628b3

Please sign in to comment.