Erweiterungen

This commit is contained in:
2025-08-14 09:43:25 +02:00
parent 3eb6343084
commit 248b71aab9
8 changed files with 307 additions and 30 deletions

View File

@@ -21,7 +21,7 @@ import java.time.Clock;
@Route(value = "add_company", layout = de.assecutor.votianlt.pages.base.ui.view.MainLayout.class) @Route(value = "add_company", layout = de.assecutor.votianlt.pages.base.ui.view.MainLayout.class)
@PageTitle("Neuen Firma anlegen") @PageTitle("Neuen Firma anlegen")
@Menu(order = 0, icon = "vaadin:clipboard-check", title = "Neue Firma anlegen") //@Menu(order = 0, icon = "vaadin:clipboard-check", title = "Neue Firma anlegen")
@RolesAllowed("USER") @RolesAllowed("USER")
public class AddCompanyView extends Main { public class AddCompanyView extends Main {
private final AddCompanyService addCompanyService; private final AddCompanyService addCompanyService;

View File

@@ -21,7 +21,7 @@ import java.time.Clock;
@Route(value = "add_customer", layout = de.assecutor.votianlt.pages.base.ui.view.MainLayout.class) @Route(value = "add_customer", layout = de.assecutor.votianlt.pages.base.ui.view.MainLayout.class)
@PageTitle("Neuen Kunden anlegen") @PageTitle("Neuen Kunden anlegen")
@Menu(order = 0, icon = "vaadin:clipboard-check", title = "Neuen Kunden anlegen") //@Menu(order = 0, icon = "vaadin:clipboard-check", title = "Neuen Kunden anlegen")
@RolesAllowed("USER") @RolesAllowed("USER")
public class AddCustomerView extends Main { public class AddCustomerView extends Main {
private final AddCustomerService addCustomerService; private final AddCustomerService addCustomerService;

View File

@@ -42,7 +42,7 @@ import java.util.Optional;
@Route(value = "add_job", layout = de.assecutor.votianlt.pages.base.ui.view.MainLayout.class) @Route(value = "add_job", layout = de.assecutor.votianlt.pages.base.ui.view.MainLayout.class)
@PageTitle("Neuen Auftrag anlegen") @PageTitle("Neuen Auftrag anlegen")
@Menu(order = 0, icon = "vaadin:clipboard-check", title = "Neuen Auftrag anlegen") @Menu(order = 0, icon = "vaadin:clipboard-check", title = "Auftragserstellung")
@RolesAllowed("USER") @RolesAllowed("USER")
@Slf4j @Slf4j
public class AddJobView extends Main { public class AddJobView extends Main {

View File

@@ -4,6 +4,7 @@ import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.applayout.AppLayout; import com.vaadin.flow.component.applayout.AppLayout;
import com.vaadin.flow.component.avatar.Avatar; import com.vaadin.flow.component.avatar.Avatar;
import com.vaadin.flow.component.avatar.AvatarVariant; import com.vaadin.flow.component.avatar.AvatarVariant;
import com.vaadin.flow.component.details.Details;
import com.vaadin.flow.component.html.Div; import com.vaadin.flow.component.html.Div;
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;
@@ -11,6 +12,7 @@ import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.menubar.MenuBar; import com.vaadin.flow.component.menubar.MenuBar;
import com.vaadin.flow.component.menubar.MenuBarVariant; import com.vaadin.flow.component.menubar.MenuBarVariant;
import com.vaadin.flow.component.orderedlayout.Scroller; import com.vaadin.flow.component.orderedlayout.Scroller;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.sidenav.SideNav; import com.vaadin.flow.component.sidenav.SideNav;
import com.vaadin.flow.component.sidenav.SideNavItem; import com.vaadin.flow.component.sidenav.SideNavItem;
import com.vaadin.flow.router.Layout; import com.vaadin.flow.router.Layout;
@@ -20,6 +22,7 @@ import de.assecutor.votianlt.security.SecurityService;
import static com.vaadin.flow.theme.lumo.LumoUtility.*; import static com.vaadin.flow.theme.lumo.LumoUtility.*;
@Layout
public final class MainLayout extends AppLayout { public final class MainLayout extends AppLayout {
private final SecurityService securityService; private final SecurityService securityService;
@@ -45,11 +48,64 @@ public final class MainLayout extends AppLayout {
return header; return header;
} }
private SideNav createSideNav() { private Component createSideNav() {
var nav = new SideNav(); var nav = new SideNav();
nav.addClassNames(Margin.Horizontal.MEDIUM); nav.addClassNames(Margin.Horizontal.MEDIUM);
MenuConfiguration.getMenuEntries().forEach(entry -> nav.addItem(createSideNavItem(entry)));
return nav; MenuConfiguration.getMenuEntries().forEach(entry -> {
// Skip "Verwaltung" entry as we'll handle it separately with Details component
if (!"Verwaltung".equals(entry.title())) {
SideNavItem item = createSideNavItem(entry);
nav.addItem(item);
}
});
// Create Details component for "Verwaltung" with collapsible list
Details verwaltungDetails = new Details();
verwaltungDetails.setSummaryText("Verwaltung");
verwaltungDetails.addClassNames(Margin.Horizontal.MEDIUM, FontSize.MEDIUM, FontWeight.MEDIUM, "#000000");
// Create collapsible content with navigation items
VerticalLayout verwaltungContent = new VerticalLayout();
verwaltungContent.setPadding(false);
verwaltungContent.setSpacing(true);
// Create navigation items for the collapsible list
SideNavItem jobs = new SideNavItem("Aufträge", "1", new Icon(VaadinIcon.COG));
SideNavItem customers = new SideNavItem("Kunden", "2", new Icon(VaadinIcon.COG));
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));
SideNavItem statistics = new SideNavItem("Statistik", "6", new Icon(VaadinIcon.COG));
verwaltungContent.add(jobs, customers, appUsers, devices, invoices, statistics);
verwaltungDetails.setContent(verwaltungContent);
// Create Details component for "Verwaltung" with collapsible list
Details userDetails = new Details();
userDetails.setSummaryText("Benutzer");
userDetails.addClassNames(Margin.Horizontal.MEDIUM, FontSize.MEDIUM, FontWeight.MEDIUM, TextColor.BODY);
// Create collapsible content with navigation items
VerticalLayout userContent = new VerticalLayout();
userContent.setPadding(false);
userContent.setSpacing(true);
// Create navigation items for the collapsible list
SideNavItem profile = new SideNavItem("Mein Profil", "7", new Icon(VaadinIcon.COG));
SideNavItem myInvoices = new SideNavItem("Meine Rechnungen", "8", new Icon(VaadinIcon.COG));
SideNavItem imprint = new SideNavItem("Impressum", "9", new Icon(VaadinIcon.COG));
userContent.add(profile, myInvoices, imprint);
userDetails.setContent(userContent);
// Create a vertical layout to hold both regular menu items and collapsible sections
VerticalLayout navContainer = new VerticalLayout();
navContainer.setPadding(false);
navContainer.setSpacing(false);
navContainer.add(nav, verwaltungDetails, userDetails);
return navContainer;
} }
private SideNavItem createSideNavItem(MenuEntry menuEntry) { private SideNavItem createSideNavItem(MenuEntry menuEntry) {

View File

@@ -0,0 +1,189 @@
package de.assecutor.votianlt.pages.start.ui.view;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.html.*;
import com.vaadin.flow.component.icon.Icon;
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.router.PageTitle;
import com.vaadin.flow.router.Route;
import de.assecutor.votianlt.security.SecurityService;
import de.assecutor.votianlt.pages.base.ui.view.MainLayout;
import jakarta.annotation.security.RolesAllowed;
@Route(value = "dashboard", layout = MainLayout.class)
@PageTitle("VotianLT - Dashboard")
@RolesAllowed("USER")
public class AuthenticatedStartView extends VerticalLayout {
private final SecurityService securityService;
public AuthenticatedStartView(SecurityService securityService) {
this.securityService = securityService;
setSizeFull();
setPadding(false);
setSpacing(false);
setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.STRETCH);
// Hero Section for authenticated users
add(createAuthenticatedHeroSection());
// System Section
add(createSystemSection());
// App Section
add(createAppSection());
// Footer
add(createFooter());
}
private Component createAuthenticatedHeroSection() {
VerticalLayout heroSection = new VerticalLayout();
heroSection.setWidthFull();
heroSection.setPadding(true);
heroSection.setSpacing(true);
heroSection.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.CENTER);
heroSection.getStyle().set("background", "linear-gradient(135deg, var(--lumo-primary-color-10pct), var(--lumo-primary-color-50pct))");
heroSection.getStyle().set("min-height", "300px");
heroSection.setJustifyContentMode(FlexComponent.JustifyContentMode.CENTER);
// Welcome message for authenticated user
String currentUser = securityService.getCurrentUsername();
H1 welcomeTitle = new H1("Willkommen zurück, " + currentUser + "!");
welcomeTitle.getStyle().set("text-align", "center");
welcomeTitle.getStyle().set("color", "var(--lumo-primary-text-color)");
welcomeTitle.getStyle().set("margin-bottom", "var(--lumo-space-l)");
Paragraph welcomeDescription = new Paragraph(
"Nutzen Sie die Navigation links, um neue Aufträge zu erstellen oder Ihre Verwaltung zu bearbeiten."
);
welcomeDescription.getStyle().set("text-align", "center");
welcomeDescription.getStyle().set("max-width", "600px");
welcomeDescription.getStyle().set("font-size", "var(--lumo-font-size-l)");
heroSection.add(welcomeTitle, welcomeDescription);
return heroSection;
}
private Component createSystemSection() {
VerticalLayout systemSection = new VerticalLayout();
systemSection.setWidthFull();
systemSection.setPadding(true);
systemSection.setSpacing(true);
systemSection.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.CENTER);
systemSection.getStyle().set("background-color", "var(--lumo-base-color)");
// Section Header
H2 systemTitle = new H2("Das System");
systemTitle.getStyle().set("color", "var(--lumo-primary-color)");
systemTitle.getStyle().set("text-align", "center");
Paragraph systemIntro = new Paragraph(
"Für Solo-Selbstständige und Kleinunternehmer im Transportgewerbe ist von entscheidender Bedeutung, " +
"dass sie sich in erster Linie auf ihr eigentliches Geschäft konzentrieren können: Kunden gewinnen und Waren von A nach B liefern."
);
systemIntro.getStyle().set("text-align", "center");
systemIntro.getStyle().set("max-width", "800px");
systemIntro.getStyle().set("margin-bottom", "var(--lumo-space-xl)");
// Features Grid
HorizontalLayout featuresGrid = new HorizontalLayout();
featuresGrid.setWidthFull();
featuresGrid.setSpacing(true);
featuresGrid.setDefaultVerticalComponentAlignment(FlexComponent.Alignment.START);
// Feature Cards
featuresGrid.add(
createFeatureCard(VaadinIcon.COG, "Einrichtungsassistent",
"Mithilfe des Einrichtungsassistenten haben Sie die Möglichkeit, Ihr Nutzerprofil zu vervollständigen."),
createFeatureCard(VaadinIcon.USERS, "Kunden- und Auftragsverwaltung",
"Mit der Kunden- und Auftragsverwaltung haben Sie alle Kontaktdaten und Auftragsdetails stets im Blick."),
createFeatureCard(VaadinIcon.CLIPBOARD_TEXT, "Auftragserstellung",
"Stellen Sie mit wenigen Mausklicks Aufträge ins System ein und legen Sie fest, welcher Mitarbeiter welchen Transportauftrag abarbeiten soll.")
);
systemSection.add(systemTitle, systemIntro, featuresGrid);
return systemSection;
}
private Component createFeatureCard(VaadinIcon iconType, String title, String description) {
VerticalLayout card = new VerticalLayout();
card.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.CENTER);
card.setPadding(true);
card.setSpacing(true);
card.getStyle().set("background-color", "var(--lumo-base-color)");
card.getStyle().set("border", "1px solid var(--lumo-contrast-20pct)");
card.getStyle().set("border-radius", "var(--lumo-border-radius-m)");
card.getStyle().set("box-shadow", "var(--lumo-box-shadow-s)");
card.setWidth("300px");
Icon icon = iconType.create();
icon.setSize("48px");
icon.getStyle().set("color", "var(--lumo-primary-color)");
icon.getStyle().set("margin-bottom", "var(--lumo-space-m)");
H3 cardTitle = new H3(title);
cardTitle.getStyle().set("text-align", "center");
cardTitle.getStyle().set("margin", "0");
cardTitle.getStyle().set("color", "var(--lumo-primary-text-color)");
Paragraph cardDescription = new Paragraph(description);
cardDescription.getStyle().set("text-align", "center");
cardDescription.getStyle().set("font-size", "var(--lumo-font-size-s)");
cardDescription.getStyle().set("color", "var(--lumo-secondary-text-color)");
cardDescription.getStyle().set("margin", "0");
card.add(icon, cardTitle, cardDescription);
return card;
}
private Component createAppSection() {
VerticalLayout appSection = new VerticalLayout();
appSection.setWidthFull();
appSection.setPadding(true);
appSection.setSpacing(true);
appSection.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.CENTER);
appSection.getStyle().set("background-color", "var(--lumo-contrast-5pct)");
H2 appTitle = new H2("Die App");
appTitle.getStyle().set("color", "var(--lumo-primary-color)");
appTitle.getStyle().set("text-align", "center");
Paragraph appDescription = new Paragraph(
"Mit unserer mobilen App bleiben Sie auch unterwegs immer über Ihre Aufträge informiert " +
"und können wichtige Aufgaben direkt vom Smartphone aus erledigen."
);
appDescription.getStyle().set("text-align", "center");
appDescription.getStyle().set("max-width", "600px");
appSection.add(appTitle, appDescription);
return appSection;
}
private Component createFooter() {
VerticalLayout footer = new VerticalLayout();
footer.setWidthFull();
footer.setPadding(true);
footer.setSpacing(true);
footer.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.CENTER);
footer.getStyle().set("background-color", "var(--lumo-contrast-10pct)");
footer.getStyle().set("border-top", "1px solid var(--lumo-contrast-20pct)");
HorizontalLayout footerContent = new HorizontalLayout();
footerContent.setDefaultVerticalComponentAlignment(FlexComponent.Alignment.CENTER);
footerContent.setJustifyContentMode(FlexComponent.JustifyContentMode.CENTER);
Paragraph copyright = new Paragraph("© 2024 VotianLT. Alle Rechte vorbehalten.");
copyright.getStyle().set("color", "var(--lumo-secondary-text-color)");
copyright.getStyle().set("font-size", "var(--lumo-font-size-s)");
copyright.getStyle().set("margin", "0");
footerContent.add(copyright);
footer.add(footerContent);
return footer;
}
}

View File

@@ -14,13 +14,16 @@ import com.vaadin.flow.component.orderedlayout.VerticalLayout;
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 com.vaadin.flow.router.RouterLayout; 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 com.vaadin.flow.server.auth.AnonymousAllowed;
import de.assecutor.votianlt.security.SecurityService; import de.assecutor.votianlt.security.SecurityService;
import de.assecutor.votianlt.pages.base.ui.view.MainLayout;
@Route("") @Route("")
@PageTitle("VotianLT - Willkommen") @PageTitle("VotianLT - Willkommen")
@AnonymousAllowed @AnonymousAllowed
public class StartView extends VerticalLayout { public class StartView extends VerticalLayout implements BeforeEnterObserver {
private final SecurityService securityService; private final SecurityService securityService;
@@ -47,6 +50,14 @@ public class StartView extends VerticalLayout {
add(createFooter()); add(createFooter());
} }
@Override
public void beforeEnter(BeforeEnterEvent event) {
// Redirect authenticated users to dashboard
if (securityService.isUserLoggedIn()) {
event.forwardTo("dashboard");
}
}
private Component createHeader() { private Component createHeader() {
HorizontalLayout header = new HorizontalLayout(); HorizontalLayout header = new HorizontalLayout();
header.setWidthFull(); header.setWidthFull();

View File

@@ -1,23 +0,0 @@
package de.assecutor.votianlt.pages.test;
import com.vaadin.flow.component.html.H1;
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 de.assecutor.votianlt.pages.base.ui.view.MainLayout;
@Route(value = "test", layout = MainLayout.class)
@PageTitle("Test Seite")
public class TestView extends VerticalLayout {
public TestView() {
H1 title = new H1("Test Seite");
Paragraph description = new Paragraph("Diese Seite dient zum Testen der Navigation.");
add(title, description);
setPadding(true);
setSpacing(true);
}
}

View File

@@ -0,0 +1,44 @@
package de.assecutor.votianlt.pages.verwaltung.ui.view;
import com.vaadin.flow.component.html.H1;
import com.vaadin.flow.component.html.Main;
import com.vaadin.flow.component.html.Paragraph;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
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.pages.base.ui.component.ViewToolbar;
import jakarta.annotation.security.RolesAllowed;
@Route(value = "verwaltung", layout = de.assecutor.votianlt.pages.base.ui.view.MainLayout.class)
@PageTitle("Verwaltung")
@Menu(order = 5, icon = "vaadin:cogs", title = "Verwaltung")
@RolesAllowed("USER")
public class VerwaltungView extends Main {
public VerwaltungView() {
setSizeFull();
addClassNames(LumoUtility.BoxSizing.BORDER, LumoUtility.Display.FLEX, LumoUtility.FlexDirection.COLUMN,
LumoUtility.Padding.MEDIUM, LumoUtility.Gap.SMALL);
add(new ViewToolbar("Verwaltung"));
// Content
VerticalLayout content = new VerticalLayout();
H1 title = new H1("Verwaltung");
title.getStyle().set("color", "var(--lumo-primary-color)");
Paragraph description = new Paragraph("Willkommen im Verwaltungsbereich. Wählen Sie eine Option aus dem Menü.");
description.getStyle().set("color", "var(--lumo-secondary-text-color)");
content.add(title, description);
content.setDefaultHorizontalComponentAlignment(VerticalLayout.Alignment.CENTER);
content.setJustifyContentMode(VerticalLayout.JustifyContentMode.CENTER);
content.setSizeFull();
add(content);
}
}