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); + } +}