diff --git a/src/main/java/de/assecutor/votianlt/pages/base/ui/component/DeliveryStationTile.java b/src/main/java/de/assecutor/votianlt/pages/base/ui/component/DeliveryStationTile.java index 69744fd..5dd1deb 100644 --- a/src/main/java/de/assecutor/votianlt/pages/base/ui/component/DeliveryStationTile.java +++ b/src/main/java/de/assecutor/votianlt/pages/base/ui/component/DeliveryStationTile.java @@ -1,10 +1,12 @@ package de.assecutor.votianlt.pages.base.ui.component; +import com.vaadin.flow.component.Component; import com.vaadin.flow.component.button.Button; import com.vaadin.flow.component.button.ButtonVariant; import com.vaadin.flow.component.checkbox.Checkbox; import com.vaadin.flow.component.combobox.ComboBox; import com.vaadin.flow.component.html.H3; +import com.vaadin.flow.component.html.Span; import com.vaadin.flow.component.icon.Icon; import com.vaadin.flow.component.icon.VaadinIcon; import com.vaadin.flow.component.orderedlayout.FlexComponent; @@ -31,6 +33,10 @@ public class DeliveryStationTile extends VerticalLayout { void onDelete(DeliveryStationTile tile); } + public interface CollapseListener { + void onCollapseChanged(boolean collapsed); + } + private final int stationNumber; private final ComboBox company; @@ -48,6 +54,12 @@ public class DeliveryStationTile extends VerticalLayout { private ChangeListener changeListener; private DeleteListener deleteListener; + private CollapseListener collapseListener; + + private boolean collapsed = false; + private Button collapseButton; + private VerticalLayout collapsedContent; + private List expandedOnlyComponents; public DeliveryStationTile(int stationNumber, boolean removable, List customers, TranslationHelper translationHelper) { @@ -62,15 +74,14 @@ public class DeliveryStationTile extends VerticalLayout { getStyle().set("border-radius", "var(--lumo-border-radius-m)"); getStyle().set("background-color", "var(--lumo-base-color)"); - // Header + // Header with title, collapse button and delete button on one line title = new H3(translationHelper.getTranslation("addjob.station.delivery", stationNumber)); - title.getStyle().set("margin", "0"); + title.getStyle().set("margin", "0").set("flex-grow", "1"); - HorizontalLayout titleLayout = new HorizontalLayout(); - titleLayout.setWidthFull(); - titleLayout.setJustifyContentMode(FlexComponent.JustifyContentMode.BETWEEN); - titleLayout.setDefaultVerticalComponentAlignment(FlexComponent.Alignment.CENTER); - titleLayout.add(title); + collapseButton = new Button(new Icon(VaadinIcon.ANGLE_DOUBLE_LEFT)); + collapseButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY_INLINE); + collapseButton.getStyle().set("cursor", "pointer"); + collapseButton.addClickListener(e -> toggleCollapse()); Button deleteButton = new Button(new Icon(VaadinIcon.CLOSE_SMALL)); deleteButton.addThemeVariants(ButtonVariant.LUMO_ERROR, ButtonVariant.LUMO_TERTIARY); @@ -83,7 +94,11 @@ public class DeliveryStationTile extends VerticalLayout { } else { deleteButton.getStyle().set("visibility", "hidden"); } - titleLayout.add(deleteButton); + + HorizontalLayout titleLayout = new HorizontalLayout(); + titleLayout.setWidthFull(); + titleLayout.setDefaultVerticalComponentAlignment(FlexComponent.Alignment.CENTER); + titleLayout.add(title, collapseButton, deleteButton); add(titleLayout); @@ -172,6 +187,19 @@ public class DeliveryStationTile extends VerticalLayout { // Register change listeners on all fields setupChangeListeners(); + + // Store references to expanded-mode components (excluding titleLayout which stays visible) + expandedOnlyComponents = getChildren().filter(c -> c != titleLayout).toList(); + + getStyle().set("transition", "width 0.3s ease, min-width 0.3s ease"); + + // Collapsed content (initially hidden) + collapsedContent = new VerticalLayout(); + collapsedContent.setPadding(false); + collapsedContent.setSpacing(false); + collapsedContent.getStyle().set("gap", "var(--lumo-space-xs)"); + collapsedContent.setVisible(false); + add(collapsedContent); } private void setupChangeListeners() { @@ -372,6 +400,14 @@ public class DeliveryStationTile extends VerticalLayout { this.deleteListener = listener; } + public void setCollapseListener(CollapseListener listener) { + this.collapseListener = listener; + } + + public boolean isCollapsed() { + return collapsed; + } + /** * Returns whether the user wants to save this address as a customer. */ @@ -403,6 +439,51 @@ public class DeliveryStationTile extends VerticalLayout { } } + private void toggleCollapse() { + collapsed = !collapsed; + if (collapsed) { + updateCollapsedContent(); + expandedOnlyComponents.forEach(c -> c.setVisible(false)); + collapsedContent.setVisible(true); + setWidth("25%"); + getStyle().set("min-width", "150px"); + collapseButton.setIcon(new Icon(VaadinIcon.ANGLE_DOUBLE_RIGHT)); + } else { + expandedOnlyComponents.forEach(c -> c.setVisible(true)); + collapsedContent.setVisible(false); + setWidth("40%"); + getStyle().set("min-width", "300px"); + collapseButton.setIcon(new Icon(VaadinIcon.ANGLE_DOUBLE_LEFT)); + } + if (collapseListener != null) { + collapseListener.onCollapseChanged(collapsed); + } + } + + private void updateCollapsedContent() { + collapsedContent.removeAll(); + + addCollapsedLine(company.getValue()); + + String name = (getValueOrEmpty(firstName) + " " + getValueOrEmpty(lastName)).trim(); + addCollapsedLine(name); + + String streetLine = (getValueOrEmpty(street) + " " + getValueOrEmpty(houseNumber)).trim(); + addCollapsedLine(streetLine); + + String zipCityLine = (getValueOrEmpty(zip) + " " + getValueOrEmpty(city)).trim(); + addCollapsedLine(zipCityLine); + } + + private void addCollapsedLine(String text) { + if (text != null && !text.trim().isEmpty()) { + Span span = new Span(text); + span.getStyle().set("font-size", "var(--lumo-font-size-xs)").set("word-break", "break-word") + .set("color", "var(--lumo-secondary-text-color)"); + collapsedContent.add(span); + } + } + /** * Functional interface for accessing translations from the parent view. */ diff --git a/src/main/java/de/assecutor/votianlt/pages/view/AddJobView.java b/src/main/java/de/assecutor/votianlt/pages/view/AddJobView.java index 6ebfa59..a5f27a9 100644 --- a/src/main/java/de/assecutor/votianlt/pages/view/AddJobView.java +++ b/src/main/java/de/assecutor/votianlt/pages/view/AddJobView.java @@ -162,6 +162,10 @@ public class AddJobView extends Main implements HasDynamicTitle { private ComboBox templateComboBox; private TextArea remarkArea; private VerticalLayout pickupSection; + private boolean pickupCollapsed = false; + private Button pickupCollapseButton; + private VerticalLayout pickupCollapsedContent; + private List pickupExpandedOnlyComponents; private final Binder binder = new Binder<>(Job.class); @@ -531,6 +535,7 @@ public class AddJobView extends Main implements HasDynamicTitle { updateTabLabels(); }); tile.setDeleteListener(this::removeDeliveryStationTile); + tile.setCollapseListener(collapsed -> updateAddStationButtonSize()); deliveryStationTiles.add(tile); @@ -571,6 +576,7 @@ public class AddJobView extends Main implements HasDynamicTitle { resetRouteInformation(); triggerValidation(); updateTabLabels(); + updateAddStationButtonSize(); }); dialog.open(); } @@ -1013,19 +1019,22 @@ public class AddJobView extends Main implements HasDynamicTitle { section.getStyle().set("background-color", "var(--lumo-base-color)"); H3 title = new H3(getTranslation("addjob.section.pickup")); - title.getStyle().set("margin", "0"); + title.getStyle().set("margin", "0").set("flex-grow", "1"); - HorizontalLayout titleLayout = new HorizontalLayout(); - titleLayout.setWidthFull(); - titleLayout.setJustifyContentMode(FlexComponent.JustifyContentMode.BETWEEN); - titleLayout.setDefaultVerticalComponentAlignment(FlexComponent.Alignment.CENTER); - titleLayout.add(title); + pickupCollapseButton = new Button(new Icon(VaadinIcon.ANGLE_DOUBLE_LEFT)); + pickupCollapseButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY_INLINE); + pickupCollapseButton.getStyle().set("cursor", "pointer"); + pickupCollapseButton.addClickListener(e -> togglePickupCollapse()); // Invisible placeholder button to match DeliveryStationTile header height Button placeholder = new Button(new Icon(VaadinIcon.CLOSE_SMALL)); placeholder.addThemeVariants(ButtonVariant.LUMO_TERTIARY); placeholder.getStyle().set("visibility", "hidden"); - titleLayout.add(placeholder); + + HorizontalLayout titleLayout = new HorizontalLayout(); + titleLayout.setWidthFull(); + titleLayout.setDefaultVerticalComponentAlignment(FlexComponent.Alignment.CENTER); + titleLayout.add(title, pickupCollapseButton, placeholder); // Alle einzelnen Controls auf volle Breite setzen pickupCompany.setWidthFull(); @@ -1064,9 +1073,81 @@ public class AddJobView extends Main implements HasDynamicTitle { section.add(savePickupAddress); + // Store references to expanded-mode components (excluding titleLayout which stays visible) + pickupExpandedOnlyComponents = section.getChildren().filter(c -> c != titleLayout).toList(); + + section.getStyle().set("transition", "width 0.3s ease, min-width 0.3s ease"); + + // Collapsed content (initially hidden) + pickupCollapsedContent = new VerticalLayout(); + pickupCollapsedContent.setPadding(false); + pickupCollapsedContent.setSpacing(false); + pickupCollapsedContent.getStyle().set("gap", "var(--lumo-space-xs)"); + pickupCollapsedContent.setVisible(false); + section.add(pickupCollapsedContent); + return section; } + private void togglePickupCollapse() { + pickupCollapsed = !pickupCollapsed; + if (pickupCollapsed) { + updatePickupCollapsedContent(); + pickupExpandedOnlyComponents.forEach(c -> c.setVisible(false)); + pickupCollapsedContent.setVisible(true); + pickupSection.setWidth("25%"); + pickupSection.getStyle().set("min-width", "150px"); + pickupCollapseButton.setIcon(new Icon(VaadinIcon.ANGLE_DOUBLE_RIGHT)); + } else { + pickupExpandedOnlyComponents.forEach(c -> c.setVisible(true)); + pickupCollapsedContent.setVisible(false); + pickupSection.setWidth("40%"); + pickupSection.getStyle().set("min-width", "300px"); + pickupCollapseButton.setIcon(new Icon(VaadinIcon.ANGLE_DOUBLE_LEFT)); + } + updateAddStationButtonSize(); + } + + private void updateAddStationButtonSize() { + boolean allCollapsed = pickupCollapsed + && deliveryStationTiles.stream().allMatch(DeliveryStationTile::isCollapsed); + if (allCollapsed) { + addStationButton.getStyle().set("width", "25%"); + addStationButton.getStyle().set("min-width", "150px"); + } else { + addStationButton.getStyle().set("width", "40%"); + addStationButton.getStyle().set("min-width", "300px"); + } + } + + private void updatePickupCollapsedContent() { + pickupCollapsedContent.removeAll(); + + addPickupCollapsedLine(pickupCompany.getValue()); + + String name = (safeValue(pickupFirstName) + " " + safeValue(pickupLastName)).trim(); + addPickupCollapsedLine(name); + + String streetLine = (safeValue(pickupStreet) + " " + safeValue(pickupHouseNumber)).trim(); + addPickupCollapsedLine(streetLine); + + String zipCityLine = (safeValue(pickupZip) + " " + safeValue(pickupCity)).trim(); + addPickupCollapsedLine(zipCityLine); + } + + private void addPickupCollapsedLine(String text) { + if (text != null && !text.trim().isEmpty()) { + Span span = new Span(text); + span.getStyle().set("font-size", "var(--lumo-font-size-xs)").set("word-break", "break-word") + .set("color", "var(--lumo-secondary-text-color)"); + pickupCollapsedContent.add(span); + } + } + + private String safeValue(TextField field) { + return field.getValue() != null ? field.getValue().trim() : ""; + } + // createDeliverySection() removed - delivery stations are now handled by // DeliveryStationTile