Erweiterungen
This commit is contained in:
@@ -7,6 +7,8 @@ import com.vaadin.flow.component.formlayout.FormLayout;
|
||||
import com.vaadin.flow.component.html.Div;
|
||||
import com.vaadin.flow.component.html.Paragraph;
|
||||
import com.vaadin.flow.component.html.Span;
|
||||
import com.vaadin.flow.component.html.H3;
|
||||
import com.vaadin.flow.component.html.IFrame;
|
||||
import com.vaadin.flow.component.icon.Icon;
|
||||
import com.vaadin.flow.component.icon.VaadinIcon;
|
||||
import com.vaadin.flow.component.notification.Notification;
|
||||
@@ -14,8 +16,14 @@ import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
||||
import com.vaadin.flow.component.textfield.EmailField;
|
||||
import com.vaadin.flow.component.textfield.TextField;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.Base64;
|
||||
|
||||
import com.vaadin.flow.component.textfield.TextArea;
|
||||
import com.vaadin.flow.component.tabs.TabSheet;
|
||||
import com.vaadin.flow.data.binder.Binder;
|
||||
import com.vaadin.flow.data.validator.EmailValidator;
|
||||
import com.vaadin.flow.data.value.ValueChangeMode;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import de.assecutor.votianlt.model.User;
|
||||
@@ -27,14 +35,20 @@ import jakarta.annotation.security.RolesAllowed;
|
||||
@Route(value = "edit-profile", layout = de.assecutor.votianlt.pages.base.ui.view.MainLayout.class)
|
||||
@RolesAllowed({"USER","ADMIN"})
|
||||
public class EditProfileView extends HorizontalLayout {
|
||||
private final UserService userService;
|
||||
private final SecurityService securityService;
|
||||
private final TextField prefixField;
|
||||
private final TextField ustIdField;
|
||||
private final TextField taxNumberField;
|
||||
private final TextField bankNameField;
|
||||
private final TextField ibanField;
|
||||
private final TextField taxRateField;
|
||||
private final TextArea introTextArea;
|
||||
private final TextArea termsTextArea;
|
||||
private final IFrame pdfFrame;
|
||||
|
||||
private final Binder<User> binder = new Binder<>(User.class);
|
||||
private final User currentUser;
|
||||
|
||||
public EditProfileView(UserService userService, SecurityService securityService) {
|
||||
this.userService = userService;
|
||||
this.securityService = securityService;
|
||||
this.currentUser = securityService.getCurrentDatabaseUser();
|
||||
setSizeFull();
|
||||
setPadding(true);
|
||||
@@ -49,9 +63,13 @@ public class EditProfileView extends HorizontalLayout {
|
||||
|
||||
// Linke Spalte: Formular
|
||||
VerticalLayout formColumn = new VerticalLayout();
|
||||
formColumn.setWidth("48%");
|
||||
formColumn.setWidth("68%");
|
||||
formColumn.setPadding(false);
|
||||
formColumn.setSpacing(false);
|
||||
// TabSheet
|
||||
TabSheet tabSheet = new TabSheet();
|
||||
tabSheet.setSizeFull();
|
||||
|
||||
|
||||
FormLayout form = new FormLayout();
|
||||
form.setWidthFull();
|
||||
@@ -72,10 +90,6 @@ public class EditProfileView extends HorizontalLayout {
|
||||
TextField zipField = new TextField("Postleitzahl");
|
||||
TextField cityField = new TextField("Stadt");
|
||||
|
||||
// Pflichtfeldhinweis
|
||||
Paragraph pflichtHinweis = new Paragraph("Die mit (*) gekennzeichneten Felder sind Pflichtfelder.");
|
||||
pflichtHinweis.getStyle().set("color", "#d32f2f").set("fontWeight", "bold");
|
||||
|
||||
// Abweichende Rechnungsadresse
|
||||
Checkbox diffInvoiceAddress = new Checkbox("Abweichende Rechnungsadresse");
|
||||
diffInvoiceAddress.getStyle().set("marginTop", "1em");
|
||||
@@ -194,39 +208,126 @@ public class EditProfileView extends HorizontalLayout {
|
||||
form.add(diffInvoiceAddress, 2);
|
||||
form.add(invCompanyField, 2);
|
||||
form.add(invCompanyAddField, 2);
|
||||
// Tabs hinzufügen
|
||||
form.add(invFirstnameField, invLastnameField);
|
||||
form.add(invStreetField, invHouseNumberField);
|
||||
form.add(invAddressAddField, 2);
|
||||
form.add(invZipField, invCityField);
|
||||
|
||||
formColumn.add(form, pflichtHinweis);
|
||||
tabSheet.add("Stammdaten", form);
|
||||
|
||||
// Schalter
|
||||
Checkbox locateAppUser = new Checkbox("App-Nutzer orten");
|
||||
Checkbox digitalProcess = new Checkbox("Digitale Abwicklung");
|
||||
Checkbox invoiceViaVotianlt = new Checkbox("Rechnungslegung über votianlt");
|
||||
formColumn.add(locateAppUser, digitalProcess, invoiceViaVotianlt);
|
||||
|
||||
// Rechte Spalte: Karte und Aktionen
|
||||
VerticalLayout rightColumn = new VerticalLayout();
|
||||
rightColumn.setWidth("48%");
|
||||
rightColumn.setAlignItems(Alignment.CENTER);
|
||||
rightColumn.setSpacing(true);
|
||||
rightColumn.setPadding(false);
|
||||
|
||||
// Koordinatenanzeige
|
||||
// Karte (2. Tab)
|
||||
Span coordsLabel = new Span("53°36'25.1\"N 10°06'46.9\"E");
|
||||
coordsLabel.getStyle().set("fontWeight", "bold").set("marginTop", "1em");
|
||||
rightColumn.add(coordsLabel);
|
||||
|
||||
// Google Maps Platzhalter (iFrame)
|
||||
Div mapDiv = new Div();
|
||||
mapDiv.setWidth("400px");
|
||||
mapDiv.setWidth("100%");
|
||||
mapDiv.setHeight("400px");
|
||||
mapDiv.getElement().setProperty("innerHTML",
|
||||
"<iframe width='100%' height='100%' frameborder='0' style='border:0' " +
|
||||
"src='https://www.google.com/maps/embed/v1/place?key=AIzaSyDnbitL06iLp3elmj-WtPudCykX9xvXcVE&q=53.6070,10.1125' allowfullscreen></iframe>");
|
||||
rightColumn.add(mapDiv);
|
||||
VerticalLayout mapTab = new VerticalLayout();
|
||||
mapTab.setPadding(false);
|
||||
mapTab.setSpacing(true);
|
||||
mapTab.add(coordsLabel, mapDiv);
|
||||
tabSheet.add("Karte", mapTab);
|
||||
// Dritter Tab: Rechnungsstellung
|
||||
HorizontalLayout billingTab = new HorizontalLayout();
|
||||
billingTab.setWidthFull();
|
||||
billingTab.setSpacing(true);
|
||||
billingTab.setPadding(false);
|
||||
|
||||
// Linke Spalte: Rechnungsbestandteile
|
||||
VerticalLayout billingLeft = new VerticalLayout();
|
||||
billingLeft.setWidth("45%");
|
||||
billingLeft.setPadding(false);
|
||||
billingLeft.setSpacing(true);
|
||||
H3 partsTitle = new H3("Rechnungsbestandteile");
|
||||
partsTitle.getStyle().set("margin", "0 0 var(--lumo-space-s) 0");
|
||||
|
||||
// Felder für Rechnungsstellung (für Live-Update)
|
||||
Checkbox billingEnabled = new Checkbox("Rechnungslegung über votianLT");
|
||||
billingEnabled.setValue(false);
|
||||
billingEnabled.addValueChangeListener(e -> toggleBilling(e.getValue()));
|
||||
|
||||
prefixField = new TextField("Rechnungs Präfix");
|
||||
ustIdField = new TextField("USt-IdNr.");
|
||||
taxNumberField = new TextField("Steuernummer");
|
||||
bankNameField = new TextField("Bankname");
|
||||
ibanField = new TextField("IBAN");
|
||||
taxRateField = new TextField("Steuersatz");
|
||||
introTextArea = new TextArea("Einleitungstext");
|
||||
termsTextArea = new TextArea("Zahlungsbedingungen");
|
||||
introTextArea.setWidthFull();
|
||||
termsTextArea.setWidthFull();
|
||||
|
||||
// Anfangszustand: Felder deaktiviert bis Checkbox aktiv
|
||||
setBillingFieldsEnabled(false);
|
||||
|
||||
// Live-Update: EAGER und Listener
|
||||
prefixField.setValueChangeMode(ValueChangeMode.EAGER);
|
||||
ustIdField.setValueChangeMode(ValueChangeMode.EAGER);
|
||||
taxNumberField.setValueChangeMode(ValueChangeMode.EAGER);
|
||||
bankNameField.setValueChangeMode(ValueChangeMode.EAGER);
|
||||
ibanField.setValueChangeMode(ValueChangeMode.EAGER);
|
||||
taxRateField.setValueChangeMode(ValueChangeMode.EAGER);
|
||||
introTextArea.setValueChangeMode(ValueChangeMode.EAGER);
|
||||
termsTextArea.setValueChangeMode(ValueChangeMode.EAGER);
|
||||
prefixField.addValueChangeListener(e -> refreshPdf());
|
||||
ustIdField.addValueChangeListener(e -> refreshPdf());
|
||||
taxNumberField.addValueChangeListener(e -> refreshPdf());
|
||||
bankNameField.addValueChangeListener(e -> refreshPdf());
|
||||
ibanField.addValueChangeListener(e -> refreshPdf());
|
||||
taxRateField.addValueChangeListener(e -> refreshPdf());
|
||||
introTextArea.addValueChangeListener(e -> refreshPdf());
|
||||
termsTextArea.addValueChangeListener(e -> refreshPdf());
|
||||
|
||||
billingLeft.add(partsTitle, billingEnabled, prefixField, ustIdField, taxNumberField, bankNameField,
|
||||
ibanField, taxRateField, introTextArea, termsTextArea);
|
||||
|
||||
// Rechte Spalte: Vorschau
|
||||
VerticalLayout billingRight = new VerticalLayout();
|
||||
billingRight.setWidth("55%");
|
||||
billingRight.setPadding(false);
|
||||
billingRight.setSpacing(false);
|
||||
billingRight.setHeight("70vh");
|
||||
H3 previewTitle = new H3("Rechnungsvorschau");
|
||||
previewTitle.getStyle().set("margin", "0 0 var(--lumo-space-s) 0");
|
||||
|
||||
// Echte PDF-Vorschau mittels StreamResource und iframe
|
||||
Div previewWrapper = new Div();
|
||||
previewWrapper.setWidth("100%");
|
||||
previewWrapper.setHeight("100%");
|
||||
previewWrapper.getStyle()
|
||||
.set("overflow", "auto");
|
||||
|
||||
// Initial noch keine PDF laden (erst bei aktiver Checkbox)
|
||||
pdfFrame = new IFrame();
|
||||
pdfFrame.setWidth("100%");
|
||||
pdfFrame.setHeight("100%");
|
||||
previewWrapper.removeAll();
|
||||
previewWrapper.add(pdfFrame);
|
||||
|
||||
billingRight.add(previewTitle, previewWrapper);
|
||||
|
||||
|
||||
billingTab.add(billingLeft, billingRight);
|
||||
tabSheet.add("Rechnungsstellung", billingTab);
|
||||
|
||||
|
||||
// Zweiter Tab: Einstellungen (Beispiel mit Schaltern)
|
||||
VerticalLayout switches = new VerticalLayout();
|
||||
switches.setPadding(false);
|
||||
switches.setSpacing(true);
|
||||
Checkbox locateAppUser = new Checkbox("App-Nutzer orten");
|
||||
Checkbox digitalProcess = new Checkbox("Digitale Abwicklung");
|
||||
Checkbox invoiceViaVotianlt = new Checkbox("Rechnungslegung über votianlt");
|
||||
switches.add(locateAppUser, digitalProcess, invoiceViaVotianlt);
|
||||
tabSheet.add("Einstellungen", switches);
|
||||
|
||||
// Sicherheit-Tab (2FA, Passwort, Konto)
|
||||
VerticalLayout securityTab = new VerticalLayout();
|
||||
securityTab.setPadding(false);
|
||||
securityTab.setSpacing(true);
|
||||
|
||||
// 2-Faktor Auth
|
||||
Checkbox twoFactor = new Checkbox("2-Faktor-Authentifizierung");
|
||||
@@ -234,17 +335,19 @@ public class EditProfileView extends HorizontalLayout {
|
||||
twoFactorInfo.getStyle().set("marginLeft", "0.3em");
|
||||
HorizontalLayout twoFactorLayout = new HorizontalLayout(twoFactor, twoFactorInfo);
|
||||
twoFactorLayout.setAlignItems(Alignment.CENTER);
|
||||
rightColumn.add(twoFactorLayout);
|
||||
securityTab.add(twoFactorLayout);
|
||||
|
||||
// Passwort ändern Button
|
||||
Button changePassword = new Button("Passwort ändern");
|
||||
changePassword.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
||||
rightColumn.add(changePassword);
|
||||
securityTab.add(changePassword);
|
||||
|
||||
// Benutzerkonto löschen Button
|
||||
Button deleteAccount = new Button("Benutzerkonto löschen");
|
||||
deleteAccount.addThemeVariants(ButtonVariant.LUMO_ERROR);
|
||||
rightColumn.add(deleteAccount);
|
||||
securityTab.add(deleteAccount);
|
||||
|
||||
tabSheet.add("Sicherheit", securityTab);
|
||||
|
||||
// Profil speichern Button (unten rechts)
|
||||
Button saveProfile = new Button("Profiländerungen speichern");
|
||||
@@ -260,8 +363,91 @@ public class EditProfileView extends HorizontalLayout {
|
||||
}
|
||||
}
|
||||
});
|
||||
// Tabs dem linken Bereich hinzufügen
|
||||
formColumn.add(tabSheet);
|
||||
|
||||
saveProfile.getStyle().set("position", "absolute").set("right", "2em").set("bottom", "2em");
|
||||
add(formColumn, rightColumn, saveProfile);
|
||||
|
||||
// Button horizontal zentrieren unterhalb der Tabs
|
||||
HorizontalLayout buttonBar = new HorizontalLayout(saveProfile);
|
||||
buttonBar.setWidthFull();
|
||||
buttonBar.setJustifyContentMode(JustifyContentMode.CENTER);
|
||||
buttonBar.setPadding(false);
|
||||
buttonBar.setSpacing(false);
|
||||
// 20px Abstand nach oben
|
||||
buttonBar.getStyle().set("margin-top", "20px");
|
||||
// Fixieren am unteren Rand
|
||||
buttonBar.getStyle().set("position", "sticky");
|
||||
buttonBar.getStyle().set("bottom", "0");
|
||||
buttonBar.getStyle().set("background", "var(--lumo-base-color)");
|
||||
buttonBar.getStyle().set("padding", "var(--lumo-space-s) 0");
|
||||
buttonBar.getStyle().set("z-index", "1");
|
||||
formColumn.add(buttonBar);
|
||||
add(formColumn);
|
||||
}
|
||||
|
||||
// PDF neu rendern und iframe aktualisieren
|
||||
// Felder im Billing-Tab aktivieren/deaktivieren
|
||||
private void setBillingFieldsEnabled(boolean enabled) {
|
||||
if (prefixField != null) prefixField.setEnabled(enabled);
|
||||
if (ustIdField != null) ustIdField.setEnabled(enabled);
|
||||
if (taxNumberField != null) taxNumberField.setEnabled(enabled);
|
||||
if (bankNameField != null) bankNameField.setEnabled(enabled);
|
||||
if (ibanField != null) ibanField.setEnabled(enabled);
|
||||
if (taxRateField != null) taxRateField.setEnabled(enabled);
|
||||
if (introTextArea != null) introTextArea.setEnabled(enabled);
|
||||
if (termsTextArea != null) termsTextArea.setEnabled(enabled);
|
||||
}
|
||||
|
||||
// Checkbox steuert Aktivierung und PDF
|
||||
private void toggleBilling(boolean enabled) {
|
||||
setBillingFieldsEnabled(enabled);
|
||||
if (enabled) {
|
||||
refreshPdf();
|
||||
} else {
|
||||
if (pdfFrame != null) {
|
||||
pdfFrame.setSrc((String) null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void refreshPdf() {
|
||||
byte[] bytes = generatePreviewPdf();
|
||||
String dataUrl = "data:application/pdf;base64," + Base64.getEncoder().encodeToString(bytes) + "#toolbar=0&navpanes=0&zoom=page-width&view=FitH";
|
||||
if (pdfFrame != null) {
|
||||
pdfFrame.setSrc(dataUrl);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Einfache PDF-Vorschau generieren (kann später durch echte Logik ersetzt werden)
|
||||
private byte[] generatePreviewPdf() {
|
||||
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
|
||||
com.lowagie.text.Document document = new com.lowagie.text.Document();
|
||||
com.lowagie.text.pdf.PdfWriter.getInstance(document, out);
|
||||
document.open();
|
||||
document.add(new com.lowagie.text.Paragraph("Rechnungsvorschau"));
|
||||
document.add(new com.lowagie.text.Paragraph(" "));
|
||||
document.add(new com.lowagie.text.Paragraph("Rechnungs Präfix: " + safe(prefixField)));
|
||||
document.add(new com.lowagie.text.Paragraph("USt-IdNr.: " + safe(ustIdField)));
|
||||
document.add(new com.lowagie.text.Paragraph("Steuernummer: " + safe(taxNumberField)));
|
||||
document.add(new com.lowagie.text.Paragraph("Bankname: " + safe(bankNameField)));
|
||||
document.add(new com.lowagie.text.Paragraph("IBAN: " + safe(ibanField)));
|
||||
document.add(new com.lowagie.text.Paragraph("Steuersatz: " + safe(taxRateField)));
|
||||
document.add(new com.lowagie.text.Paragraph(" "));
|
||||
document.add(new com.lowagie.text.Paragraph("Einleitungstext:"));
|
||||
document.add(new com.lowagie.text.Paragraph(safe(introTextArea)));
|
||||
document.add(new com.lowagie.text.Paragraph(" "));
|
||||
document.add(new com.lowagie.text.Paragraph("Zahlungsbedingungen:"));
|
||||
document.add(new com.lowagie.text.Paragraph(safe(termsTextArea)));
|
||||
document.close();
|
||||
return out.toByteArray();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("PDF-Generierung fehlgeschlagen: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
// Utility: safe getter für TextField/TextArea
|
||||
private String safe(TextField f) { return f != null && f.getValue() != null ? f.getValue() : ""; }
|
||||
private String safe(TextArea f) { return f != null && f.getValue() != null ? f.getValue() : ""; }
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user