From cbde45629e5cfe0a857b6c4ad24e1331304d6ad6 Mon Sep 17 00:00:00 2001 From: Sven Carstensen Date: Wed, 13 Aug 2025 16:08:54 +0200 Subject: [PATCH] Erweiterungen --- .../java/de/assecutor/votianlt/model/Job.java | 4 + .../pages/add_job/ui/view/AddJobView.java | 145 ++++++++++++++++-- 2 files changed, 134 insertions(+), 15 deletions(-) diff --git a/src/main/java/de/assecutor/votianlt/model/Job.java b/src/main/java/de/assecutor/votianlt/model/Job.java index 06fd1a3..9ff48e9 100644 --- a/src/main/java/de/assecutor/votianlt/model/Job.java +++ b/src/main/java/de/assecutor/votianlt/model/Job.java @@ -111,4 +111,8 @@ public class Job { @Field("app_user") private String appUser; + + // Preis (netto) + @Field("price") + private String price; } diff --git a/src/main/java/de/assecutor/votianlt/pages/add_job/ui/view/AddJobView.java b/src/main/java/de/assecutor/votianlt/pages/add_job/ui/view/AddJobView.java index 3aeda18..37efc89 100644 --- a/src/main/java/de/assecutor/votianlt/pages/add_job/ui/view/AddJobView.java +++ b/src/main/java/de/assecutor/votianlt/pages/add_job/ui/view/AddJobView.java @@ -86,6 +86,20 @@ public class AddJobView extends Main { private Checkbox digitalProcessing; private ComboBox appUser; + // Price field + private TextField price; + + // Date picker fields for appointments + private DatePicker pickupDate; + private DatePicker deliveryDate; + + // TabSheet and Tab references for dynamic label updates + private TabSheet tabSheet; + private com.vaadin.flow.component.tabs.Tab addressesTab; + private com.vaadin.flow.component.tabs.Tab appointmentsTab; + private com.vaadin.flow.component.tabs.Tab cargoTab; + private com.vaadin.flow.component.tabs.Tab priceTab; + // Submit button private Button submitButton; @@ -182,6 +196,17 @@ public class AddJobView extends Main { appUser.setItems("App-Nutzer"); appUser.setPlaceholder("App-Nutzer auswählen..."); + // Price field + price = new TextField("Preis"); + price.setPlaceholder("z.B. 150.00"); + price.setRequiredIndicatorVisible(true); + + // Date picker fields for appointments + pickupDate = new DatePicker("Datum"); + pickupDate.setRequiredIndicatorVisible(true); + deliveryDate = new DatePicker("Datum"); + deliveryDate.setRequiredIndicatorVisible(true); + // Submit button submitButton = new Button("Auftrag anlegen", event -> submit()); submitButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY); @@ -196,20 +221,20 @@ public class AddJobView extends Main { add(new ViewToolbar("Neuen Auftrag anlegen")); // Create TabSheet for organizing the form - TabSheet tabSheet = new TabSheet(); + tabSheet = new TabSheet(); tabSheet.setSizeFull(); // Tab 1: Customer & Addresses - tabSheet.add("Auftraggeber & Adressen", createCustomerAndAddressesTab()); + addressesTab = tabSheet.add("Auftraggeber & Adressen", createCustomerAndAddressesTab()); // Tab 2: Appointments & Processing - tabSheet.add("Termine & Verarbeitung", createAppointmentsAndProcessingTab()); + appointmentsTab = tabSheet.add("Termine & Verarbeitung", createAppointmentsAndProcessingTab()); // Tab 3: Cargo & Tasks - tabSheet.add("Ladung & Aufgaben", createCargoAndTasksTab()); + cargoTab = tabSheet.add("Ladung & Aufgaben", createCargoAndTasksTab()); // Tab 4: Price & Submit - tabSheet.add("Preis & Abschluss", createPriceAndSubmitTab()); + priceTab = tabSheet.add("Preis & Abschluss", createPriceAndSubmitTab()); add(tabSheet); @@ -294,8 +319,6 @@ public class AddJobView extends Main { // Appointment (Pickup) H3 pickupApptTitle = new H3("Termin (Abholung)"); pickupApptTitle.getStyle().set("margin", "0"); - DatePicker pickupDate = new DatePicker("Datum"); - pickupDate.setRequiredIndicatorVisible(true); TimePicker pickupTime = new TimePicker("Uhrzeit"); HorizontalLayout pickupApptRow = new HorizontalLayout(pickupDate, pickupTime); pickupApptRow.setWidthFull(); @@ -307,8 +330,6 @@ public class AddJobView extends Main { // Appointment (Delivery) H3 deliveryApptTitle = new H3("Termin (Lieferung)"); deliveryApptTitle.getStyle().set("margin", "0"); - DatePicker deliveryDate = new DatePicker("Datum"); - deliveryDate.setRequiredIndicatorVisible(true); TimePicker deliveryTime = new TimePicker("Uhrzeit"); HorizontalLayout deliveryApptRow = new HorizontalLayout(deliveryDate, deliveryTime); deliveryApptRow.setWidthFull(); @@ -353,8 +374,6 @@ public class AddJobView extends Main { // Preis (netto) - moved from createTasksAndNotesSection H3 priceTitle = new H3("Preis (netto)"); priceTitle.getStyle().set("margin", "0"); - TextField price = new TextField("Preis"); - price.setRequiredIndicatorVisible(true); content.add(priceTitle, price); tabContent.add(content); @@ -527,6 +546,16 @@ public class AddJobView extends Main { .asRequired("") .bind(Job::getDeliveryCity, Job::setDeliveryCity); + // Bind price field with validation + binder.forField(price) + .asRequired("") + .bind(Job::getPrice, Job::setPrice); + + // Bind date picker fields with validation (we'll need to add these to Job model later) + // For now, we'll just set up the validation without binding to the model + binder.forField(pickupDate).asRequired(""); + binder.forField(deliveryDate).asRequired(""); + // Bind optional fields without validation binder.bind(customerSelection, Job::getCustomerSelection, Job::setCustomerSelection); binder.bind(pickupCompany, Job::getPickupCompany, Job::setPickupCompany); @@ -549,20 +578,30 @@ public class AddJobView extends Main { // Trigger initial validation when view is displayed triggerValidation(); + + // Update tab labels with initial validation state + updateTabLabels(); } private void setupValidationTriggers() { // List of all required fields - TextField[] requiredFields = { + TextField[] requiredTextFields = { pickupFirstName, pickupLastName, pickupStreet, pickupHouseNumber, pickupZip, pickupCity, - deliveryFirstName, deliveryLastName, deliveryStreet, deliveryHouseNumber, deliveryZip, deliveryCity + deliveryFirstName, deliveryLastName, deliveryStreet, deliveryHouseNumber, deliveryZip, deliveryCity, + price }; - // Add value change listeners to trigger validation on every change - for (TextField field : requiredFields) { + // List of required date fields + DatePicker[] requiredDateFields = { + pickupDate, deliveryDate + }; + + // Add value change listeners to trigger validation on every change for text fields + for (TextField field : requiredTextFields) { field.addValueChangeListener(event -> { triggerValidation(); updateFieldStyling(field); + updateTabLabels(); }); // Add focus listeners for immediate visual feedback @@ -572,6 +611,22 @@ public class AddJobView extends Main { // Set initial styling updateFieldStyling(field); } + + // Add value change listeners for date fields + for (DatePicker field : requiredDateFields) { + field.addValueChangeListener(event -> { + triggerValidation(); + updateDateFieldStyling(field); + updateTabLabels(); + }); + + // Add focus listeners for immediate visual feedback + field.addFocusListener(event -> updateDateFieldStyling(field)); + field.addBlurListener(event -> updateDateFieldStyling(field)); + + // Set initial styling + updateDateFieldStyling(field); + } } private void triggerValidation() { @@ -595,6 +650,63 @@ public class AddJobView extends Main { } } + private void updateDateFieldStyling(DatePicker field) { + boolean isEmpty = field.getValue() == null; + + if (isEmpty) { + // Apply transparent red background only to the input field, not the label + field.getStyle().set("--vaadin-input-field-background", "rgba(255, 0, 0, 0.1)"); + field.getStyle().set("--vaadin-input-field-border-color", "rgba(255, 0, 0, 0.3)"); + } else { + // Remove styling when field is filled + field.getStyle().remove("--vaadin-input-field-background"); + field.getStyle().remove("--vaadin-input-field-border-color"); + } + } + + private void updateTabLabels() { + // Check validation state for each tab and update labels with exclamation marks + updateTabLabel(addressesTab, "Auftraggeber & Adressen", hasAddressValidationErrors()); + updateTabLabel(appointmentsTab, "Termine & Verarbeitung", hasAppointmentValidationErrors()); + updateTabLabel(cargoTab, "Ladung & Aufgaben", false); // No required fields in cargo tab + updateTabLabel(priceTab, "Preis & Abschluss", hasPriceValidationErrors()); + } + + private void updateTabLabel(com.vaadin.flow.component.tabs.Tab tab, String baseLabel, boolean hasErrors) { + if (hasErrors) { + tab.setLabel(baseLabel + " ⚠️"); + } else { + tab.setLabel(baseLabel); + } + } + + private boolean hasAddressValidationErrors() { + // Check pickup address fields + boolean pickupErrors = isFieldEmpty(pickupFirstName) || isFieldEmpty(pickupLastName) || + isFieldEmpty(pickupStreet) || isFieldEmpty(pickupHouseNumber) || + isFieldEmpty(pickupZip) || isFieldEmpty(pickupCity); + + // Check delivery address fields + boolean deliveryErrors = isFieldEmpty(deliveryFirstName) || isFieldEmpty(deliveryLastName) || + isFieldEmpty(deliveryStreet) || isFieldEmpty(deliveryHouseNumber) || + isFieldEmpty(deliveryZip) || isFieldEmpty(deliveryCity); + + return pickupErrors || deliveryErrors; + } + + private boolean hasAppointmentValidationErrors() { + return pickupDate.getValue() == null || deliveryDate.getValue() == null; + } + + private boolean hasPriceValidationErrors() { + return isFieldEmpty(price); + } + + private boolean isFieldEmpty(TextField field) { + String value = field.getValue(); + return value == null || value.trim().isEmpty(); + } + private void submit() { try { Job job = new Job(); @@ -979,6 +1091,9 @@ public class AddJobView extends Main { digitalProcessing.setValue(false); appUser.clear(); + // Price field + price.clear(); + // Benutzer-Feedback Notification.show("Alle Felder wurden geleert", 2000, Notification.Position.BOTTOM_CENTER); }