Erweiterungen

This commit is contained in:
2025-08-14 16:22:14 +02:00
parent b4c6416934
commit b94921cec0
32 changed files with 513 additions and 165 deletions

View File

@@ -2,21 +2,53 @@ package de.assecutor.votianlt.model;
import lombok.Data;
import org.bson.types.ObjectId;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
@Data
@Document(collection = "customers")
public class Customer
{
@Id
private ObjectId id;
@Field("title")
private String title;
@Field("company_name")
private String companyName;
@Field("firstname")
private String firstname;
@Field("last_name")
private String lastName;
@Field("telephone")
private String telephone;
@Field("fax")
private String fax;
@Field("mail")
private String mail;
@Field("street")
private String street;
@Field("house_number")
private String houseNumber;
@Field("address_addition")
private String addressAddition;
@Field("zip")
private String zip;
@Field("city")
private String city;
@Field("created_by")
private ObjectId createdBy;
}

View File

@@ -1,6 +1,7 @@
package de.assecutor.votianlt.model;
import lombok.Data;
import org.bson.types.ObjectId;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.index.Indexed;
@@ -14,7 +15,7 @@ import java.util.Set;
public class User {
@Id
private String id;
private ObjectId id;
private int usrId;
private int hqId;

View File

@@ -1,98 +0,0 @@
package de.assecutor.votianlt.pages.add_customer.ui.view;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.html.Main;
import com.vaadin.flow.component.orderedlayout.FlexComponent;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.data.binder.Binder;
import com.vaadin.flow.data.binder.ValidationException;
import com.vaadin.flow.router.Menu;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.theme.lumo.LumoUtility;
import de.assecutor.votianlt.model.Customer;
import de.assecutor.votianlt.pages.add_customer.service.AddCustomerService;
import de.assecutor.votianlt.pages.base.ui.component.ViewToolbar;
import jakarta.annotation.security.RolesAllowed;
import java.time.Clock;
@Route(value = "add_customer", layout = de.assecutor.votianlt.pages.base.ui.view.MainLayout.class)
@PageTitle("Neuen Kunden anlegen")
//@Menu(order = 0, icon = "vaadin:clipboard-check", title = "Neuen Kunden anlegen")
@RolesAllowed("USER")
public class AddCustomerView extends Main {
private final AddCustomerService addCustomerService;
TextField companyName;
TextField firstName;
TextField lastName;
TextField telephone;
TextField fax;
TextField mail;
TextField street;
TextField houseNumber;
TextField addressAddition;
TextField zip;
TextField city;
final Button submitButton;
private final Binder<Customer> binder = new Binder<>(Customer.class); // Binder f
public AddCustomerView(AddCustomerService todoService, Clock clock) {
this.addCustomerService = todoService;
companyName = new TextField("Firmenname");
companyName.setRequiredIndicatorVisible(true);
binder.forField(companyName)
.asRequired("Firmenname ist ein Pflichtfeld") // Pflichtfeldmeldung
.bind(Customer::getCompanyName, Customer::setCompanyName);
firstName = new TextField("Vorname");
lastName = new TextField("Nachname");
telephone = new TextField("Telefonnummer");
fax = new TextField("Faxnummer");
mail = new TextField("E-Mail-Adresse");
street = new TextField("Straße");
houseNumber = new TextField("Hausnummer");
addressAddition = new TextField("Adresszusatz");
zip = new TextField("Postleitzahl");
city = new TextField("Stadt");
// Setze den Button als primär
submitButton = new Button("Kunden anlegen", event -> submit());
submitButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
// Erstelle ein Div als Container (oder direkt ein Layout)
VerticalLayout formLayout = new VerticalLayout();
formLayout.add(companyName, /*firstName, lastName, telephone, fax, mail, street, houseNumber, addressAddition, zip, city,*/ submitButton);
// Zentriere die Inhalte vertikal und horizontal
formLayout.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.CENTER);
formLayout.setSpacing(true);
formLayout.setSizeUndefined(); // Inhalt eng setzen
setSizeFull();
addClassNames(LumoUtility.BoxSizing.BORDER, LumoUtility.Display.FLEX, LumoUtility.FlexDirection.COLUMN,
LumoUtility.Padding.MEDIUM, LumoUtility.Gap.SMALL);
add(new ViewToolbar("Neuen Kunden anlegen"));
add(formLayout);
}
private void submit() {
Customer customer = new Customer();
customer.setCompanyName(companyName.getValue());
try {
binder.writeBean(customer);
addCustomerService.addCustomer(customer);
} catch (ValidationException e) {
System.err.println("Validierungsfehler: " + e.getMessage());
}
}
}

