diff --git a/pom.xml b/pom.xml
index 799c785..ace7454 100644
--- a/pom.xml
+++ b/pom.xml
@@ -87,6 +87,11 @@
openpdf
1.3.30
+
+ com.sun.mail
+ jakarta.mail
+ 2.0.1
+
diff --git a/src/main/java/de/assecutor/votianlt/model/AppUser.java b/src/main/java/de/assecutor/votianlt/model/AppUser.java
index 432ea19..b3a8592 100644
--- a/src/main/java/de/assecutor/votianlt/model/AppUser.java
+++ b/src/main/java/de/assecutor/votianlt/model/AppUser.java
@@ -40,6 +40,9 @@ public class AppUser {
@Field("app_device_id")
private ObjectId appDeviceId;
+ @Field("owner")
+ private ObjectId owner;
+
@Field("erstellt_am")
private LocalDateTime erstelltAm;
diff --git a/src/main/java/de/assecutor/votianlt/pages/base/ui/view/MainLayout.java b/src/main/java/de/assecutor/votianlt/pages/base/ui/view/MainLayout.java
index c7b5054..4f6f9a5 100644
--- a/src/main/java/de/assecutor/votianlt/pages/base/ui/view/MainLayout.java
+++ b/src/main/java/de/assecutor/votianlt/pages/base/ui/view/MainLayout.java
@@ -98,9 +98,9 @@ public final class MainLayout extends AppLayout {
userContent.setSpacing(true);
// Create navigation items for the collapsible list
- SideNavItem profile = new SideNavItem("Mein Profil", "7", new Icon(VaadinIcon.COG));
+ SideNavItem profile = new SideNavItem("Mein Profil", "edit-profile", new Icon(VaadinIcon.USER));
SideNavItem myInvoices = new SideNavItem("Meine Rechnungen", "8", new Icon(VaadinIcon.COG));
- SideNavItem imprint = new SideNavItem("Impressum", "9", new Icon(VaadinIcon.COG));
+ SideNavItem imprint = new SideNavItem("Impressum", "impressum", new Icon(VaadinIcon.INFO_CIRCLE));
userContent.add(profile, myInvoices, imprint);
userDetails.add(userContent);
diff --git a/src/main/java/de/assecutor/votianlt/pages/service/AppUserService.java b/src/main/java/de/assecutor/votianlt/pages/service/AppUserService.java
index 61838f3..40420d5 100644
--- a/src/main/java/de/assecutor/votianlt/pages/service/AppUserService.java
+++ b/src/main/java/de/assecutor/votianlt/pages/service/AppUserService.java
@@ -32,6 +32,7 @@ public class AppUserService {
ObjectId currentUserId = securityService.getCurrentUserId();
appUser.setErstelltVon(currentUserId);
appUser.setAktualisiertVon(currentUserId);
+ appUser.setOwner(currentUserId);
return appUserRepository.save(appUser);
}
diff --git a/src/main/java/de/assecutor/votianlt/pages/view/AppUserView.java b/src/main/java/de/assecutor/votianlt/pages/view/AppUserView.java
index 264c7eb..ff9f253 100644
--- a/src/main/java/de/assecutor/votianlt/pages/view/AppUserView.java
+++ b/src/main/java/de/assecutor/votianlt/pages/view/AppUserView.java
@@ -13,10 +13,7 @@ import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import de.assecutor.votianlt.model.AppUser;
import de.assecutor.votianlt.pages.service.AppUserService;
-import de.assecutor.votianlt.model.AppDevice;
-import de.assecutor.votianlt.pages.service.AppDeviceService;
import jakarta.annotation.security.RolesAllowed;
-import org.springframework.beans.factory.annotation.Autowired;
@PageTitle("App-Nutzer")
@Route(value = "app-user", layout = de.assecutor.votianlt.pages.base.ui.view.MainLayout.class)
@@ -25,12 +22,9 @@ public class AppUserView extends VerticalLayout {
private final AppUserService appUserService;
private final Grid appUserGrid;
- private final AppDeviceService appDeviceService;
- @Autowired
- public AppUserView(AppUserService appUserService, AppDeviceService appDeviceService) {
+ public AppUserView(AppUserService appUserService) {
this.appUserService = appUserService;
- this.appDeviceService = appDeviceService;
setSizeFull();
setPadding(true);
@@ -63,13 +57,7 @@ public class AppUserView extends VerticalLayout {
appUserGrid.addColumn(AppUser::getTelefon).setHeader("Telefon").setAutoWidth(true);
appUserGrid.addColumn(AppUser::getAppCode).setHeader("App-Code").setAutoWidth(true);
appUserGrid.addColumn(AppUser::getEmail).setHeader("E-Mail").setAutoWidth(true);
- appUserGrid.addColumn(appUser -> {
- if (appUser.getAppDeviceId() != null) {
- AppDevice device = appDeviceService.findById(appUser.getAppDeviceId());
- return device != null ? device.getName() : "Unbekannt";
- }
- return "Nicht zugeordnet";
- }).setHeader("Gerät").setAutoWidth(true);
+ appUserGrid.addColumn(AppUser::getGeraet).setHeader("Gerät").setAutoWidth(true);
// Make grid rows clickable
appUserGrid.setSelectionMode(Grid.SelectionMode.SINGLE);
diff --git a/src/main/java/de/assecutor/votianlt/pages/view/EditProfileView.java b/src/main/java/de/assecutor/votianlt/pages/view/EditProfileView.java
new file mode 100644
index 0000000..b28c7b1
--- /dev/null
+++ b/src/main/java/de/assecutor/votianlt/pages/view/EditProfileView.java
@@ -0,0 +1,170 @@
+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.checkbox.Checkbox;
+import com.vaadin.flow.component.formlayout.FormLayout;
+import com.vaadin.flow.component.html.Div;
+import com.vaadin.flow.component.html.H2;
+import com.vaadin.flow.component.html.Label;
+import com.vaadin.flow.component.html.Paragraph;
+import com.vaadin.flow.component.icon.Icon;
+import com.vaadin.flow.component.icon.VaadinIcon;
+import com.vaadin.flow.component.notification.Notification;
+import com.vaadin.flow.component.orderedlayout.FlexComponent;
+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 com.vaadin.flow.component.textfield.NumberField;
+import com.vaadin.flow.router.PageTitle;
+import com.vaadin.flow.router.Route;
+import jakarta.annotation.security.RolesAllowed;
+
+@PageTitle("Profil bearbeiten")
+@Route(value = "edit-profile", layout = de.assecutor.votianlt.pages.base.ui.view.MainLayout.class)
+@RolesAllowed({"USER","ADMIN"})
+public class EditProfileView extends HorizontalLayout {
+ public EditProfileView() {
+ setSizeFull();
+ setPadding(true);
+ setSpacing(true);
+ setJustifyContentMode(JustifyContentMode.CENTER);
+ setAlignItems(Alignment.START);
+
+ // Linke Spalte: Formular
+ VerticalLayout formColumn = new VerticalLayout();
+ formColumn.setWidth("500px");
+ formColumn.setPadding(false);
+ formColumn.setSpacing(false);
+
+ FormLayout form = new FormLayout();
+ form.setWidthFull();
+ form.setResponsiveSteps(new FormLayout.ResponsiveStep("0", 2));
+
+ // Firmenfelder
+ TextField companyField = new TextField("Firma*");
+ TextField companyAddField = new TextField("Firmenzusatz");
+ TextField firstnameField = new TextField("Vorname*");
+ TextField lastnameField = new TextField("Nachname*");
+ TextField phoneField = new TextField("Telefonnummer*");
+ TextField faxField = new TextField("Telefon (Fax)");
+ TextField mobileField = new TextField("Telefon (Mobil)");
+ EmailField emailField = new EmailField("E-Mail-Adresse (Login)*");
+ TextField streetField = new TextField("Straße*");
+ TextField houseNumberField = new TextField("Hausnr*");
+ TextField addressAddField = new TextField("Adresszusatz");
+ 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");
+ // Rechnungsadresse Felder (disabled by default)
+ TextField invCompanyField = new TextField("Firma*");
+ TextField invCompanyAddField = new TextField("Firmenzusatz");
+ TextField invFirstnameField = new TextField("Vorname*");
+ TextField invLastnameField = new TextField("Nachname*");
+ TextField invStreetField = new TextField("Straße*");
+ TextField invHouseNumberField = new TextField("Hausnr*");
+ TextField invAddressAddField = new TextField("Adresszusatz");
+ TextField invZipField = new TextField("Postleitzahl*");
+ TextField invCityField = new TextField("Stadt*");
+ invCompanyField.setEnabled(false);
+ invCompanyAddField.setEnabled(false);
+ invFirstnameField.setEnabled(false);
+ invLastnameField.setEnabled(false);
+ invStreetField.setEnabled(false);
+ invHouseNumberField.setEnabled(false);
+ invAddressAddField.setEnabled(false);
+ invZipField.setEnabled(false);
+ invCityField.setEnabled(false);
+ diffInvoiceAddress.addValueChangeListener(e -> {
+ boolean enabled = e.getValue();
+ invCompanyField.setEnabled(enabled);
+ invCompanyAddField.setEnabled(enabled);
+ invFirstnameField.setEnabled(enabled);
+ invLastnameField.setEnabled(enabled);
+ invStreetField.setEnabled(enabled);
+ invHouseNumberField.setEnabled(enabled);
+ invAddressAddField.setEnabled(enabled);
+ invZipField.setEnabled(enabled);
+ invCityField.setEnabled(enabled);
+ });
+
+ // Formularfelder hinzufügen
+ form.add(companyField, 2);
+ form.add(companyAddField, 2);
+ form.add(firstnameField, lastnameField);
+ form.add(phoneField, faxField);
+ form.add(mobileField, 2);
+ form.add(emailField, 2);
+ form.add(streetField, houseNumberField);
+ form.add(addressAddField, 2);
+ form.add(zipField, cityField);
+ form.add(diffInvoiceAddress, 2);
+ form.add(invCompanyField, 2);
+ form.add(invCompanyAddField, 2);
+ form.add(invFirstnameField, invLastnameField);
+ form.add(invStreetField, invHouseNumberField);
+ form.add(invAddressAddField, 2);
+ form.add(invZipField, invCityField);
+
+ formColumn.add(form, pflichtHinweis);
+
+ // 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("100%");
+ rightColumn.setAlignItems(Alignment.CENTER);
+ rightColumn.setSpacing(true);
+ rightColumn.setPadding(false);
+
+ // Koordinatenanzeige
+ Label coordsLabel = new Label("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.setHeight("400px");
+ mapDiv.getElement().setProperty("innerHTML",
+ "");
+ rightColumn.add(mapDiv);
+
+ // 2-Faktor Auth
+ Checkbox twoFactor = new Checkbox("2-Faktor-Authentifizierung");
+ Icon twoFactorInfo = VaadinIcon.QUESTION_CIRCLE_O.create();
+ twoFactorInfo.getStyle().set("marginLeft", "0.3em");
+ HorizontalLayout twoFactorLayout = new HorizontalLayout(twoFactor, twoFactorInfo);
+ twoFactorLayout.setAlignItems(Alignment.CENTER);
+ rightColumn.add(twoFactorLayout);
+
+ // Passwort ändern Button
+ Button changePassword = new Button("Passwort ändern");
+ changePassword.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
+ rightColumn.add(changePassword);
+
+ // Benutzerkonto löschen Button
+ Button deleteAccount = new Button("Benutzerkonto löschen");
+ deleteAccount.addThemeVariants(ButtonVariant.LUMO_ERROR);
+ rightColumn.add(deleteAccount);
+
+ // Profil speichern Button (unten rechts)
+ Button saveProfile = new Button("Profiländerungen speichern");
+ saveProfile.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
+ saveProfile.getStyle().set("position", "absolute").set("right", "2em").set("bottom", "2em");
+ add(formColumn, rightColumn, saveProfile);
+ }
+}
diff --git a/src/main/java/de/assecutor/votianlt/pages/view/ImprintView.java b/src/main/java/de/assecutor/votianlt/pages/view/ImprintView.java
new file mode 100644
index 0000000..813c413
--- /dev/null
+++ b/src/main/java/de/assecutor/votianlt/pages/view/ImprintView.java
@@ -0,0 +1,30 @@
+package de.assecutor.votianlt.pages.view;
+
+import com.vaadin.flow.component.html.H2;
+import com.vaadin.flow.component.html.Paragraph;
+import com.vaadin.flow.component.orderedlayout.VerticalLayout;
+import com.vaadin.flow.router.PageTitle;
+import com.vaadin.flow.router.Route;
+import jakarta.annotation.security.PermitAll;
+
+@PageTitle("Impressum")
+@Route(value = "impressum", layout = de.assecutor.votianlt.pages.base.ui.view.MainLayout.class)
+@PermitAll
+public class ImprintView extends VerticalLayout {
+ public ImprintView() {
+ setSizeFull();
+ setPadding(true);
+ setSpacing(true);
+ setAlignItems(Alignment.CENTER);
+
+ H2 title = new H2("Impressum");
+ add(title);
+
+ Paragraph p1 = new Paragraph("Max Mustermann\nMusterstraße 1\n12345 Musterstadt\nDeutschland");
+ Paragraph p2 = new Paragraph("Telefon: +49 123 456789\nE-Mail: info@example.com");
+ Paragraph p3 = new Paragraph("Umsatzsteuer-ID: DE123456789\nHandelsregister: Amtsgericht Musterstadt, HRB 12345");
+ Paragraph p4 = new Paragraph("Verantwortlich für den Inhalt nach § 55 Abs. 2 RStV: Max Mustermann");
+
+ add(p1, p2, p3, p4);
+ }
+}
diff --git a/src/main/java/de/assecutor/votianlt/util/MailUtil.java b/src/main/java/de/assecutor/votianlt/util/MailUtil.java
new file mode 100644
index 0000000..4609da3
--- /dev/null
+++ b/src/main/java/de/assecutor/votianlt/util/MailUtil.java
@@ -0,0 +1,41 @@
+package de.assecutor.votianlt.util;
+
+import jakarta.mail.Message;
+import jakarta.mail.MessagingException;
+import jakarta.mail.PasswordAuthentication;
+import jakarta.mail.Session;
+import jakarta.mail.Transport;
+import jakarta.mail.internet.InternetAddress;
+import jakarta.mail.internet.MimeMessage;
+import java.util.Properties;
+
+public class MailUtil {
+ public static void sendMail(String to, String subject, String body) throws MessagingException {
+ // SMTP-Konfiguration (hier Beispiel für Gmail, anpassen für Produktivsystem!)
+ final String username = "your-email@gmail.com"; // TODO: ersetzen
+ final String password = "your-password"; // TODO: ersetzen
+ final String host = "smtp.gmail.com";
+ final int port = 587;
+
+ Properties props = new Properties();
+ props.put("mail.smtp.auth", "true");
+ props.put("mail.smtp.starttls.enable", "true");
+ props.put("mail.smtp.host", host);
+ props.put("mail.smtp.port", String.valueOf(port));
+
+ Session session = Session.getInstance(props, new jakarta.mail.Authenticator() {
+ @Override
+ protected PasswordAuthentication getPasswordAuthentication() {
+ return new PasswordAuthentication(username, password);
+ }
+ });
+
+ Message message = new MimeMessage(session);
+ message.setFrom(new InternetAddress(username));
+ message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(to));
+ message.setSubject(subject);
+ message.setText(body);
+
+ Transport.send(message);
+ }
+}