From 1a866d6083fada7c0da3da4f736bafa9f8c4bc46 Mon Sep 17 00:00:00 2001 From: Sven Carstensen Date: Fri, 6 Feb 2026 20:06:22 +0100 Subject: [PATCH] Erweiterungen --- .../java/de/assecutor/votianlt/model/Job.java | 7 ++++ .../votianlt/pages/view/AddJobView.java | 41 ++++++++++++++++--- .../votianlt/pages/view/JobSummaryView.java | 24 +++++++++-- .../votianlt/pages/view/ShowJobsView.java | 5 ++- .../votianlt/util/DateTimeFormatUtil.java | 15 +++++++ 5 files changed, 82 insertions(+), 10 deletions(-) diff --git a/src/main/java/de/assecutor/votianlt/model/Job.java b/src/main/java/de/assecutor/votianlt/model/Job.java index 1ea2e8e..f9da10e 100644 --- a/src/main/java/de/assecutor/votianlt/model/Job.java +++ b/src/main/java/de/assecutor/votianlt/model/Job.java @@ -10,6 +10,7 @@ import org.springframework.data.mongodb.core.mapping.Field; import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.LocalTime; import java.math.BigDecimal; @Data @@ -115,9 +116,15 @@ public class Job { @Field("pickup_date") private LocalDate pickupDate; + @Field("pickup_time") + private LocalTime pickupTime; + @Field("delivery_date") private LocalDate deliveryDate; + @Field("delivery_time") + private LocalTime deliveryTime; + // Bemerkung @Field("remark") private String remark; 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 1bfe7eb..1545437 100644 --- a/src/main/java/de/assecutor/votianlt/pages/view/AddJobView.java +++ b/src/main/java/de/assecutor/votianlt/pages/view/AddJobView.java @@ -112,6 +112,10 @@ public class AddJobView extends Main { private DatePicker pickupDate; private DatePicker deliveryDate; + // Time picker fields for appointments + private TimePicker pickupTime; + private TimePicker deliveryTime; + private com.vaadin.flow.component.tabs.Tab addressesTab; private com.vaadin.flow.component.tabs.Tab appointmentsTab; private com.vaadin.flow.component.tabs.Tab cargoTab; @@ -510,7 +514,7 @@ public class AddJobView extends Main { // Appointment (Pickup) H3 pickupApptTitle = new H3("Termin (Abholung)"); pickupApptTitle.getStyle().set("margin", "0"); - TimePicker pickupTime = new TimePicker("Uhrzeit"); + pickupTime = new TimePicker("Uhrzeit"); HorizontalLayout pickupApptRow = new HorizontalLayout(pickupDate, pickupTime); pickupApptRow.setWidthFull(); pickupApptRow.setSpacing(true); @@ -521,7 +525,7 @@ public class AddJobView extends Main { // Appointment (Delivery) H3 deliveryApptTitle = new H3("Termin (Lieferung)"); deliveryApptTitle.getStyle().set("margin", "0"); - TimePicker deliveryTime = new TimePicker("Uhrzeit"); + deliveryTime = new TimePicker("Uhrzeit"); HorizontalLayout deliveryApptRow = new HorizontalLayout(deliveryDate, deliveryTime); deliveryApptRow.setWidthFull(); deliveryApptRow.setSpacing(true); @@ -843,6 +847,10 @@ public class AddJobView extends Main { binder.forField(deliveryDate).asRequired("").bind(Job::getDeliveryDate, Job::setDeliveryDate); + // Bind time picker fields (optional) + binder.bind(pickupTime, Job::getPickupTime, Job::setPickupTime); + binder.bind(deliveryTime, Job::getDeliveryTime, Job::setDeliveryTime); + // Bind customerSelection field with validation binder.forField(customerSelection).asRequired("").bind(Job::getCustomerSelection, Job::setCustomerSelection); @@ -1082,12 +1090,16 @@ public class AddJobView extends Main { private boolean hasTasksValidationErrors() { for (BaseTask task : tasksState) { - // Check if any ConfirmationTask has an empty description (required field) - if (task instanceof ConfirmationTask) { + // Check if any ConfirmationTask has an empty description or buttonText (required fields) + if (task instanceof ConfirmationTask confirmationTask) { String description = task.getDescription(); if (description == null || description.trim().isEmpty()) { return true; } + String buttonText = confirmationTask.getButtonText(); + if (buttonText == null || buttonText.trim().isEmpty()) { + return true; + } } // Check if any TodoListTask has at least one non-empty todo item if (task instanceof TodoListTask todoListTask) { @@ -1120,7 +1132,9 @@ public class AddJobView extends Main { // Zusätzliche Felder, die nicht über den Binder gebunden sind, manuell setzen job.setPickupDate(pickupDate.getValue()); + job.setPickupTime(pickupTime.getValue()); job.setDeliveryDate(deliveryDate.getValue()); + job.setDeliveryTime(deliveryTime.getValue()); if (remarkArea != null) job.setRemark(remarkArea.getValue()); @@ -1815,10 +1829,11 @@ public class AddJobView extends Main { descriptionField.getStyle().set("--vaadin-input-field-border-color", "rgba(255, 0, 0, 0.3)"); } - // Button text field + // Button text field (required) TextField buttonTextField = new TextField("Button-Text"); buttonTextField.setPlaceholder("z.B. 'Bestätigen', 'Abgeschlossen'"); buttonTextField.setWidthFull(); + buttonTextField.setRequiredIndicatorVisible(true); ConfirmationTask confirmationTask = (ConfirmationTask) task; buttonTextField.setValue(confirmationTask.getButtonText() != null ? confirmationTask.getButtonText() : ""); buttonTextField.addValueChangeListener(ev -> { @@ -1828,7 +1843,23 @@ public class AddJobView extends Main { ((ConfirmationTask) stateTask).setButtonText(ev.getValue()); } } + // Update field styling based on value + boolean isEmpty = ev.getValue() == null || ev.getValue().trim().isEmpty(); + if (isEmpty) { + buttonTextField.getStyle().set("--vaadin-input-field-background", "rgba(255, 0, 0, 0.1)"); + buttonTextField.getStyle().set("--vaadin-input-field-border-color", "rgba(255, 0, 0, 0.3)"); + } else { + buttonTextField.getStyle().remove("--vaadin-input-field-background"); + buttonTextField.getStyle().remove("--vaadin-input-field-border-color"); + } + triggerValidation(); + updateTabLabels(); }); + // Initial styling for empty field + if (confirmationTask.getButtonText() == null || confirmationTask.getButtonText().trim().isEmpty()) { + buttonTextField.getStyle().set("--vaadin-input-field-background", "rgba(255, 0, 0, 0.1)"); + buttonTextField.getStyle().set("--vaadin-input-field-border-color", "rgba(255, 0, 0, 0.3)"); + } configContainer.add(descriptionField, buttonTextField); break; diff --git a/src/main/java/de/assecutor/votianlt/pages/view/JobSummaryView.java b/src/main/java/de/assecutor/votianlt/pages/view/JobSummaryView.java index 42870c6..229acac 100644 --- a/src/main/java/de/assecutor/votianlt/pages/view/JobSummaryView.java +++ b/src/main/java/de/assecutor/votianlt/pages/view/JobSummaryView.java @@ -180,7 +180,7 @@ public class JobSummaryView extends Main implements HasUrlParameter { topRow.setSpacing(true); VerticalLayout pickupBox = borderedBox(); - pickupBox.add(new H3("Abholung " + (job.getPickupDate() != null ? formatLocalDate(job.getPickupDate()) : ""))); + pickupBox.add(new H3("Abholung " + formatDateWithTime(job.getPickupDate(), job.getPickupTime()))); pickupBox.add(new Span(valueOrEmpty(job.getPickupCompany()))); pickupBox.add(new Span(valueOrEmpty(job.getPickupSalutation()) + (job.getPickupSalutation() != null ? " " : "") + valueOrEmpty(job.getPickupFirstName()) + (job.getPickupFirstName() != null ? " " : "") @@ -189,8 +189,7 @@ public class JobSummaryView extends Main implements HasUrlParameter { pickupBox.add(new Span(concatZipCity(job.getPickupZip(), job.getPickupCity()))); VerticalLayout deliveryBox = borderedBox(); - deliveryBox.add( - new H3("Lieferung " + (job.getDeliveryDate() != null ? formatLocalDate(job.getDeliveryDate()) : ""))); + deliveryBox.add(new H3("Lieferung " + formatDateWithTime(job.getDeliveryDate(), job.getDeliveryTime()))); deliveryBox.add(new Span(valueOrEmpty(job.getDeliveryCompany()))); deliveryBox.add(new Span(valueOrEmpty(job.getDeliverySalutation()) + (job.getDeliverySalutation() != null ? " " : "") + valueOrEmpty(job.getDeliveryFirstName()) @@ -340,6 +339,25 @@ public class JobSummaryView extends Main implements HasUrlParameter { } } + private String formatLocalTime(java.time.LocalTime time) { + try { + return DateTimeFormatUtil.formatTime(time); + } catch (Exception e) { + return ""; + } + } + + private String formatDateWithTime(java.time.LocalDate date, java.time.LocalTime time) { + StringBuilder sb = new StringBuilder(); + if (date != null) { + sb.append(formatLocalDate(date)); + if (time != null) { + sb.append(", ").append(formatLocalTime(time)); + } + } + return sb.toString(); + } + private String valueOrEmpty(String v) { return v == null ? "" : v; } diff --git a/src/main/java/de/assecutor/votianlt/pages/view/ShowJobsView.java b/src/main/java/de/assecutor/votianlt/pages/view/ShowJobsView.java index 9bc7241..5061975 100644 --- a/src/main/java/de/assecutor/votianlt/pages/view/ShowJobsView.java +++ b/src/main/java/de/assecutor/votianlt/pages/view/ShowJobsView.java @@ -21,6 +21,7 @@ import com.vaadin.flow.router.Route; import de.assecutor.votianlt.model.Job; import de.assecutor.votianlt.model.JobStatus; import de.assecutor.votianlt.mqtt.MqttPublisher; +import de.assecutor.votianlt.util.DateTimeFormatUtil; import de.assecutor.votianlt.repository.JobRepository; import de.assecutor.votianlt.security.SecurityService; import de.assecutor.votianlt.service.ClientConnectionService; @@ -106,7 +107,7 @@ public class ShowJobsView extends VerticalLayout { grid.addColumn(job -> extractCompanyName(job.getCustomerSelection())).setHeader("Auftraggeber") .setAutoWidth(true).setFlexGrow(1).setSortable(true); grid.addColumn(Job::getJobNumber).setHeader("Auftragsnummer").setAutoWidth(true).setSortable(true); - grid.addColumn(Job::getCreatedAt).setHeader("Auftragsdatum").setAutoWidth(true).setSortable(true); + grid.addColumn(job -> DateTimeFormatUtil.formatDateTime(job.getCreatedAt())).setHeader("Auftragsdatum").setAutoWidth(true).setSortable(true); grid.addColumn(Job::getDeliveryCity).setHeader("Zielort").setAutoWidth(true).setFlexGrow(1).setSortable(true); // Action column: manual completion for jobs without digital processing @@ -299,7 +300,7 @@ public class ShowJobsView extends VerticalLayout { for (Job job : jobs) { csv.append(escapeCsv(extractCompanyName(job.getCustomerSelection()))).append(","); csv.append(escapeCsv(job.getJobNumber())).append(","); - csv.append(job.getCreatedAt() != null ? job.getCreatedAt().toString() : "").append(","); + csv.append(DateTimeFormatUtil.formatDateTime(job.getCreatedAt())).append(","); csv.append(escapeCsv(job.getDeliveryCity())).append("\n"); } diff --git a/src/main/java/de/assecutor/votianlt/util/DateTimeFormatUtil.java b/src/main/java/de/assecutor/votianlt/util/DateTimeFormatUtil.java index 0b42e02..6fd1d1e 100644 --- a/src/main/java/de/assecutor/votianlt/util/DateTimeFormatUtil.java +++ b/src/main/java/de/assecutor/votianlt/util/DateTimeFormatUtil.java @@ -2,6 +2,7 @@ package de.assecutor.votianlt.util; import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.LocalTime; import java.time.format.DateTimeFormatter; /** @@ -60,6 +61,20 @@ public class DateTimeFormatUtil { return dateTime.format(TIME_FORMATTER); } + /** + * Formats a LocalTime to German format: "HH:MM Uhr" + * + * @param time + * the LocalTime to format + * @return formatted time string or empty string if time is null + */ + public static String formatTime(LocalTime time) { + if (time == null) { + return ""; + } + return time.format(TIME_FORMATTER); + } + /** * Returns the date time formatter for direct use *