diff --git a/src/main/java/de/assecutor/votianlt/model/User.java b/src/main/java/de/assecutor/votianlt/model/User.java index 2c05ac3..79c9be1 100644 --- a/src/main/java/de/assecutor/votianlt/model/User.java +++ b/src/main/java/de/assecutor/votianlt/model/User.java @@ -56,4 +56,8 @@ public class User { private LocalDateTime createdAt; private LocalDateTime updatedAt; private Set roles; + + // Digitale Abwicklung und App-Nutzer Ortung + private boolean digitalProcessingEnabled = true; // Digitale Abwicklung per App + private boolean locationTrackingEnabled = true; // App-Nutzer orten } \ No newline at end of file diff --git a/src/main/java/de/assecutor/votianlt/pages/view/AddAppDeviceView.java b/src/main/java/de/assecutor/votianlt/pages/view/AddAppDeviceView.java index 6fa667e..996df64 100644 --- a/src/main/java/de/assecutor/votianlt/pages/view/AddAppDeviceView.java +++ b/src/main/java/de/assecutor/votianlt/pages/view/AddAppDeviceView.java @@ -38,8 +38,10 @@ public class AddAppDeviceView extends VerticalLayout { // Formularfelder erstellen nameField = new TextField("Gerätename"); nameField.setRequired(true); + nameField.setRequiredIndicatorVisible(true); nameField.setPlaceholder("z.B. iPhone 15, Samsung Galaxy S24"); nameField.setWidth("100%"); + nameField.addBlurListener(e -> validateNameField()); // Layout konfigurieren setSizeFull(); @@ -103,10 +105,18 @@ public class AddAppDeviceView extends VerticalLayout { } private void populateTestData() { - nameField.setValue("iPhone 15 Pro"); + // No default values - field starts empty } private void createAppDevice() { + // Validate field first + validateNameField(); + + if (nameField.isInvalid()) { + Notification.show("Bitte füllen Sie das Pflichtfeld korrekt aus", 3000, Notification.Position.MIDDLE); + return; + } + if (binder.validate().isOk()) { try { AppDevice appDevice = new AppDevice(); @@ -135,4 +145,15 @@ public class AddAppDeviceView extends VerticalLayout { private void navigateBack() { getUI().ifPresent(ui -> ui.navigate("app-devices")); } + + private void validateNameField() { + String value = nameField.getValue(); + if (value == null || value.trim().isEmpty()) { + nameField.setInvalid(true); + nameField.setErrorMessage("Gerätename ist ein Pflichtfeld"); + } else { + nameField.setInvalid(false); + nameField.setErrorMessage(""); + } + } } diff --git a/src/main/java/de/assecutor/votianlt/pages/view/AddAppUserView.java b/src/main/java/de/assecutor/votianlt/pages/view/AddAppUserView.java index e925f4d..45dcd93 100644 --- a/src/main/java/de/assecutor/votianlt/pages/view/AddAppUserView.java +++ b/src/main/java/de/assecutor/votianlt/pages/view/AddAppUserView.java @@ -89,9 +89,16 @@ public class AddAppUserView extends VerticalLayout { // Configure fields designationField.setPlaceholder("(HH H 000)"); designationField.setWidthFull(); + designationField.setRequiredIndicatorVisible(true); + designationField.addBlurListener(e -> validateField(designationField, "Kennung ist ein Pflichtfeld")); firstnameField.setWidthFull(); + firstnameField.setRequiredIndicatorVisible(true); + firstnameField.addBlurListener(e -> validateField(firstnameField, "Vorname ist ein Pflichtfeld")); + lastnameField.setWidthFull(); + lastnameField.setRequiredIndicatorVisible(true); + lastnameField.addBlurListener(e -> validateField(lastnameField, "Nachname ist ein Pflichtfeld")); // Create horizontal layout for firstname and lastname HorizontalLayout nameLayout = new HorizontalLayout(); @@ -100,20 +107,41 @@ public class AddAppUserView extends VerticalLayout { nameLayout.add(firstnameField, lastnameField); phoneField.setWidthFull(); + phoneField.setRequiredIndicatorVisible(true); + phoneField.addBlurListener(e -> validateField(phoneField, "Telefonnummer ist ein Pflichtfeld")); + appCodeField.setWidthFull(); + appCodeField.setRequiredIndicatorVisible(true); + appCodeField.addBlurListener(e -> validateField(appCodeField, "App-Code ist ein Pflichtfeld")); + emailField.setWidthFull(); + emailField.setRequiredIndicatorVisible(true); + emailField.addBlurListener(e -> validateEmailField()); + // Clear error message when user types a new email + emailField.addValueChangeListener(e -> { + emailField.setInvalid(false); + emailField.setErrorMessage(""); + }); + passwordField.setWidthFull(); passwordField.setRequired(true); + passwordField.setRequiredIndicatorVisible(true); + passwordField.addBlurListener(e -> validatePasswordField()); + confirmPasswordField.setWidthFull(); confirmPasswordField.setRequired(true); + confirmPasswordField.setRequiredIndicatorVisible(true); + confirmPasswordField.addBlurListener(e -> validateConfirmPasswordField()); // Configure device dropdown // Geräteauswahl vorbereiten deviceComboBox.setWidthFull(); deviceComboBox.setItemLabelGenerator(device -> device.getName()); deviceComboBox.setRequired(true); + deviceComboBox.setRequiredIndicatorVisible(true); deviceComboBox.setPlaceholder("Bitte wählen..."); deviceComboBox.setItems(appDeviceService.findUnassignedDevices()); + deviceComboBox.addBlurListener(e -> validateDeviceField()); // Add fields to form formLayout.add(designationField); @@ -170,6 +198,12 @@ public class AddAppUserView extends VerticalLayout { } private void createAppUser() { + // Validate all fields first + if (!validateAllFields()) { + Notification.show("Bitte füllen Sie alle Pflichtfelder korrekt aus", 3000, Notification.Position.MIDDLE); + return; + } + try { // Create new AppUser object AppUser newAppUser = new AppUser(); @@ -193,38 +227,106 @@ public class AddAppUserView extends VerticalLayout { } catch (ValidationException e) { Notification.show("Bitte überprüfen Sie die Eingaben", 3000, Notification.Position.MIDDLE); + } catch (org.springframework.dao.DuplicateKeyException e) { + // Handle duplicate email error + if (e.getMessage().contains("email")) { + Notification.show("Diese E-Mail-Adresse ist bereits vergeben", 5000, Notification.Position.MIDDLE); + emailField.focus(); + emailField.setInvalid(true); + emailField.setErrorMessage("E-Mail-Adresse bereits vorhanden"); + } else { + Notification.show("Ein Fehler ist aufgetreten: Doppelter Wert gefunden", 5000, Notification.Position.MIDDLE); + } + } catch (Exception e) { + Notification.show("Fehler beim Anlegen des App-Nutzers: " + e.getMessage(), 5000, Notification.Position.MIDDLE); } } private void populateTestData() { - // Fill designation field - designationField.setValue("HH H 001"); - - // Fill name fields - firstnameField.setValue("Max"); - lastnameField.setValue("Mustermann"); - - // Fill phone field - phoneField.setValue("+49 123 456789"); - - // Fill app code field - appCodeField.setValue("APP001"); - - // Fill email field - emailField.setValue("max.mustermann@example.com"); - - // Fill password field - passwordField.setValue("testpassword123"); - - // Fill confirm password field - confirmPasswordField.setValue("testpassword123"); - - // Set device to iPhone - // deviceComboBox.setValue("iPhone"); // This line is removed as deviceComboBox - // is now a ComboBox + // No default values - all fields start empty } private void navigateBack() { getUI().ifPresent(ui -> ui.navigate("app-user")); } + + private void validateField(TextField field, String errorMessage) { + String value = field.getValue(); + if (value == null || value.trim().isEmpty()) { + field.setInvalid(true); + field.setErrorMessage(errorMessage); + } else { + field.setInvalid(false); + field.setErrorMessage(""); + } + } + + private void validateEmailField() { + String value = emailField.getValue(); + if (value == null || value.trim().isEmpty()) { + emailField.setInvalid(true); + emailField.setErrorMessage("E-Mail-Adresse ist ein Pflichtfeld"); + } else if (!value.contains("@") || !value.contains(".")) { + emailField.setInvalid(true); + emailField.setErrorMessage("Bitte geben Sie eine gültige E-Mail-Adresse ein"); + } else { + emailField.setInvalid(false); + emailField.setErrorMessage(""); + } + } + + private void validatePasswordField() { + String value = passwordField.getValue(); + if (value == null || value.trim().isEmpty()) { + passwordField.setInvalid(true); + passwordField.setErrorMessage("Passwort ist ein Pflichtfeld"); + } else if (value.length() < 6) { + passwordField.setInvalid(true); + passwordField.setErrorMessage("Passwort muss mindestens 6 Zeichen lang sein"); + } else { + passwordField.setInvalid(false); + passwordField.setErrorMessage(""); + } + } + + private void validateConfirmPasswordField() { + String password = passwordField.getValue(); + String confirmPassword = confirmPasswordField.getValue(); + if (confirmPassword == null || confirmPassword.trim().isEmpty()) { + confirmPasswordField.setInvalid(true); + confirmPasswordField.setErrorMessage("Bitte bestätigen Sie das Passwort"); + } else if (!confirmPassword.equals(password)) { + confirmPasswordField.setInvalid(true); + confirmPasswordField.setErrorMessage("Passwörter stimmen nicht überein"); + } else { + confirmPasswordField.setInvalid(false); + confirmPasswordField.setErrorMessage(""); + } + } + + private void validateDeviceField() { + if (deviceComboBox.getValue() == null) { + deviceComboBox.setInvalid(true); + deviceComboBox.setErrorMessage("Bitte wählen Sie ein Gerät aus"); + } else { + deviceComboBox.setInvalid(false); + deviceComboBox.setErrorMessage(""); + } + } + + private boolean validateAllFields() { + validateField(designationField, "Kennung ist ein Pflichtfeld"); + validateField(firstnameField, "Vorname ist ein Pflichtfeld"); + validateField(lastnameField, "Nachname ist ein Pflichtfeld"); + validateField(phoneField, "Telefonnummer ist ein Pflichtfeld"); + validateField(appCodeField, "App-Code ist ein Pflichtfeld"); + validateEmailField(); + validatePasswordField(); + validateConfirmPasswordField(); + validateDeviceField(); + + return !designationField.isInvalid() && !firstnameField.isInvalid() && !lastnameField.isInvalid() + && !phoneField.isInvalid() && !appCodeField.isInvalid() && !emailField.isInvalid() + && !passwordField.isInvalid() && !confirmPasswordField.isInvalid() && !deviceComboBox.isInvalid(); + } } diff --git a/src/main/java/de/assecutor/votianlt/pages/view/AddCustomerView.java b/src/main/java/de/assecutor/votianlt/pages/view/AddCustomerView.java index 0eb53db..f0ff864 100644 --- a/src/main/java/de/assecutor/votianlt/pages/view/AddCustomerView.java +++ b/src/main/java/de/assecutor/votianlt/pages/view/AddCustomerView.java @@ -51,6 +51,7 @@ public class AddCustomerView extends Main { companyName = new TextField("Firma"); companyName.setRequiredIndicatorVisible(true); companyName.setWidthFull(); + companyName.addBlurListener(e -> validateField(companyName)); // Anrede (Dropdown) title = new ComboBox<>("Anrede"); @@ -62,16 +63,19 @@ public class AddCustomerView extends Main { firstName = new TextField("Vorname"); firstName.setRequiredIndicatorVisible(true); firstName.setWidthFull(); + firstName.addBlurListener(e -> validateField(firstName)); // Nachname (Pflichtfeld) lastName = new TextField("Nachname"); lastName.setRequiredIndicatorVisible(true); lastName.setWidthFull(); + lastName.addBlurListener(e -> validateField(lastName)); // Telefonnummer (Pflichtfeld) telephone = new TextField("Telefonnummer"); telephone.setRequiredIndicatorVisible(true); telephone.setWidthFull(); + telephone.addBlurListener(e -> validateField(telephone)); // Fax (optional) fax = new TextField("Fax"); @@ -81,14 +85,17 @@ public class AddCustomerView extends Main { mail = new TextField("E-Mail-Adresse"); mail.setRequiredIndicatorVisible(true); mail.setWidthFull(); + mail.addBlurListener(e -> validateEmail()); // Straße (Pflichtfeld) street = new TextField("Straße"); street.setRequiredIndicatorVisible(true); + street.addBlurListener(e -> validateField(street)); // Hausnummer (Pflichtfeld) houseNumber = new TextField("Hausnr."); houseNumber.setRequiredIndicatorVisible(true); + houseNumber.addBlurListener(e -> validateField(houseNumber)); // Adresszusatz (optional) addressAddition = new TextField("Adresszusatz"); @@ -97,10 +104,12 @@ public class AddCustomerView extends Main { // PLZ (Pflichtfeld) zip = new TextField("Postleitzahl"); zip.setRequiredIndicatorVisible(true); + zip.addBlurListener(e -> validateField(zip)); // Ort (Pflichtfeld) city = new TextField("Ort"); city.setRequiredIndicatorVisible(true); + city.addBlurListener(e -> validateField(city)); // Binder-Konfiguration configureBinder(); @@ -185,21 +194,19 @@ public class AddCustomerView extends Main { } private void setTestData() { - companyName.setValue("Mustermann Transport GmbH"); - title.setValue("Herr"); - firstName.setValue("Max"); - lastName.setValue("Mustermann"); - telephone.setValue("+49 40 123456789"); - fax.setValue("+49 40 123456790"); - mail.setValue("max.mustermann@mustermann-transport.de"); - street.setValue("Musterstraße"); - houseNumber.setValue("123"); - addressAddition.setValue("2. OG"); - zip.setValue("20095"); - city.setValue("Hamburg"); + // No default values - all fields start empty } private void submit() { + // First validate all fields + boolean isValid = validateAllFields(); + + if (!isValid) { + com.vaadin.flow.component.notification.Notification.show("Bitte füllen Sie alle Pflichtfelder aus", 3000, + com.vaadin.flow.component.notification.Notification.Position.TOP_CENTER); + return; + } + try { Customer customer = new Customer(); binder.writeBean(customer); @@ -220,4 +227,45 @@ public class AddCustomerView extends Main { com.vaadin.flow.component.notification.Notification.Position.TOP_CENTER); } } + + private void validateField(TextField field) { + String value = field.getValue(); + if (value == null || value.trim().isEmpty()) { + field.setInvalid(true); + field.setErrorMessage("Dieses Feld ist ein Pflichtfeld"); + } else { + field.setInvalid(false); + field.setErrorMessage(""); + } + } + + private void validateEmail() { + String value = mail.getValue(); + if (value == null || value.trim().isEmpty()) { + mail.setInvalid(true); + mail.setErrorMessage("E-Mail-Adresse ist ein Pflichtfeld"); + } else if (!value.contains("@")) { + mail.setInvalid(true); + mail.setErrorMessage("Bitte geben Sie eine gültige E-Mail-Adresse ein"); + } else { + mail.setInvalid(false); + mail.setErrorMessage(""); + } + } + + private boolean validateAllFields() { + validateField(companyName); + validateField(firstName); + validateField(lastName); + validateField(telephone); + validateField(street); + validateField(houseNumber); + validateField(zip); + validateField(city); + validateEmail(); + + return !companyName.isInvalid() && !firstName.isInvalid() && !lastName.isInvalid() + && !telephone.isInvalid() && !mail.isInvalid() && !street.isInvalid() + && !houseNumber.isInvalid() && !zip.isInvalid() && !city.isInvalid(); + } } 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 2dc3686..cf0a712 100644 --- a/src/main/java/de/assecutor/votianlt/pages/view/AddJobView.java +++ b/src/main/java/de/assecutor/votianlt/pages/view/AddJobView.java @@ -48,6 +48,7 @@ import de.assecutor.votianlt.pages.service.AppUserService; import de.assecutor.votianlt.model.AppUser; import de.assecutor.votianlt.pages.service.TaskTemplateService; import de.assecutor.votianlt.model.TaskTemplate; +import de.assecutor.votianlt.model.User; import de.assecutor.votianlt.security.SecurityService; import jakarta.annotation.security.RolesAllowed; @@ -331,9 +332,15 @@ public class AddJobView extends Main { saveDeliveryAddress = new Checkbox("Die Adresse für zukünftige Aufträge speichern."); saveDeliveryAddress.setValue(true); - // Digital processing + // Digital processing - set value based on user's profile setting digitalProcessing = new Checkbox("Digitale Abwicklung per App"); - digitalProcessing.setValue(true); + // Get current user's digital processing preference from profile + try { + User currentUser = securityService.getCurrentDatabaseUser(); + digitalProcessing.setValue(currentUser.isDigitalProcessingEnabled()); + } catch (Exception e) { + digitalProcessing.setValue(true); // Default to true if user not found + } appUser = new ComboBox<>("App-Nutzer"); // Load app users for current user and set up the ComboBox @@ -1224,25 +1231,31 @@ public class AddJobView extends Main { desc.setAllowCustomValue(true); desc.setPlaceholder("Wählen Sie eine Option oder geben Sie eigenen Text ein..."); desc.setWidth("40%"); + desc.setRequiredIndicatorVisible(true); IntegerField qty = new IntegerField("Anzahl"); qty.setMin(1); qty.setValue(1); qty.setWidth("10%"); + qty.setRequiredIndicatorVisible(true); NumberField weight = new NumberField("Gewicht"); weight.setSuffixComponent(new Span("kg")); weight.setWidth("15%"); + weight.setRequiredIndicatorVisible(true); NumberField len = new NumberField("Länge"); len.setSuffixComponent(new Span("mm")); len.setWidth("12%"); + len.setRequiredIndicatorVisible(true); NumberField wid = new NumberField("Breite"); wid.setSuffixComponent(new Span("mm")); wid.setWidth("12%"); + wid.setRequiredIndicatorVisible(true); NumberField hei = new NumberField("Höhe"); hei.setSuffixComponent(new Span("mm")); hei.setWidth("12%"); + hei.setRequiredIndicatorVisible(true); Button remove = new Button(new Icon(VaadinIcon.CLOSE_SMALL)); remove.addThemeVariants(ButtonVariant.LUMO_ERROR, ButtonVariant.LUMO_TERTIARY); @@ -1270,31 +1283,59 @@ public class AddJobView extends Main { item.setWidthMm(wid.getValue()); item.setHeightMm(hei.getValue()); + // Validation helper to update field styling + java.util.function.Consumer> validateField = field -> { + if (field instanceof ComboBox) { + ComboBox combo = (ComboBox) field; + String value = combo.getValue() != null ? combo.getValue().toString() : ""; + combo.setInvalid(value.trim().isEmpty()); + } else if (field instanceof NumberField) { + NumberField numField = (NumberField) field; + numField.setInvalid(numField.getValue() == null || numField.getValue() <= 0); + } else if (field instanceof IntegerField) { + IntegerField intField = (IntegerField) field; + intField.setInvalid(intField.getValue() == null || intField.getValue() <= 0); + } + }; + desc.addValueChangeListener(ev -> { item.setDescription(ev.getValue()); + validateField.accept(desc); updateTabLabels(); // Update tab validation when cargo description changes }); qty.addValueChangeListener(ev -> { item.setQuantity(ev.getValue()); + validateField.accept(qty); updateTabLabels(); // Update tab validation when cargo quantity changes }); weight.addValueChangeListener(ev -> { item.setWeightKg(ev.getValue()); + validateField.accept(weight); updateTabLabels(); // Update tab validation when cargo weight changes }); len.addValueChangeListener(ev -> { item.setLengthMm(ev.getValue()); + validateField.accept(len); updateTabLabels(); // Update tab validation when cargo length changes }); wid.addValueChangeListener(ev -> { item.setWidthMm(ev.getValue()); + validateField.accept(wid); updateTabLabels(); // Update tab validation when cargo width changes }); hei.addValueChangeListener(ev -> { item.setHeightMm(ev.getValue()); + validateField.accept(hei); updateTabLabels(); // Update tab validation when cargo height changes }); + // Initial validation + validateField.accept(desc); + validateField.accept(weight); + validateField.accept(len); + validateField.accept(wid); + validateField.accept(hei); + if (afterCreate != null) afterCreate.accept(row); }; diff --git a/src/main/java/de/assecutor/votianlt/pages/view/EditAppDeviceView.java b/src/main/java/de/assecutor/votianlt/pages/view/EditAppDeviceView.java index 9d96a1e..9fd144f 100644 --- a/src/main/java/de/assecutor/votianlt/pages/view/EditAppDeviceView.java +++ b/src/main/java/de/assecutor/votianlt/pages/view/EditAppDeviceView.java @@ -2,7 +2,7 @@ package de.assecutor.votianlt.pages.view; import com.vaadin.flow.component.button.Button; import com.vaadin.flow.component.button.ButtonVariant; -import com.vaadin.flow.component.combobox.ComboBox; +import com.vaadin.flow.component.html.Span; import com.vaadin.flow.component.confirmdialog.ConfirmDialog; import com.vaadin.flow.component.formlayout.FormLayout; import com.vaadin.flow.component.html.H2; @@ -34,7 +34,7 @@ public class EditAppDeviceView extends VerticalLayout implements HasUrlParameter // Formularfelder private final TextField nameField; - private final ComboBox appUserComboBox; + private final Span appUserDisplay; // Aktuelles Endgerät private AppDevice currentAppDevice; @@ -53,20 +53,15 @@ public class EditAppDeviceView extends VerticalLayout implements HasUrlParameter nameField.setPlaceholder("z.B. iPhone 15, Samsung Galaxy S24"); nameField.setWidth("100%"); - // AppUser ComboBox - appUserComboBox = new ComboBox<>("App-Nutzer zuordnen"); - appUserComboBox.setPlaceholder("App-Nutzer auswählen (optional)"); - appUserComboBox.setWidth("100%"); - appUserComboBox.setItemLabelGenerator( - appUser -> appUser.getVorname() + " " + appUser.getNachname() + " (" + appUser.getEmail() + ")"); - - // Lade verfügbare App-Nutzer - try { - appUserComboBox.setItems(appUserService.findByCurrentUser()); - } catch (Exception e) { - Notification.show("Fehler beim Laden der App-Nutzer: " + e.getMessage(), 3000, - Notification.Position.MIDDLE); - } + // App-Nutzer Anzeige (nur lesend) + appUserDisplay = new Span(); + appUserDisplay.getStyle().set("font-weight", "bold") + .set("padding", "var(--lumo-space-s)") + .set("border", "1px solid var(--lumo-contrast-20pct)") + .set("border-radius", "var(--lumo-border-radius-m)") + .set("background-color", "var(--lumo-contrast-5pct)") + .set("display", "block") + .set("width", "100%"); // Layout konfigurieren setSizeFull(); @@ -96,7 +91,15 @@ public class EditAppDeviceView extends VerticalLayout implements HasUrlParameter // Formular FormLayout formLayout = new FormLayout(); formLayout.setResponsiveSteps(new FormLayout.ResponsiveStep("0", 1)); - formLayout.add(nameField, appUserComboBox); + // Label für App-Nutzer + Span appUserLabel = new Span("Zugeordneter App-Nutzer:"); + appUserLabel.getStyle().set("font-weight", "500") + .set("color", "var(--lumo-secondary-text-color)") + .set("font-size", "var(--lumo-font-size-s)"); + + formLayout.add(nameField); + formLayout.add(appUserLabel); + formLayout.add(appUserDisplay); contentContainer.add(formLayout); // Buttons @@ -142,6 +145,9 @@ public class EditAppDeviceView extends VerticalLayout implements HasUrlParameter if (currentAppDevice != null) { // Formular mit aktuellen Daten füllen binder.readBean(currentAppDevice); + + // App-Nutzer Anzeige aktualisieren + updateAppUserDisplay(); } else { Notification.show("Endgerät nicht gefunden", 3000, Notification.Position.MIDDLE); navigateBack(); @@ -151,20 +157,35 @@ public class EditAppDeviceView extends VerticalLayout implements HasUrlParameter private void setupBinder() { binder.forField(nameField).asRequired("Gerätename ist erforderlich").bind(AppDevice::getName, AppDevice::setName); + } - binder.forField(appUserComboBox).bind(appDevice -> { - if (appDevice.getAppUserId() != null) { - return appUserService.findByCurrentUser().stream() - .filter(user -> user.getId().equals(appDevice.getAppUserId())).findFirst().orElse(null); + private void updateAppUserDisplay() { + if (currentAppDevice != null && currentAppDevice.getAppUserId() != null) { + try { + // Finde den zugeordneten App-Nutzer + AppUser assignedUser = appUserService.findByCurrentUser().stream() + .filter(user -> user.getId().equals(currentAppDevice.getAppUserId())) + .findFirst() + .orElse(null); + + if (assignedUser != null) { + String displayText = assignedUser.getVorname() + " " + assignedUser.getNachname() + + " (" + assignedUser.getEmail() + ")"; + appUserDisplay.setText(displayText); + appUserDisplay.getStyle().set("color", "var(--lumo-body-text-color)"); + } else { + appUserDisplay.setText("App-Nutzer nicht gefunden (ID: " + currentAppDevice.getAppUserId() + ")"); + appUserDisplay.getStyle().set("color", "var(--lumo-error-text-color)"); + } + } catch (Exception e) { + appUserDisplay.setText("Fehler beim Laden des App-Nutzers"); + appUserDisplay.getStyle().set("color", "var(--lumo-error-text-color)"); } - return null; - }, (appDevice, appUser) -> { - if (appUser != null) { - appDevice.setAppUserId(appUser.getId()); - } else { - appDevice.setAppUserId(null); - } - }); + } else { + appUserDisplay.setText("Kein App-Nutzer zugeordnet"); + appUserDisplay.getStyle().set("color", "var(--lumo-secondary-text-color)"); + appUserDisplay.getStyle().set("font-style", "italic"); + } } private void saveAppDevice() { diff --git a/src/main/java/de/assecutor/votianlt/pages/view/EditProfileView.java b/src/main/java/de/assecutor/votianlt/pages/view/EditProfileView.java index e1c856d..c0d704b 100644 --- a/src/main/java/de/assecutor/votianlt/pages/view/EditProfileView.java +++ b/src/main/java/de/assecutor/votianlt/pages/view/EditProfileView.java @@ -94,18 +94,38 @@ public class EditProfileView extends HorizontalLayout { // Firmenfelder TextField companyField = new TextField("Firma"); + companyField.addBlurListener(e -> validateField(companyField, "Firma ist ein Pflichtfeld")); + TextField companyAddField = new TextField("Firmenzusatz"); + TextField firstnameField = new TextField("Vorname"); + firstnameField.addBlurListener(e -> validateField(firstnameField, "Vorname ist ein Pflichtfeld")); + TextField lastnameField = new TextField("Nachname"); + lastnameField.addBlurListener(e -> validateField(lastnameField, "Nachname ist ein Pflichtfeld")); + TextField phoneField = new TextField("Telefonnummer"); + phoneField.addBlurListener(e -> validateField(phoneField, "Telefonnummer ist ein Pflichtfeld")); + TextField faxField = new TextField("Telefon (Fax)"); TextField mobileField = new TextField("Telefon (Mobil)"); + EmailField emailField = new EmailField("E-Mail-Adresse (Login)*"); + emailField.addBlurListener(e -> validateEmailField(emailField)); + TextField streetField = new TextField("Straße"); + streetField.addBlurListener(e -> validateField(streetField, "Straße ist ein Pflichtfeld")); + TextField houseNumberField = new TextField("Hausnr"); + houseNumberField.addBlurListener(e -> validateField(houseNumberField, "Hausnummer ist ein Pflichtfeld")); + TextField addressAddField = new TextField("Adresszusatz"); + TextField zipField = new TextField("Postleitzahl"); + zipField.addBlurListener(e -> validateField(zipField, "Postleitzahl ist ein Pflichtfeld")); + TextField cityField = new TextField("Stadt"); + cityField.addBlurListener(e -> validateField(cityField, "Stadt ist ein Pflichtfeld")); // Abweichende Rechnungsadresse Checkbox diffInvoiceAddress = new Checkbox("Abweichende Rechnungsadresse"); @@ -337,9 +357,33 @@ public class EditProfileView extends HorizontalLayout { VerticalLayout switches = new VerticalLayout(); switches.setPadding(false); switches.setSpacing(true); + + Checkbox digitalProcess = new Checkbox("Digitale Abwicklung per App"); + digitalProcess.setValue(currentUser.isDigitalProcessingEnabled()); + + Span digitalProcessInfo = new Span("Aktiviert die digitale Auftragsabwicklung über die mobile App"); + digitalProcessInfo.getStyle().set("font-size", "var(--lumo-font-size-s)") + .set("color", "var(--lumo-secondary-text-color)") + .set("margin-left", "var(--lumo-space-xl)"); + Checkbox locateAppUser = new Checkbox("App-Nutzer orten"); - Checkbox digitalProcess = new Checkbox("Digitale Abwicklung"); - switches.add(locateAppUser, digitalProcess); + locateAppUser.setValue(currentUser.isLocationTrackingEnabled()); + + Span locateAppUserInfo = new Span("Ermöglicht die Ortung von App-Nutzern während der Auftragsausführung"); + locateAppUserInfo.getStyle().set("font-size", "var(--lumo-font-size-s)") + .set("color", "var(--lumo-secondary-text-color)") + .set("margin-left", "var(--lumo-space-xl)"); + + // Save checkbox states when changed + digitalProcess.addValueChangeListener(e -> { + currentUser.setDigitalProcessingEnabled(e.getValue()); + }); + + locateAppUser.addValueChangeListener(e -> { + currentUser.setLocationTrackingEnabled(e.getValue()); + }); + + switches.add(digitalProcess, digitalProcessInfo, locateAppUser, locateAppUserInfo); tabSheet.add("Einstellungen", switches); // Sicherheit-Tab (2FA, Passwort, Konto) @@ -371,6 +415,15 @@ public class EditProfileView extends HorizontalLayout { Button saveProfile = new Button("Profiländerungen speichern"); saveProfile.addThemeVariants(ButtonVariant.LUMO_PRIMARY); saveProfile.addClickListener(e -> { + // Validate all required fields first + boolean isValid = validateAllProfileFields(companyField, firstnameField, lastnameField, phoneField, + emailField, streetField, houseNumberField, zipField, cityField); + + if (!isValid) { + Notification.show("Bitte füllen Sie alle Pflichtfelder korrekt aus", 3000, Notification.Position.MIDDLE); + return; + } + if (binder.validate().isOk()) { try { // Check if billing status changed @@ -620,4 +673,47 @@ public class EditProfileView extends HorizontalLayout { return value != null ? value : ""; } + private void validateField(TextField field, String errorMessage) { + String value = field.getValue(); + if (value == null || value.trim().isEmpty()) { + field.setInvalid(true); + field.setErrorMessage(errorMessage); + } else { + field.setInvalid(false); + field.setErrorMessage(""); + } + } + + private void validateEmailField(EmailField emailField) { + String value = emailField.getValue(); + if (value == null || value.trim().isEmpty()) { + emailField.setInvalid(true); + emailField.setErrorMessage("E-Mail-Adresse ist ein Pflichtfeld"); + } else if (!value.contains("@") || !value.contains(".")) { + emailField.setInvalid(true); + emailField.setErrorMessage("Bitte geben Sie eine gültige E-Mail-Adresse ein"); + } else { + emailField.setInvalid(false); + emailField.setErrorMessage(""); + } + } + + private boolean validateAllProfileFields(TextField companyField, TextField firstnameField, TextField lastnameField, + TextField phoneField, EmailField emailField, TextField streetField, + TextField houseNumberField, TextField zipField, TextField cityField) { + validateField(companyField, "Firma ist ein Pflichtfeld"); + validateField(firstnameField, "Vorname ist ein Pflichtfeld"); + validateField(lastnameField, "Nachname ist ein Pflichtfeld"); + validateField(phoneField, "Telefonnummer ist ein Pflichtfeld"); + validateEmailField(emailField); + validateField(streetField, "Straße ist ein Pflichtfeld"); + validateField(houseNumberField, "Hausnummer ist ein Pflichtfeld"); + validateField(zipField, "Postleitzahl ist ein Pflichtfeld"); + validateField(cityField, "Stadt ist ein Pflichtfeld"); + + return !companyField.isInvalid() && !firstnameField.isInvalid() && !lastnameField.isInvalid() + && !phoneField.isInvalid() && !emailField.isInvalid() && !streetField.isInvalid() + && !houseNumberField.isInvalid() && !zipField.isInvalid() && !cityField.isInvalid(); + } + }