View File

@@ -74,7 +74,7 @@ public final class MainLayout extends AppLayout {
// Create navigation items for the collapsible list
SideNavItem jobs = new SideNavItem("Aufträge", "jobs", new Icon(VaadinIcon.LIST));
SideNavItem customers = new SideNavItem("Kunden", "2", new Icon(VaadinIcon.COG));
SideNavItem customers = new SideNavItem("Kunden", "customers", new Icon(VaadinIcon.USERS));
SideNavItem appUsers = new SideNavItem("App-Nutzer", "3", new Icon(VaadinIcon.COG));
SideNavItem devices = new SideNavItem("Endgeräte", "4", new Icon(VaadinIcon.COG));
SideNavItem invoices = new SideNavItem("Rechnungen", "5", new Icon(VaadinIcon.COG));

View File

@@ -1,7 +1,6 @@
package de.assecutor.votianlt.pages.add_company.domain;
package de.assecutor.votianlt.pages.domain;
import de.assecutor.votianlt.model.Company;
import de.assecutor.votianlt.model.Customer;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.mongodb.repository.MongoRepository;

View File

@@ -1,11 +1,12 @@
package de.assecutor.votianlt.pages.add_customer.domain;
package de.assecutor.votianlt.pages.domain;
import de.assecutor.votianlt.model.Customer;
import org.bson.types.ObjectId;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.mongodb.repository.MongoRepository;
public interface AddCustomerRepository extends MongoRepository<Customer, String> {
public interface AddCustomerRepository extends MongoRepository<Customer, ObjectId> {
// If you don't need a total row count, Slice is better than Page.
Slice<Customer> findAllBy(Pageable pageable);

View File

@@ -1,11 +1,12 @@
package de.assecutor.votianlt.pages.customers.domain;
package de.assecutor.votianlt.pages.domain;
import de.assecutor.votianlt.model.Customer;
import org.bson.types.ObjectId;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.mongodb.repository.MongoRepository;
public interface CustomerRepository extends MongoRepository<Customer, String> {
public interface CustomerRepository extends MongoRepository<Customer, ObjectId> {
// If you don't need a total row count, Slice is better than Page.
Slice<Customer> findAllBy(Pageable pageable);

View File

@@ -1,4 +1,4 @@
package de.assecutor.votianlt.pages.login.domain;
package de.assecutor.votianlt.pages.domain;
import de.assecutor.votianlt.model.User;
import org.springframework.data.domain.Pageable;

View File

@@ -1,8 +1,6 @@
package de.assecutor.votianlt.pages.register.domain;
package de.assecutor.votianlt.pages.domain;
import jakarta.validation.constraints.Size;
import lombok.Data;
import org.jspecify.annotations.Nullable;
import org.springframework.data.mongodb.core.mapping.Document;
import java.time.Instant;

View File

@@ -1,4 +1,4 @@
package de.assecutor.votianlt.pages.register.domain;
package de.assecutor.votianlt.pages.domain;
import de.assecutor.votianlt.repository.UserRepository;

View File

@@ -1,8 +1,7 @@
package de.assecutor.votianlt.pages.add_company.service;
package de.assecutor.votianlt.pages.service;
import de.assecutor.votianlt.model.Company;
import de.assecutor.votianlt.model.Customer;
import de.assecutor.votianlt.pages.add_company.domain.AddCompanyRepository;
import de.assecutor.votianlt.pages.domain.AddCompanyRepository;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;

View File

@@ -1,8 +1,10 @@
package de.assecutor.votianlt.pages.add_customer.service;
package de.assecutor.votianlt.pages.service;
import de.assecutor.votianlt.model.Customer;
import de.assecutor.votianlt.pages.add_customer.domain.AddCustomerRepository;
import de.assecutor.votianlt.pages.domain.AddCustomerRepository;
import de.assecutor.votianlt.security.SecurityService;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@@ -13,9 +15,11 @@ import java.util.List;
@Transactional(propagation = Propagation.REQUIRES_NEW)
public class AddCustomerService {
private final AddCustomerRepository addCustomerRepository;
private final SecurityService securityService;
AddCustomerService(AddCustomerRepository addCustomerRepository) {
AddCustomerService(AddCustomerRepository addCustomerRepository, SecurityService securityService) {
this.addCustomerRepository = addCustomerRepository;
this.securityService = securityService;
}
public List<Customer> list(Pageable pageable) {
@@ -23,6 +27,10 @@ public class AddCustomerService {
}
public void addCustomer(Customer customer) {
// Setze den aktuellen Benutzer als Ersteller - jetzt direkt aus der Session
de.assecutor.votianlt.model.User currentUser = securityService.getCurrentDatabaseUser();
customer.setCreatedBy(currentUser.getId());
addCustomerRepository.save(customer);
}
}

View File

@@ -1,4 +1,4 @@
package de.assecutor.votianlt.pages.add_job.service;
package de.assecutor.votianlt.pages.service;
import de.assecutor.votianlt.model.CargoItem;
import de.assecutor.votianlt.model.Job;

View File

@@ -1,7 +1,7 @@
package de.assecutor.votianlt.pages.customers.service;
package de.assecutor.votianlt.pages.service;
import de.assecutor.votianlt.model.Customer;
import de.assecutor.votianlt.pages.customers.domain.CustomerRepository;
import de.assecutor.votianlt.pages.domain.CustomerRepository;
import org.jspecify.annotations.Nullable;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
@@ -37,4 +37,8 @@ public class CustomerService {
return todoRepository.findAllBy(pageable).toList();
}
public List<Customer> findAll() {
return todoRepository.findAll();
}
}

View File

@@ -1,8 +1,8 @@
package de.assecutor.votianlt.pages.login.service;
package de.assecutor.votianlt.pages.service;
import com.vaadin.flow.component.notification.Notification;
import de.assecutor.votianlt.model.User;
import de.assecutor.votianlt.pages.login.domain.LoginRepository;
import de.assecutor.votianlt.pages.domain.LoginRepository;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;

View File

@@ -1,13 +1,11 @@
package de.assecutor.votianlt.pages.register.service;
package de.assecutor.votianlt.pages.service;
import de.assecutor.votianlt.model.User;
import de.assecutor.votianlt.pages.register.domain.RegisterRepository;
import de.assecutor.votianlt.pages.domain.RegisterRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.time.Clock;
@Service
@Transactional(propagation = Propagation.REQUIRES_NEW)
public class RegisterService {

View File

@@ -1,4 +1,4 @@
package de.assecutor.votianlt.pages.register.service;
package de.assecutor.votianlt.pages.service;
import de.assecutor.votianlt.model.User;
import de.assecutor.votianlt.repository.UserRepository;

View File

@@ -1,4 +1,4 @@
package de.assecutor.votianlt.pages.add_company.ui.view;
package de.assecutor.votianlt.pages.view;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
@@ -8,12 +8,11 @@ import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.data.binder.Binder;
import com.vaadin.flow.data.binder.ValidationException;
import com.vaadin.flow.router.Menu;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.theme.lumo.LumoUtility;
import de.assecutor.votianlt.model.Company;
import de.assecutor.votianlt.pages.add_company.service.AddCompanyService;
import de.assecutor.votianlt.pages.service.AddCompanyService;
import de.assecutor.votianlt.pages.base.ui.component.ViewToolbar;
import jakarta.annotation.security.RolesAllowed;

View File

@@ -0,0 +1,240 @@
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.formlayout.FormLayout;
import com.vaadin.flow.component.html.Main;
import com.vaadin.flow.component.orderedlayout.FlexComponent;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.data.binder.Binder;
import com.vaadin.flow.data.binder.ValidationException;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.theme.lumo.LumoUtility;
import de.assecutor.votianlt.model.Customer;
import de.assecutor.votianlt.pages.service.AddCustomerService;
import de.assecutor.votianlt.pages.base.ui.component.ViewToolbar;
import jakarta.annotation.security.RolesAllowed;
import java.time.Clock;
@Route(value = "add-customer", layout = de.assecutor.votianlt.pages.base.ui.view.MainLayout.class)
@PageTitle("Neuen Kunden anlegen")
//@Menu(order = 0, icon = "vaadin:clipboard-check", title = "Neuen Kunden anlegen")
@RolesAllowed("USER")
public class AddCustomerView extends Main {
private final AddCustomerService addCustomerService;
TextField companyName;
ComboBox<String> title;
TextField firstName;
TextField lastName;
TextField telephone;
TextField fax;
TextField mail;
TextField street;
TextField houseNumber;
TextField addressAddition;
TextField zip;
TextField city;
final Button submitButton;
private final Binder<Customer> binder = new Binder<>(Customer.class); // Binder f
public AddCustomerView(AddCustomerService todoService, Clock clock) {
this.addCustomerService = todoService;
// Firma (Pflichtfeld)
companyName = new TextField("Firma");
companyName.setRequiredIndicatorVisible(true);
companyName.setWidthFull();
// Anrede (Dropdown)
title = new ComboBox<>("Anrede");
title.setItems("Herr", "Frau", "Divers");
title.setPlaceholder("Anrede");
title.setWidthFull();
// Vorname (Pflichtfeld)
firstName = new TextField("Vorname");
firstName.setRequiredIndicatorVisible(true);
firstName.setWidthFull();
// Nachname (Pflichtfeld)
lastName = new TextField("Nachname");
lastName.setRequiredIndicatorVisible(true);
lastName.setWidthFull();
// Telefonnummer (Pflichtfeld)
telephone = new TextField("Telefonnummer");
telephone.setRequiredIndicatorVisible(true);
telephone.setWidthFull();
// Fax (optional)
fax = new TextField("Fax");
fax.setWidthFull();
// E-Mail (Pflichtfeld)
mail = new TextField("E-Mail-Adresse");
mail.setRequiredIndicatorVisible(true);
mail.setWidthFull();
// Straße (Pflichtfeld)
street = new TextField("Straße");
street.setRequiredIndicatorVisible(true);
// Hausnummer (Pflichtfeld)
houseNumber = new TextField("Hausnr.");
houseNumber.setRequiredIndicatorVisible(true);
// Adresszusatz (optional)
addressAddition = new TextField("Adresszusatz");
addressAddition.setWidthFull();
// PLZ (Pflichtfeld)
zip = new TextField("Postleitzahl");
zip.setRequiredIndicatorVisible(true);
// Ort (Pflichtfeld)
city = new TextField("Ort");
city.setRequiredIndicatorVisible(true);
// Binder-Konfiguration
configureBinder();
// Testdaten setzen
setTestData();
// Setze den Button als primär
submitButton = new Button("Kunden anlegen", event -> submit());
submitButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
submitButton.setWidthFull();
// Erstelle FormLayout wie im Bild
FormLayout formLayout = new FormLayout();
formLayout.setMaxWidth("600px");
formLayout.getStyle().set("margin", "0 auto");
// Felder hinzufügen (einzeln pro Zeile, außer Straße/Hausnr und PLZ/Ort)
formLayout.add(companyName);
formLayout.add(title);
formLayout.add(firstName);
formLayout.add(lastName);
formLayout.add(telephone);
formLayout.add(fax);
formLayout.add(mail);
// Straße und Hausnummer nebeneinander
formLayout.add(street, houseNumber);
formLayout.add(addressAddition);
// PLZ und Ort nebeneinander
formLayout.add(zip, city);
formLayout.add(submitButton);
// Container für zentrierte Darstellung
VerticalLayout container = new VerticalLayout();
container.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.CENTER);
container.setSpacing(true);
container.add(formLayout);
setSizeFull();
addClassNames(LumoUtility.BoxSizing.BORDER, LumoUtility.Display.FLEX, LumoUtility.FlexDirection.COLUMN,
LumoUtility.Padding.MEDIUM, LumoUtility.Gap.SMALL);
add(new ViewToolbar("Neuen Kunden anlegen"));
add(container);
}
private void configureBinder() {
binder.forField(companyName)
.asRequired("Firma ist ein Pflichtfeld")
.bind(Customer::getCompanyName, Customer::setCompanyName);
binder.forField(title)
.bind(Customer::getTitle, Customer::setTitle);
binder.forField(firstName)
.asRequired("Vorname ist ein Pflichtfeld")
.bind(Customer::getFirstname, Customer::setFirstname);
binder.forField(lastName)
.asRequired("Nachname ist ein Pflichtfeld")
.bind(Customer::getLastName, Customer::setLastName);
binder.forField(telephone)
.asRequired("Telefonnummer ist ein Pflichtfeld")
.bind(Customer::getTelephone, Customer::setTelephone);
binder.forField(fax)
.bind(Customer::getFax, Customer::setFax);
binder.forField(mail)
.asRequired("E-Mail-Adresse ist ein Pflichtfeld")
.withValidator(email -> email.contains("@"), "Bitte geben Sie eine gültige E-Mail-Adresse ein")
.bind(Customer::getMail, Customer::setMail);
binder.forField(street)
.asRequired("Straße ist ein Pflichtfeld")
.bind(Customer::getStreet, Customer::setStreet);
binder.forField(houseNumber)
.asRequired("Hausnummer ist ein Pflichtfeld")
.bind(Customer::getHouseNumber, Customer::setHouseNumber);
binder.forField(addressAddition)
.bind(Customer::getAddressAddition, Customer::setAddressAddition);
binder.forField(zip)
.asRequired("Postleitzahl ist ein Pflichtfeld")
.bind(Customer::getZip, Customer::setZip);
binder.forField(city)
.asRequired("Ort ist ein Pflichtfeld")
.bind(Customer::getCity, Customer::setCity);
}
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");
}
private void submit() {
try {
Customer customer = new Customer();
binder.writeBean(customer);
addCustomerService.addCustomer(customer);
// Erfolg anzeigen und zur Kundenliste navigieren
com.vaadin.flow.component.notification.Notification.show(
"Kunde erfolgreich angelegt", 3000,
com.vaadin.flow.component.notification.Notification.Position.TOP_CENTER);
getUI().ifPresent(ui -> ui.navigate("customers"));
} catch (ValidationException e) {
com.vaadin.flow.component.notification.Notification.show(
"Bitte überprüfen Sie Ihre Eingaben", 3000,
com.vaadin.flow.component.notification.Notification.Position.TOP_CENTER);
} catch (Exception e) {
com.vaadin.flow.component.notification.Notification.show(
"Fehler beim Speichern: " + e.getMessage(), 5000,
com.vaadin.flow.component.notification.Notification.Position.TOP_CENTER);
}
}
}

View File

@@ -1,4 +1,4 @@
package de.assecutor.votianlt.pages.add_job.ui.view;
package de.assecutor.votianlt.pages.view;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
@@ -34,7 +34,7 @@ import com.vaadin.flow.router.Route;
import com.vaadin.flow.theme.lumo.LumoUtility;
import de.assecutor.votianlt.model.Job;
import de.assecutor.votianlt.model.TaskEntry;
import de.assecutor.votianlt.pages.add_job.service.AddJobService;
import de.assecutor.votianlt.pages.service.AddJobService;
import de.assecutor.votianlt.pages.base.ui.component.ViewToolbar;
import jakarta.annotation.security.RolesAllowed;
@@ -42,7 +42,6 @@ import lombok.extern.slf4j.Slf4j;
import de.assecutor.votianlt.model.CargoItem;
import java.util.*;
import java.util.Objects;
import de.assecutor.votianlt.model.TaskEntry;
import java.util.Optional;
@Route(value = "add_job", layout = de.assecutor.votianlt.pages.base.ui.view.MainLayout.class)

View File

@@ -1,4 +1,4 @@
package de.assecutor.votianlt.pages.start.ui.view;
package de.assecutor.votianlt.pages.view;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.html.*;

View File

@@ -1,4 +1,4 @@
package de.assecutor.votianlt.pages.customers.ui.view;
package de.assecutor.votianlt.pages.view;
import com.vaadin.flow.component.UI;
import com.vaadin.flow.component.button.Button;
@@ -12,7 +12,7 @@ import com.vaadin.flow.router.Route;
import com.vaadin.flow.theme.lumo.LumoUtility;
import de.assecutor.votianlt.model.Customer;
import de.assecutor.votianlt.pages.base.ui.component.ViewToolbar;
import de.assecutor.votianlt.pages.customers.service.CustomerService;
import de.assecutor.votianlt.pages.service.CustomerService;
import java.time.Clock;

View File

@@ -1,11 +1,9 @@
package de.assecutor.votianlt.pages.login.ui.view;
package de.assecutor.votianlt.pages.view;
import com.vaadin.flow.component.UI;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.html.Anchor;
import com.vaadin.flow.component.html.H1;
import com.vaadin.flow.component.html.Main;
import com.vaadin.flow.component.login.LoginForm;
import com.vaadin.flow.component.orderedlayout.FlexComponent;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;

View File

@@ -1,4 +1,4 @@
package de.assecutor.votianlt.pages.register.ui.view;
package de.assecutor.votianlt.pages.view;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
@@ -12,8 +12,8 @@ import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.auth.AnonymousAllowed;
import de.assecutor.votianlt.pages.register.service.RegisterService;
import de.assecutor.votianlt.pages.register.service.UserService;
import de.assecutor.votianlt.pages.service.RegisterService;
import de.assecutor.votianlt.pages.service.UserService;
import java.time.Clock;

View File

@@ -0,0 +1,72 @@
package de.assecutor.votianlt.pages.view;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.html.H2;
import com.vaadin.flow.component.icon.Icon;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import de.assecutor.votianlt.model.Customer;
import de.assecutor.votianlt.pages.service.CustomerService;
import jakarta.annotation.security.RolesAllowed;
import org.springframework.beans.factory.annotation.Autowired;
@PageTitle("Kunden")
@Route(value = "customers", layout = de.assecutor.votianlt.pages.base.ui.view.MainLayout.class)
@RolesAllowed({"USER","ADMIN"})
public class ShowCustomersView extends VerticalLayout {
private final CustomerService customerService;
private final Grid<Customer> grid = new Grid<>(Customer.class, false);
@Autowired
public ShowCustomersView(CustomerService customerService) {
this.customerService = customerService;
setSizeFull();
setPadding(true);
setSpacing(true);
// Header with title and add button
HorizontalLayout header = new HorizontalLayout();
header.setWidthFull();
header.add(new H2("Kunden"));
Button addCustomerButton = new Button("Kunde hinzufügen", new Icon(VaadinIcon.PLUS));
header.add(addCustomerButton);
header.setJustifyContentMode(JustifyContentMode.BETWEEN);
header.setAlignItems(Alignment.CENTER);
add(header);
// Configure grid columns
grid.addColumn(Customer::getCompanyName).setHeader("Firma").setAutoWidth(true).setFlexGrow(1).setSortable(true);
grid.addColumn(customer -> (customer.getFirstname() != null ? customer.getFirstname() : "") + " " +
(customer.getLastName() != null ? customer.getLastName() : ""))
.setHeader("Name").setAutoWidth(true).setFlexGrow(1).setSortable(true);
grid.addColumn(Customer::getMail).setHeader("E-Mail").setAutoWidth(true).setFlexGrow(1).setSortable(true);
grid.addColumn(Customer::getTelephone).setHeader("Telefon").setAutoWidth(true).setSortable(true);
grid.addColumn(customer -> (customer.getStreet() != null ? customer.getStreet() : "") + " " +
(customer.getHouseNumber() != null ? customer.getHouseNumber() : ""))
.setHeader("Straße").setAutoWidth(true).setFlexGrow(1).setSortable(true);
grid.addColumn(customer -> (customer.getZip() != null ? customer.getZip() : "") + " " +
(customer.getCity() != null ? customer.getCity() : ""))
.setHeader("Ort").setAutoWidth(true).setFlexGrow(1).setSortable(true);
grid.setMultiSort(true);
grid.setSizeFull();
add(grid);
// Button action
addCustomerButton.addClickListener(e ->
getUI().ifPresent(ui -> ui.navigate("add-customer"))
);
loadData();
}
private void loadData() {
var customers = customerService.findAll();
grid.setItems(customers);
}
}

View File

@@ -1,4 +1,4 @@
package de.assecutor.votianlt.pages.jobs.ui.view;
package de.assecutor.votianlt.pages.view;
import com.vaadin.flow.component.datepicker.DatePicker;
import com.vaadin.flow.component.button.Button;

View File

@@ -1,4 +1,4 @@
package de.assecutor.votianlt.pages.start.ui.view;
package de.assecutor.votianlt.pages.view;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.UI;
@@ -13,12 +13,10 @@ import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.router.RouterLayout;
import com.vaadin.flow.router.BeforeEnterEvent;
import com.vaadin.flow.router.BeforeEnterObserver;
import com.vaadin.flow.server.auth.AnonymousAllowed;
import de.assecutor.votianlt.security.SecurityService;
import de.assecutor.votianlt.pages.base.ui.view.MainLayout;
@Route("")
@PageTitle("VotianLT - Willkommen")

View File

@@ -1,4 +1,4 @@
package de.assecutor.votianlt.pages.verwaltung.ui.view;
package de.assecutor.votianlt.pages.view;
import com.vaadin.flow.component.html.H1;
import com.vaadin.flow.component.html.Main;

View File

@@ -0,0 +1,73 @@
package de.assecutor.votianlt.security;
import de.assecutor.votianlt.model.User;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import java.util.stream.Collectors;
/**
* Custom UserDetails implementation that holds a reference to the MongoDB User entity.
* This allows access to the complete User object from the session without additional database queries.
*/
public class CustomUserPrincipal implements UserDetails {
private final User user; // MongoDB User entity
public CustomUserPrincipal(User user) {
this.user = user;
}
/**
* Get the complete MongoDB User entity
*/
public User getUser() {
return user;
}
@Override
public String getUsername() {
return user.getEmail();
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public boolean isEnabled() {
return user.getIsActivated() == 1;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Set<String> roles = user.getRoles();
if (roles != null && !roles.isEmpty()) {
return roles.stream()
.map(role -> new SimpleGrantedAuthority("ROLE_" + role))
.collect(Collectors.toList());
}
// Default role if no roles are set
return Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER"));
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
}

View File

@@ -9,8 +9,6 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import de.assecutor.votianlt.pages.login.ui.view.LoginView;
@EnableWebSecurity
@Configuration
public class SecurityConfig extends VaadinWebSecurity {

View File

@@ -1,6 +1,8 @@
package de.assecutor.votianlt.security;
import com.vaadin.flow.spring.security.AuthenticationContext;
import org.bson.types.ObjectId;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
@@ -25,8 +27,44 @@ public class SecurityService {
public String getCurrentUsername() {
return getAuthenticatedUser()
.map(UserDetails::getUsername)
.orElse("Anonymous");
.map(UserDetails::getUsername)
.orElse("Anonymous");
}
/**
* Get the complete MongoDB User entity from the session
*/
public de.assecutor.votianlt.model.User getCurrentDatabaseUser() {
return getAuthenticatedUser()
.filter(userDetails -> userDetails instanceof CustomUserPrincipal)
.map(userDetails -> ((CustomUserPrincipal) userDetails).getUser())
.orElseThrow(() -> new RuntimeException("No user logged in"));
}
/**
* Get the MongoDB User ID from the session
*/
public ObjectId getCurrentUserId() {
return getCurrentDatabaseUser().getId();
}
/**
* Get the usrId from the session
*/
public int getCurrentUsrId() {
return getCurrentDatabaseUser().getUsrId();
}
/**
* @deprecated Use getCurrentDatabaseUser() instead
*/
@Deprecated
public User getCurrentUser() {
if (getAuthenticatedUser().isPresent()) {
return (User) getAuthenticatedUser().get();
} else {
throw new RuntimeException("No user logged in");
}
}
public void logout() {

View File

@@ -1,7 +1,7 @@
package de.assecutor.votianlt.security;
import de.assecutor.votianlt.model.User;
import de.assecutor.votianlt.pages.register.service.UserService;
import de.assecutor.votianlt.pages.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
@@ -26,19 +26,9 @@ public class UserDetailsServiceImpl implements UserDetailsService {
throw new UsernameNotFoundException("User not found with email: " + email);
}
return new org.springframework.security.core.userdetails.User(
user.getEmail(),
user.getPassword(),
user.getIsActivated() == 1, // enabled
true, // accountNonExpired
true, // credentialsNonExpired
true, // accountNonLocked
getAuthorities(user)
);
// Use CustomUserPrincipal to store the complete User entity in the session
return new CustomUserPrincipal(user);
}
private Collection<? extends GrantedAuthority> getAuthorities(User user) {
// Basis-Rolle für alle Benutzer
return Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER"));
}
}