Erweiterungen
This commit is contained in:
@@ -99,7 +99,7 @@ public final class MainLayout extends AppLayout {
|
|||||||
|
|
||||||
// Create navigation items for the collapsible list
|
// Create navigation items for the collapsible list
|
||||||
SideNavItem profile = new SideNavItem("Mein Profil", "edit-profile", new Icon(VaadinIcon.USER));
|
SideNavItem profile = new SideNavItem("Mein Profil", "edit-profile", new Icon(VaadinIcon.USER));
|
||||||
SideNavItem myInvoices = new SideNavItem("Meine Rechnungen", "8", new Icon(VaadinIcon.COG));
|
SideNavItem myInvoices = new SideNavItem("Meine Rechnungen", "my-invoices", new Icon(VaadinIcon.FILE_TEXT));
|
||||||
SideNavItem imprint = new SideNavItem("Impressum", "impressum", new Icon(VaadinIcon.INFO_CIRCLE));
|
SideNavItem imprint = new SideNavItem("Impressum", "impressum", new Icon(VaadinIcon.INFO_CIRCLE));
|
||||||
|
|
||||||
userContent.add(profile, myInvoices, imprint);
|
userContent.add(profile, myInvoices, imprint);
|
||||||
|
|||||||
@@ -656,7 +656,7 @@ public class AddJobView extends Main {
|
|||||||
|
|
||||||
// Bind customerSelection field with validation
|
// Bind customerSelection field with validation
|
||||||
binder.forField(customerSelection)
|
binder.forField(customerSelection)
|
||||||
.asRequired("Auftraggeber/Rechnungsempfänger ist erforderlich")
|
.asRequired("")
|
||||||
.bind(Job::getCustomerSelection, Job::setCustomerSelection);
|
.bind(Job::getCustomerSelection, Job::setCustomerSelection);
|
||||||
|
|
||||||
// Bind optional fields without validation
|
// Bind optional fields without validation
|
||||||
|
|||||||
@@ -9,28 +9,47 @@ import com.vaadin.flow.component.html.Paragraph;
|
|||||||
import com.vaadin.flow.component.html.Span;
|
import com.vaadin.flow.component.html.Span;
|
||||||
import com.vaadin.flow.component.icon.Icon;
|
import com.vaadin.flow.component.icon.Icon;
|
||||||
import com.vaadin.flow.component.icon.VaadinIcon;
|
import com.vaadin.flow.component.icon.VaadinIcon;
|
||||||
|
import com.vaadin.flow.component.notification.Notification;
|
||||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
||||||
import com.vaadin.flow.component.textfield.EmailField;
|
import com.vaadin.flow.component.textfield.EmailField;
|
||||||
import com.vaadin.flow.component.textfield.TextField;
|
import com.vaadin.flow.component.textfield.TextField;
|
||||||
|
import com.vaadin.flow.data.binder.Binder;
|
||||||
|
import com.vaadin.flow.data.validator.EmailValidator;
|
||||||
import com.vaadin.flow.router.PageTitle;
|
import com.vaadin.flow.router.PageTitle;
|
||||||
import com.vaadin.flow.router.Route;
|
import com.vaadin.flow.router.Route;
|
||||||
|
import de.assecutor.votianlt.model.User;
|
||||||
|
import de.assecutor.votianlt.pages.service.UserService;
|
||||||
|
import de.assecutor.votianlt.security.SecurityService;
|
||||||
import jakarta.annotation.security.RolesAllowed;
|
import jakarta.annotation.security.RolesAllowed;
|
||||||
|
|
||||||
@PageTitle("Profil bearbeiten")
|
@PageTitle("Profil bearbeiten")
|
||||||
@Route(value = "edit-profile", layout = de.assecutor.votianlt.pages.base.ui.view.MainLayout.class)
|
@Route(value = "edit-profile", layout = de.assecutor.votianlt.pages.base.ui.view.MainLayout.class)
|
||||||
@RolesAllowed({"USER","ADMIN"})
|
@RolesAllowed({"USER","ADMIN"})
|
||||||
public class EditProfileView extends HorizontalLayout {
|
public class EditProfileView extends HorizontalLayout {
|
||||||
public EditProfileView() {
|
private final UserService userService;
|
||||||
|
private final SecurityService securityService;
|
||||||
|
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();
|
setSizeFull();
|
||||||
setPadding(true);
|
setPadding(true);
|
||||||
setSpacing(true);
|
setSpacing(true);
|
||||||
setJustifyContentMode(JustifyContentMode.CENTER);
|
setJustifyContentMode(JustifyContentMode.CENTER);
|
||||||
|
// 2% Abstand zwischen den Spalten
|
||||||
|
setWidthFull();
|
||||||
|
setSpacing(false);
|
||||||
|
getStyle().set("column-gap", "2%");
|
||||||
|
|
||||||
setAlignItems(Alignment.START);
|
setAlignItems(Alignment.START);
|
||||||
|
|
||||||
// Linke Spalte: Formular
|
// Linke Spalte: Formular
|
||||||
VerticalLayout formColumn = new VerticalLayout();
|
VerticalLayout formColumn = new VerticalLayout();
|
||||||
formColumn.setWidth("500px");
|
formColumn.setWidth("48%");
|
||||||
formColumn.setPadding(false);
|
formColumn.setPadding(false);
|
||||||
formColumn.setSpacing(false);
|
formColumn.setSpacing(false);
|
||||||
|
|
||||||
@@ -39,19 +58,19 @@ public class EditProfileView extends HorizontalLayout {
|
|||||||
form.setResponsiveSteps(new FormLayout.ResponsiveStep("0", 2));
|
form.setResponsiveSteps(new FormLayout.ResponsiveStep("0", 2));
|
||||||
|
|
||||||
// Firmenfelder
|
// Firmenfelder
|
||||||
TextField companyField = new TextField("Firma*");
|
TextField companyField = new TextField("Firma");
|
||||||
TextField companyAddField = new TextField("Firmenzusatz");
|
TextField companyAddField = new TextField("Firmenzusatz");
|
||||||
TextField firstnameField = new TextField("Vorname*");
|
TextField firstnameField = new TextField("Vorname");
|
||||||
TextField lastnameField = new TextField("Nachname*");
|
TextField lastnameField = new TextField("Nachname");
|
||||||
TextField phoneField = new TextField("Telefonnummer*");
|
TextField phoneField = new TextField("Telefonnummer");
|
||||||
TextField faxField = new TextField("Telefon (Fax)");
|
TextField faxField = new TextField("Telefon (Fax)");
|
||||||
TextField mobileField = new TextField("Telefon (Mobil)");
|
TextField mobileField = new TextField("Telefon (Mobil)");
|
||||||
EmailField emailField = new EmailField("E-Mail-Adresse (Login)*");
|
EmailField emailField = new EmailField("E-Mail-Adresse (Login)*");
|
||||||
TextField streetField = new TextField("Straße*");
|
TextField streetField = new TextField("Straße");
|
||||||
TextField houseNumberField = new TextField("Hausnr*");
|
TextField houseNumberField = new TextField("Hausnr");
|
||||||
TextField addressAddField = new TextField("Adresszusatz");
|
TextField addressAddField = new TextField("Adresszusatz");
|
||||||
TextField zipField = new TextField("Postleitzahl*");
|
TextField zipField = new TextField("Postleitzahl");
|
||||||
TextField cityField = new TextField("Stadt*");
|
TextField cityField = new TextField("Stadt");
|
||||||
|
|
||||||
// Pflichtfeldhinweis
|
// Pflichtfeldhinweis
|
||||||
Paragraph pflichtHinweis = new Paragraph("Die mit (*) gekennzeichneten Felder sind Pflichtfelder.");
|
Paragraph pflichtHinweis = new Paragraph("Die mit (*) gekennzeichneten Felder sind Pflichtfelder.");
|
||||||
@@ -61,15 +80,15 @@ public class EditProfileView extends HorizontalLayout {
|
|||||||
Checkbox diffInvoiceAddress = new Checkbox("Abweichende Rechnungsadresse");
|
Checkbox diffInvoiceAddress = new Checkbox("Abweichende Rechnungsadresse");
|
||||||
diffInvoiceAddress.getStyle().set("marginTop", "1em");
|
diffInvoiceAddress.getStyle().set("marginTop", "1em");
|
||||||
// Rechnungsadresse Felder (disabled by default)
|
// Rechnungsadresse Felder (disabled by default)
|
||||||
TextField invCompanyField = new TextField("Firma*");
|
TextField invCompanyField = new TextField("Firma");
|
||||||
TextField invCompanyAddField = new TextField("Firmenzusatz");
|
TextField invCompanyAddField = new TextField("Firmenzusatz");
|
||||||
TextField invFirstnameField = new TextField("Vorname*");
|
TextField invFirstnameField = new TextField("Vorname");
|
||||||
TextField invLastnameField = new TextField("Nachname*");
|
TextField invLastnameField = new TextField("Nachname");
|
||||||
TextField invStreetField = new TextField("Straße*");
|
TextField invStreetField = new TextField("Straße");
|
||||||
TextField invHouseNumberField = new TextField("Hausnr*");
|
TextField invHouseNumberField = new TextField("Hausnr");
|
||||||
TextField invAddressAddField = new TextField("Adresszusatz");
|
TextField invAddressAddField = new TextField("Adresszusatz");
|
||||||
TextField invZipField = new TextField("Postleitzahl*");
|
TextField invZipField = new TextField("Postleitzahl");
|
||||||
TextField invCityField = new TextField("Stadt*");
|
TextField invCityField = new TextField("Stadt");
|
||||||
invCompanyField.setEnabled(false);
|
invCompanyField.setEnabled(false);
|
||||||
invCompanyAddField.setEnabled(false);
|
invCompanyAddField.setEnabled(false);
|
||||||
invFirstnameField.setEnabled(false);
|
invFirstnameField.setEnabled(false);
|
||||||
@@ -79,6 +98,17 @@ public class EditProfileView extends HorizontalLayout {
|
|||||||
invAddressAddField.setEnabled(false);
|
invAddressAddField.setEnabled(false);
|
||||||
invZipField.setEnabled(false);
|
invZipField.setEnabled(false);
|
||||||
invCityField.setEnabled(false);
|
invCityField.setEnabled(false);
|
||||||
|
// Felder initial ausblenden
|
||||||
|
invCompanyField.setVisible(false);
|
||||||
|
invCompanyAddField.setVisible(false);
|
||||||
|
invFirstnameField.setVisible(false);
|
||||||
|
invLastnameField.setVisible(false);
|
||||||
|
invStreetField.setVisible(false);
|
||||||
|
invHouseNumberField.setVisible(false);
|
||||||
|
invAddressAddField.setVisible(false);
|
||||||
|
invZipField.setVisible(false);
|
||||||
|
invCityField.setVisible(false);
|
||||||
|
|
||||||
diffInvoiceAddress.addValueChangeListener(e -> {
|
diffInvoiceAddress.addValueChangeListener(e -> {
|
||||||
boolean enabled = e.getValue();
|
boolean enabled = e.getValue();
|
||||||
invCompanyField.setEnabled(enabled);
|
invCompanyField.setEnabled(enabled);
|
||||||
@@ -88,6 +118,17 @@ public class EditProfileView extends HorizontalLayout {
|
|||||||
invStreetField.setEnabled(enabled);
|
invStreetField.setEnabled(enabled);
|
||||||
invHouseNumberField.setEnabled(enabled);
|
invHouseNumberField.setEnabled(enabled);
|
||||||
invAddressAddField.setEnabled(enabled);
|
invAddressAddField.setEnabled(enabled);
|
||||||
|
// Sichtbarkeit an Checkbox koppeln
|
||||||
|
invCompanyField.setVisible(enabled);
|
||||||
|
invCompanyAddField.setVisible(enabled);
|
||||||
|
invFirstnameField.setVisible(enabled);
|
||||||
|
invLastnameField.setVisible(enabled);
|
||||||
|
invStreetField.setVisible(enabled);
|
||||||
|
invHouseNumberField.setVisible(enabled);
|
||||||
|
invAddressAddField.setVisible(enabled);
|
||||||
|
invZipField.setVisible(enabled);
|
||||||
|
invCityField.setVisible(enabled);
|
||||||
|
|
||||||
invZipField.setEnabled(enabled);
|
invZipField.setEnabled(enabled);
|
||||||
invCityField.setEnabled(enabled);
|
invCityField.setEnabled(enabled);
|
||||||
});
|
});
|
||||||
@@ -99,6 +140,54 @@ public class EditProfileView extends HorizontalLayout {
|
|||||||
form.add(phoneField, faxField);
|
form.add(phoneField, faxField);
|
||||||
form.add(mobileField, 2);
|
form.add(mobileField, 2);
|
||||||
form.add(emailField, 2);
|
form.add(emailField, 2);
|
||||||
|
// Binder: Pflichtfelder und Bindings
|
||||||
|
// UI-Pflichtfelder ohne Datenbindung (da nicht im User-Modell vorhanden)
|
||||||
|
companyField.setRequiredIndicatorVisible(true);
|
||||||
|
streetField.setRequiredIndicatorVisible(true);
|
||||||
|
houseNumberField.setRequiredIndicatorVisible(true);
|
||||||
|
zipField.setRequiredIndicatorVisible(true);
|
||||||
|
cityField.setRequiredIndicatorVisible(true);
|
||||||
|
|
||||||
|
binder.forField(companyField)
|
||||||
|
.asRequired("")
|
||||||
|
.bind(user -> null, (user, v) -> {});
|
||||||
|
binder.forField(streetField)
|
||||||
|
.asRequired("")
|
||||||
|
.bind(user -> null, (user, v) -> {});
|
||||||
|
binder.forField(houseNumberField)
|
||||||
|
.asRequired("")
|
||||||
|
.bind(user -> null, (user, v) -> {});
|
||||||
|
binder.forField(zipField)
|
||||||
|
.asRequired("")
|
||||||
|
.bind(user -> null, (user, v) -> {});
|
||||||
|
binder.forField(cityField)
|
||||||
|
.asRequired("")
|
||||||
|
.bind(user -> null, (user, v) -> {});
|
||||||
|
|
||||||
|
binder.forField(firstnameField)
|
||||||
|
.asRequired("")
|
||||||
|
.bind(User::getFirstname, User::setFirstname);
|
||||||
|
binder.forField(lastnameField)
|
||||||
|
.asRequired("")
|
||||||
|
.bind(User::getName, User::setName);
|
||||||
|
binder.forField(phoneField)
|
||||||
|
.asRequired("")
|
||||||
|
.bind(User::getPhone, User::setPhone);
|
||||||
|
binder.forField(emailField)
|
||||||
|
.asRequired("")
|
||||||
|
.withValidator(new EmailValidator("Ungültige E-Mail-Adresse"))
|
||||||
|
.bind(User::getEmail, User::setEmail);
|
||||||
|
// Optionale Felder
|
||||||
|
binder.forField(mobileField).bind(User::getPhone2, User::setPhone2);
|
||||||
|
binder.forField(faxField).bind(User::getFax, User::setFax);
|
||||||
|
// Pflichtindikator sichtbar machen
|
||||||
|
firstnameField.setRequiredIndicatorVisible(true);
|
||||||
|
lastnameField.setRequiredIndicatorVisible(true);
|
||||||
|
phoneField.setRequiredIndicatorVisible(true);
|
||||||
|
emailField.setRequiredIndicatorVisible(true);
|
||||||
|
// Aktuellen Benutzer in die Felder laden
|
||||||
|
binder.readBean(currentUser);
|
||||||
|
|
||||||
form.add(streetField, houseNumberField);
|
form.add(streetField, houseNumberField);
|
||||||
form.add(addressAddField, 2);
|
form.add(addressAddField, 2);
|
||||||
form.add(zipField, cityField);
|
form.add(zipField, cityField);
|
||||||
@@ -120,7 +209,7 @@ public class EditProfileView extends HorizontalLayout {
|
|||||||
|
|
||||||
// Rechte Spalte: Karte und Aktionen
|
// Rechte Spalte: Karte und Aktionen
|
||||||
VerticalLayout rightColumn = new VerticalLayout();
|
VerticalLayout rightColumn = new VerticalLayout();
|
||||||
rightColumn.setWidth("100%");
|
rightColumn.setWidth("48%");
|
||||||
rightColumn.setAlignItems(Alignment.CENTER);
|
rightColumn.setAlignItems(Alignment.CENTER);
|
||||||
rightColumn.setSpacing(true);
|
rightColumn.setSpacing(true);
|
||||||
rightColumn.setPadding(false);
|
rightColumn.setPadding(false);
|
||||||
@@ -160,6 +249,18 @@ public class EditProfileView extends HorizontalLayout {
|
|||||||
// Profil speichern Button (unten rechts)
|
// Profil speichern Button (unten rechts)
|
||||||
Button saveProfile = new Button("Profiländerungen speichern");
|
Button saveProfile = new Button("Profiländerungen speichern");
|
||||||
saveProfile.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
saveProfile.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
||||||
|
saveProfile.addClickListener(e -> {
|
||||||
|
if (binder.validate().isOk()) {
|
||||||
|
try {
|
||||||
|
binder.writeBean(currentUser);
|
||||||
|
userService.save(currentUser);
|
||||||
|
Notification.show("Profil gespeichert", 3000, Notification.Position.BOTTOM_END);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Notification.show("Fehler beim Speichern: " + ex.getMessage(), 4000, Notification.Position.MIDDLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
saveProfile.getStyle().set("position", "absolute").set("right", "2em").set("bottom", "2em");
|
saveProfile.getStyle().set("position", "absolute").set("right", "2em").set("bottom", "2em");
|
||||||
add(formColumn, rightColumn, saveProfile);
|
add(formColumn, rightColumn, saveProfile);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,189 @@
|
|||||||
|
package de.assecutor.votianlt.pages.view;
|
||||||
|
|
||||||
|
import com.vaadin.flow.component.Component;
|
||||||
|
import com.vaadin.flow.component.button.Button;
|
||||||
|
import com.vaadin.flow.component.grid.Grid;
|
||||||
|
import com.vaadin.flow.component.html.*;
|
||||||
|
import com.vaadin.flow.component.icon.VaadinIcon;
|
||||||
|
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.select.Select;
|
||||||
|
import com.vaadin.flow.component.textfield.TextField;
|
||||||
|
import com.vaadin.flow.data.value.ValueChangeMode;
|
||||||
|
import com.vaadin.flow.router.PageTitle;
|
||||||
|
import com.vaadin.flow.router.Route;
|
||||||
|
import de.assecutor.votianlt.pages.base.ui.component.ViewToolbar;
|
||||||
|
import de.assecutor.votianlt.pages.base.ui.view.MainLayout;
|
||||||
|
import jakarta.annotation.security.RolesAllowed;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Meine Rechnungen – nutzerzentrierte Übersicht.
|
||||||
|
*
|
||||||
|
* Layout orientiert am bereitgestellten Screenshot:
|
||||||
|
* - Zwei Karten oben (Offene Rechnungen, Bankverbindung)
|
||||||
|
* - Darunter ein Bereich „Rechnungen" mit Grid, Suche und Seitengröße
|
||||||
|
*/
|
||||||
|
@PageTitle("Meine Rechnungen")
|
||||||
|
@Route(value = "my-invoices", layout = MainLayout.class)
|
||||||
|
@RolesAllowed("USER")
|
||||||
|
public class MyInvoicesView extends Main {
|
||||||
|
|
||||||
|
private final Grid<MyInvoiceRow> grid = new Grid<>(MyInvoiceRow.class, false);
|
||||||
|
private final List<MyInvoiceRow> allRows = new ArrayList<>(); // zunächst leer
|
||||||
|
|
||||||
|
public MyInvoicesView() {
|
||||||
|
getStyle().set("max-width", "90%");
|
||||||
|
getStyle().set("margin-left", "auto");
|
||||||
|
getStyle().set("margin-right", "auto");
|
||||||
|
|
||||||
|
// Toolbar / Titel
|
||||||
|
add(new ViewToolbar("Meine Rechnungen"));
|
||||||
|
|
||||||
|
// Kartenbereich oben
|
||||||
|
add(createTopCards());
|
||||||
|
|
||||||
|
// Rechnungsbereich unten
|
||||||
|
add(createInvoicesSection());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Component createTopCards() {
|
||||||
|
// Container mit zwei Spalten (responsiv)
|
||||||
|
Div container = new Div();
|
||||||
|
container.getStyle()
|
||||||
|
.set("display", "grid")
|
||||||
|
.set("grid-template-columns", "48% 2% 48%");
|
||||||
|
//.set("gap", "10px");
|
||||||
|
// Spaltenabstände: 2% zwischen den beiden Spalten
|
||||||
|
container.getStyle().set("column-gap", "0");
|
||||||
|
|
||||||
|
|
||||||
|
// Karte: Offene Rechnungen
|
||||||
|
Paragraph hint = new Paragraph("Momentan sind keine neuen Rechnungen für Sie im System gespeichert.");
|
||||||
|
hint.getStyle().set("color", "var(--lumo-success-text-color)");
|
||||||
|
Div openInvoicesCard = createCard("Offene Rechnungen", hint);
|
||||||
|
|
||||||
|
// Karte: Bankverbindung
|
||||||
|
VerticalLayout bankData = new VerticalLayout();
|
||||||
|
bankData.setPadding(false);
|
||||||
|
bankData.setSpacing(false);
|
||||||
|
bankData.add(
|
||||||
|
labeledValue("Kreditinstitut", "Hamburger Sparkasse"),
|
||||||
|
labeledValue("Begünstigter", "Assecutor Data Service GmbH"),
|
||||||
|
labeledValue("IBAN", "DE67200505501217139888"),
|
||||||
|
labeledValue("Verwendungszweck", "vlt-00000610")
|
||||||
|
);
|
||||||
|
Div bankCard = createCard("Bankverbindung", bankData);
|
||||||
|
|
||||||
|
container.add(openInvoicesCard, bankCard);
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Component createInvoicesSection() {
|
||||||
|
Div card = new Div();
|
||||||
|
// Abstand zur oberen Zeile
|
||||||
|
card.getStyle().set("margin-top", "30px");
|
||||||
|
|
||||||
|
styleCard(card);
|
||||||
|
|
||||||
|
// Kopfzeile
|
||||||
|
H3 title = new H3("Rechnungen");
|
||||||
|
title.getStyle().set("margin", "0");
|
||||||
|
|
||||||
|
// Steuerleiste: Seitengröße + Suche
|
||||||
|
Select<Integer> pageSize = new Select<>();
|
||||||
|
pageSize.setItems(10, 25, 50);
|
||||||
|
pageSize.setLabel("Einträge anzeigen");
|
||||||
|
pageSize.setValue(10);
|
||||||
|
pageSize.setWidth("140px");
|
||||||
|
|
||||||
|
TextField search = new TextField();
|
||||||
|
search.setLabel("Suchen");
|
||||||
|
search.setClearButtonVisible(true);
|
||||||
|
search.setValueChangeMode(ValueChangeMode.EAGER);
|
||||||
|
|
||||||
|
// Layout Kopf + Controls
|
||||||
|
HorizontalLayout header = new HorizontalLayout();
|
||||||
|
header.setWidthFull();
|
||||||
|
header.setDefaultVerticalComponentAlignment(FlexComponent.Alignment.END);
|
||||||
|
header.setJustifyContentMode(FlexComponent.JustifyContentMode.BETWEEN);
|
||||||
|
header.add(title, new Div()); // Platzhalter zum Strecken
|
||||||
|
|
||||||
|
HorizontalLayout controls = new HorizontalLayout(pageSize, search);
|
||||||
|
controls.setWidthFull();
|
||||||
|
controls.setJustifyContentMode(FlexComponent.JustifyContentMode.BETWEEN);
|
||||||
|
|
||||||
|
// Grid konfigurieren
|
||||||
|
grid.addColumn(MyInvoiceRow::status).setHeader("Status").setAutoWidth(true);
|
||||||
|
grid.addColumn(MyInvoiceRow::invoiceNumber).setHeader("Rechnungsnummer").setAutoWidth(true);
|
||||||
|
grid.addColumn(MyInvoiceRow::date).setHeader("Datum").setAutoWidth(true);
|
||||||
|
grid.addColumn(MyInvoiceRow::amount).setHeader("Betrag").setAutoWidth(true);
|
||||||
|
grid.setAllRowsVisible(true);
|
||||||
|
grid.setItems(allRows); // zunächst leer
|
||||||
|
|
||||||
|
// Suche (einfacher Text-Filter über alle sichtbaren Felder)
|
||||||
|
search.addValueChangeListener(e -> applyFilter(e.getValue()));
|
||||||
|
|
||||||
|
// Paginierungs-Buttons (vorerst ohne Funktion, als Platzhalter)
|
||||||
|
Button prev = new Button("Zurück", VaadinIcon.ANGLE_LEFT.create());
|
||||||
|
Button next = new Button("Nächste", VaadinIcon.ANGLE_RIGHT.create());
|
||||||
|
prev.setEnabled(false);
|
||||||
|
next.setEnabled(false);
|
||||||
|
HorizontalLayout pager = new HorizontalLayout(prev, next);
|
||||||
|
pager.setWidthFull();
|
||||||
|
pager.setJustifyContentMode(FlexComponent.JustifyContentMode.END);
|
||||||
|
|
||||||
|
// Zusammenbauen
|
||||||
|
card.add(header, controls, grid, pager);
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyFilter(String filter) {
|
||||||
|
String f = filter == null ? "" : filter.toLowerCase();
|
||||||
|
grid.setItems(allRows.stream().filter(row ->
|
||||||
|
row.status.toLowerCase().contains(f)
|
||||||
|
|| row.invoiceNumber.toLowerCase().contains(f)
|
||||||
|
|| row.date.toString().toLowerCase().contains(f)
|
||||||
|
|| String.valueOf(row.amount).toLowerCase().contains(f)
|
||||||
|
).toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Div createCard(String title, Component content) {
|
||||||
|
Div card = new Div();
|
||||||
|
styleCard(card);
|
||||||
|
H3 h3 = new H3(title);
|
||||||
|
h3.getStyle().set("margin", "0");
|
||||||
|
Div inner = new Div(content);
|
||||||
|
inner.getStyle().set("padding", "var(--lumo-space-s)");
|
||||||
|
card.add(h3, inner);
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Component labeledValue(String label, String value) {
|
||||||
|
Paragraph p = new Paragraph();
|
||||||
|
Span sLabel = new Span(label + " ");
|
||||||
|
sLabel.getStyle().set("font-weight", "600");
|
||||||
|
Span sValue = new Span(value);
|
||||||
|
p.add(sLabel, sValue);
|
||||||
|
p.getStyle().set("margin", "0");
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void styleCard(Div card) {
|
||||||
|
card.getStyle()
|
||||||
|
.set("border", "1px solid var(--lumo-contrast-20pct)")
|
||||||
|
.set("border-radius", "var(--lumo-border-radius-l)")
|
||||||
|
.set("padding", "var(--lumo-space-m)")
|
||||||
|
.set("background", "var(--lumo-base-color)")
|
||||||
|
.set("box-shadow", "0 1px 1px rgba(0,0,0,0.02)");
|
||||||
|
card.setWidthFull();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schlanke lokale Repräsentation für das Grid
|
||||||
|
public record MyInvoiceRow(String status, String invoiceNumber, LocalDate date, double amount) {}
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user