Stationen-Dialoge: Google-Adressvalidierung beim Speichern mit Lade-Dialog und Bestätigungsoption
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -18,13 +18,18 @@ import com.vaadin.flow.component.tabs.Tab;
|
||||
import com.vaadin.flow.component.tabs.TabSheet;
|
||||
import com.vaadin.flow.component.textfield.IntegerField;
|
||||
import com.vaadin.flow.component.textfield.TextField;
|
||||
import com.vaadin.flow.component.UI;
|
||||
import com.vaadin.flow.component.progressbar.ProgressBar;
|
||||
import de.assecutor.votianlt.model.AddressValidationResult;
|
||||
import de.assecutor.votianlt.model.Customer;
|
||||
import de.assecutor.votianlt.model.TaskTemplate;
|
||||
import de.assecutor.votianlt.model.task.*;
|
||||
import de.assecutor.votianlt.pages.service.AddressValidationService;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* Dialog for editing delivery station data. Contains address form fields and a
|
||||
@@ -48,6 +53,15 @@ public class DeliveryStationDialog extends Dialog {
|
||||
private String city;
|
||||
private boolean saveAddress;
|
||||
private List<BaseTask> tasks = new ArrayList<>();
|
||||
private boolean addressValidatedByGoogle;
|
||||
|
||||
public boolean isAddressValidatedByGoogle() {
|
||||
return addressValidatedByGoogle;
|
||||
}
|
||||
|
||||
public void setAddressValidatedByGoogle(boolean addressValidatedByGoogle) {
|
||||
this.addressValidatedByGoogle = addressValidatedByGoogle;
|
||||
}
|
||||
|
||||
public String getCompany() {
|
||||
return company;
|
||||
@@ -176,12 +190,15 @@ public class DeliveryStationDialog extends Dialog {
|
||||
private Span tasksTabError;
|
||||
|
||||
private final DeliveryStationTile.TranslationHelper translationHelper;
|
||||
private final AddressValidationService addressValidationService;
|
||||
|
||||
public DeliveryStationDialog(String dialogTitle, List<Customer> customers,
|
||||
DeliveryStationTile.TranslationHelper translationHelper, SaveListener saveListener,
|
||||
List<TaskTemplate> templates, TemplateSaveCallback templateSaveCallback) {
|
||||
List<TaskTemplate> templates, TemplateSaveCallback templateSaveCallback,
|
||||
AddressValidationService addressValidationService) {
|
||||
|
||||
this.translationHelper = translationHelper;
|
||||
this.addressValidationService = addressValidationService;
|
||||
|
||||
setHeaderTitle(dialogTitle);
|
||||
setCloseOnOutsideClick(false);
|
||||
@@ -278,7 +295,8 @@ public class DeliveryStationDialog extends Dialog {
|
||||
saveAddress.setWidthFull();
|
||||
formLayout.add(saveAddress);
|
||||
|
||||
// Clear error styling on value change for required fields and update tab indicators
|
||||
// Clear error styling on value change for required fields and update tab
|
||||
// indicators
|
||||
firstName.addValueChangeListener(ev -> validateRequiredFields());
|
||||
lastName.addValueChangeListener(ev -> validateRequiredFields());
|
||||
street.addValueChangeListener(ev -> validateRequiredFields());
|
||||
@@ -309,11 +327,65 @@ public class DeliveryStationDialog extends Dialog {
|
||||
Notification.Position.BOTTOM_END);
|
||||
return;
|
||||
}
|
||||
DeliveryData data = collectData();
|
||||
if (saveListener != null) {
|
||||
saveListener.onSave(data);
|
||||
}
|
||||
close();
|
||||
|
||||
// Warte-Dialog anzeigen
|
||||
Dialog loadingDialog = new Dialog();
|
||||
loadingDialog.setCloseOnOutsideClick(false);
|
||||
loadingDialog.setCloseOnEsc(false);
|
||||
loadingDialog.setHeaderTitle(translationHelper.getTranslation("addjob.validation.dialog.title"));
|
||||
VerticalLayout loadingContent = new VerticalLayout();
|
||||
loadingContent.setAlignItems(FlexComponent.Alignment.CENTER);
|
||||
loadingContent.setPadding(true);
|
||||
loadingContent.setSpacing(true);
|
||||
Span loadingText = new Span(translationHelper.getTranslation("addjob.validation.dialog.loading"));
|
||||
ProgressBar progressBar = new ProgressBar();
|
||||
progressBar.setIndeterminate(true);
|
||||
progressBar.setWidthFull();
|
||||
loadingContent.add(loadingText, progressBar);
|
||||
loadingDialog.add(loadingContent);
|
||||
loadingDialog.open();
|
||||
|
||||
// Adresse asynchron bei Google validieren
|
||||
UI ui = UI.getCurrent();
|
||||
String streetVal = street.getValue();
|
||||
String houseNumberVal = houseNumber.getValue();
|
||||
String zipVal = zip.getValue();
|
||||
String cityVal = city.getValue();
|
||||
|
||||
CompletableFuture.supplyAsync(() -> addressValidationService.validateAddress("delivery", streetVal,
|
||||
houseNumberVal, zipVal, cityVal)).thenAccept(validationResult -> ui.access(() -> {
|
||||
loadingDialog.close();
|
||||
DeliveryData data = collectData();
|
||||
|
||||
if (validationResult.isValid()) {
|
||||
data.setAddressValidatedByGoogle(true);
|
||||
if (saveListener != null) {
|
||||
saveListener.onSave(data);
|
||||
}
|
||||
close();
|
||||
} else {
|
||||
// Adresse nicht gefunden: Benutzer fragen
|
||||
ConfirmDialog confirmDialog = new ConfirmDialog();
|
||||
confirmDialog.setHeader(
|
||||
translationHelper.getTranslation("addjob.validation.address.not.found.title"));
|
||||
confirmDialog.setText(
|
||||
translationHelper.getTranslation("addjob.validation.address.not.found.message"));
|
||||
confirmDialog.setConfirmText(
|
||||
translationHelper.getTranslation("addjob.validation.address.save.anyway"));
|
||||
confirmDialog.setConfirmButtonTheme("primary");
|
||||
confirmDialog.setCancelable(true);
|
||||
confirmDialog.setCancelText(
|
||||
translationHelper.getTranslation("addjob.validation.address.correct"));
|
||||
confirmDialog.addConfirmListener(ev -> {
|
||||
data.setAddressValidatedByGoogle(false);
|
||||
if (saveListener != null) {
|
||||
saveListener.onSave(data);
|
||||
}
|
||||
close();
|
||||
});
|
||||
confirmDialog.open();
|
||||
}
|
||||
}));
|
||||
});
|
||||
saveButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
||||
|
||||
|
||||
@@ -19,10 +19,15 @@ import com.vaadin.flow.component.textfield.IntegerField;
|
||||
import com.vaadin.flow.component.textfield.NumberField;
|
||||
import com.vaadin.flow.component.textfield.TextField;
|
||||
import com.vaadin.flow.component.timepicker.TimePicker;
|
||||
import com.vaadin.flow.component.UI;
|
||||
import com.vaadin.flow.component.confirmdialog.ConfirmDialog;
|
||||
import com.vaadin.flow.component.notification.Notification;
|
||||
import com.vaadin.flow.component.progressbar.ProgressBar;
|
||||
import de.assecutor.votianlt.model.AddressValidationResult;
|
||||
import de.assecutor.votianlt.model.AppUser;
|
||||
import de.assecutor.votianlt.model.CargoItem;
|
||||
import de.assecutor.votianlt.model.Customer;
|
||||
import de.assecutor.votianlt.pages.service.AddressValidationService;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalTime;
|
||||
@@ -31,6 +36,7 @@ import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* Dialog for editing pickup station data. Contains address form fields,
|
||||
@@ -59,6 +65,15 @@ public class PickupStationDialog extends Dialog {
|
||||
private boolean digitalProcessing;
|
||||
private AppUser appUser;
|
||||
private List<CargoItem> cargoItems = new ArrayList<>();
|
||||
private boolean addressValidatedByGoogle;
|
||||
|
||||
public boolean isAddressValidatedByGoogle() {
|
||||
return addressValidatedByGoogle;
|
||||
}
|
||||
|
||||
public void setAddressValidatedByGoogle(boolean addressValidatedByGoogle) {
|
||||
this.addressValidatedByGoogle = addressValidatedByGoogle;
|
||||
}
|
||||
|
||||
public String getCompany() {
|
||||
return company;
|
||||
@@ -226,10 +241,13 @@ public class PickupStationDialog extends Dialog {
|
||||
private Span cargoTabError;
|
||||
|
||||
private final DeliveryStationTile.TranslationHelper translationHelper;
|
||||
private final AddressValidationService addressValidationService;
|
||||
|
||||
public PickupStationDialog(String dialogTitle, List<Customer> customers,
|
||||
DeliveryStationTile.TranslationHelper translationHelper, SaveListener saveListener,
|
||||
List<AppUser> availableAppUsers) {
|
||||
List<AppUser> availableAppUsers, AddressValidationService addressValidationService) {
|
||||
|
||||
this.addressValidationService = addressValidationService;
|
||||
|
||||
this.translationHelper = translationHelper;
|
||||
|
||||
@@ -345,7 +363,8 @@ public class PickupStationDialog extends Dialog {
|
||||
saveAddress.setValue(true);
|
||||
saveAddress.setWidthFull();
|
||||
|
||||
// Clear error styling on value change for required fields and update tab indicators
|
||||
// Clear error styling on value change for required fields and update tab
|
||||
// indicators
|
||||
firstName.addValueChangeListener(ev -> validateRequiredFields());
|
||||
lastName.addValueChangeListener(ev -> validateRequiredFields());
|
||||
street.addValueChangeListener(ev -> validateRequiredFields());
|
||||
@@ -434,11 +453,65 @@ public class PickupStationDialog extends Dialog {
|
||||
Notification.Position.BOTTOM_END);
|
||||
return;
|
||||
}
|
||||
PickupData data = collectData();
|
||||
if (saveListener != null) {
|
||||
saveListener.onSave(data);
|
||||
}
|
||||
close();
|
||||
|
||||
// Warte-Dialog anzeigen
|
||||
Dialog loadingDialog = new Dialog();
|
||||
loadingDialog.setCloseOnOutsideClick(false);
|
||||
loadingDialog.setCloseOnEsc(false);
|
||||
loadingDialog.setHeaderTitle(translationHelper.getTranslation("addjob.validation.dialog.title"));
|
||||
VerticalLayout loadingContent = new VerticalLayout();
|
||||
loadingContent.setAlignItems(FlexComponent.Alignment.CENTER);
|
||||
loadingContent.setPadding(true);
|
||||
loadingContent.setSpacing(true);
|
||||
Span loadingText = new Span(translationHelper.getTranslation("addjob.validation.dialog.loading"));
|
||||
ProgressBar progressBar = new ProgressBar();
|
||||
progressBar.setIndeterminate(true);
|
||||
progressBar.setWidthFull();
|
||||
loadingContent.add(loadingText, progressBar);
|
||||
loadingDialog.add(loadingContent);
|
||||
loadingDialog.open();
|
||||
|
||||
// Adresse asynchron bei Google validieren
|
||||
UI ui = UI.getCurrent();
|
||||
String streetVal = street.getValue();
|
||||
String houseNumberVal = houseNumber.getValue();
|
||||
String zipVal = zip.getValue();
|
||||
String cityVal = city.getValue();
|
||||
|
||||
CompletableFuture.supplyAsync(() -> addressValidationService.validateAddress("pickup", streetVal,
|
||||
houseNumberVal, zipVal, cityVal)).thenAccept(validationResult -> ui.access(() -> {
|
||||
loadingDialog.close();
|
||||
PickupData data = collectData();
|
||||
|
||||
if (validationResult.isValid()) {
|
||||
data.setAddressValidatedByGoogle(true);
|
||||
if (saveListener != null) {
|
||||
saveListener.onSave(data);
|
||||
}
|
||||
close();
|
||||
} else {
|
||||
// Adresse nicht gefunden: Benutzer fragen
|
||||
ConfirmDialog confirmDialog = new ConfirmDialog();
|
||||
confirmDialog.setHeader(
|
||||
translationHelper.getTranslation("addjob.validation.address.not.found.title"));
|
||||
confirmDialog.setText(
|
||||
translationHelper.getTranslation("addjob.validation.address.not.found.message"));
|
||||
confirmDialog.setConfirmText(
|
||||
translationHelper.getTranslation("addjob.validation.address.save.anyway"));
|
||||
confirmDialog.setConfirmButtonTheme("primary");
|
||||
confirmDialog.setCancelable(true);
|
||||
confirmDialog.setCancelText(
|
||||
translationHelper.getTranslation("addjob.validation.address.correct"));
|
||||
confirmDialog.addConfirmListener(ev -> {
|
||||
data.setAddressValidatedByGoogle(false);
|
||||
if (saveListener != null) {
|
||||
saveListener.onSave(data);
|
||||
}
|
||||
close();
|
||||
});
|
||||
confirmDialog.open();
|
||||
}
|
||||
}));
|
||||
});
|
||||
saveButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
||||
|
||||
|
||||
@@ -172,4 +172,14 @@ public class StationTile extends VerticalLayout {
|
||||
public void setDeleteListener(DeleteListener listener) {
|
||||
this.deleteListener = listener;
|
||||
}
|
||||
|
||||
public void setAddressValidated(boolean validated) {
|
||||
if (validated) {
|
||||
getStyle().set("background-color", "rgba(76, 175, 80, 0.15)");
|
||||
getStyle().set("border", "1px solid rgba(76, 175, 80, 0.3)");
|
||||
} else {
|
||||
getStyle().set("background-color", "var(--lumo-base-color)");
|
||||
getStyle().set("border", "1px solid var(--lumo-contrast-20pct)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,6 +82,7 @@ public class AddJobView extends Main implements HasDynamicTitle {
|
||||
private final TaskTemplateService taskTemplateService;
|
||||
private final SecurityService securityService;
|
||||
private final ServiceRepository serviceRepository;
|
||||
private final AddressValidationService addressValidationService;
|
||||
// Customer selection
|
||||
private ComboBox<String> customerSelection;
|
||||
|
||||
@@ -167,6 +168,7 @@ public class AddJobView extends Main implements HasDynamicTitle {
|
||||
this.taskTemplateService = taskTemplateService;
|
||||
this.securityService = securityService;
|
||||
this.serviceRepository = serviceRepository;
|
||||
this.addressValidationService = addressValidationService;
|
||||
initializeComponents();
|
||||
setupLayout();
|
||||
setupValidation();
|
||||
@@ -750,11 +752,12 @@ public class AddJobView extends Main implements HasDynamicTitle {
|
||||
// Update tile preview
|
||||
pickupTile.updatePreview(data.getCompany(), data.getFirstName(), data.getLastName(),
|
||||
data.getStreet(), data.getHouseNumber(), data.getZip(), data.getCity());
|
||||
pickupTile.setAddressValidated(data.isAddressValidatedByGoogle());
|
||||
|
||||
resetRouteInformation();
|
||||
triggerValidation();
|
||||
updateTabLabels();
|
||||
}, availableAppUsers);
|
||||
}, availableAppUsers, addressValidationService);
|
||||
|
||||
// Pre-fill dialog with current pickup field values
|
||||
PickupStationDialog.PickupData currentData = new PickupStationDialog.PickupData();
|
||||
@@ -819,6 +822,7 @@ public class AddJobView extends Main implements HasDynamicTitle {
|
||||
// Update tile preview
|
||||
tile.updatePreview(data.getCompany(), data.getFirstName(), data.getLastName(), data.getStreet(),
|
||||
data.getHouseNumber(), data.getZip(), data.getCity());
|
||||
tile.setAddressValidated(data.isAddressValidatedByGoogle());
|
||||
|
||||
resetRouteInformation();
|
||||
triggerValidation();
|
||||
@@ -834,7 +838,7 @@ public class AddJobView extends Main implements HasDynamicTitle {
|
||||
Notification.show(getTranslation("addjob.tasks.template.save.error", ex.getMessage()), 4000,
|
||||
Notification.Position.MIDDLE);
|
||||
}
|
||||
});
|
||||
}, addressValidationService);
|
||||
|
||||
// Pre-fill dialog with current station data
|
||||
DeliveryStation station = deliveryStationsState.get(actualIndex);
|
||||
|
||||
@@ -561,6 +561,10 @@ addjob.validation.dialog.continue.anyway=Trotzdem weiter
|
||||
addjob.validation.pickup.address=Abholadresse
|
||||
addjob.validation.delivery.address=Lieferadresse
|
||||
addjob.validation.route=Route
|
||||
addjob.validation.address.not.found.title=Adresse nicht gefunden
|
||||
addjob.validation.address.not.found.message=Die eingegebene Adresse konnte bei Google nicht eindeutig gefunden werden. Möchten Sie trotzdem speichern?
|
||||
addjob.validation.address.save.anyway=Trotzdem speichern
|
||||
addjob.validation.address.correct=Adresse korrigieren
|
||||
|
||||
# Job Summary
|
||||
jobsummary.title=Zusammenfassung
|
||||
|
||||
@@ -561,6 +561,10 @@ addjob.validation.dialog.continue.anyway=Continue anyway
|
||||
addjob.validation.pickup.address=Pickup Address
|
||||
addjob.validation.delivery.address=Delivery Address
|
||||
addjob.validation.route=Route
|
||||
addjob.validation.address.not.found.title=Address not found
|
||||
addjob.validation.address.not.found.message=The entered address could not be clearly found on Google. Do you still want to save?
|
||||
addjob.validation.address.save.anyway=Save anyway
|
||||
addjob.validation.address.correct=Correct address
|
||||
|
||||
# Job Summary
|
||||
jobsummary.title=Summary
|
||||
|
||||
@@ -555,6 +555,10 @@ addjob.validation.dialog.continue.anyway=Continuer quand même
|
||||
addjob.validation.pickup.address=Adresse d'Enlèvement
|
||||
addjob.validation.delivery.address=Adresse de Livraison
|
||||
addjob.validation.route=Itinéraire
|
||||
addjob.validation.address.not.found.title=Adresse introuvable
|
||||
addjob.validation.address.not.found.message=L'adresse saisie n'a pas pu être trouvée clairement sur Google. Voulez-vous quand même enregistrer?
|
||||
addjob.validation.address.save.anyway=Enregistrer quand même
|
||||
addjob.validation.address.correct=Corriger l'adresse
|
||||
|
||||
# Job Summary
|
||||
jobsummary.title=Résumé
|
||||
|
||||
Reference in New Issue
Block a user