From 248b71aab910cd8fb99f9bf80839934c3e0972db Mon Sep 17 00:00:00 2001 From: Sven Carstensen Date: Thu, 14 Aug 2025 09:43:25 +0200 Subject: [PATCH] Erweiterungen --- .../add_company/ui/view/AddCompanyView.java | 2 +- .../add_customer/ui/view/AddCustomerView.java | 2 +- .../pages/add_job/ui/view/AddJobView.java | 2 +- .../pages/base/ui/view/MainLayout.java | 62 +++++- .../start/ui/view/AuthenticatedStartView.java | 189 ++++++++++++++++++ .../pages/start/ui/view/StartView.java | 13 +- .../votianlt/pages/test/TestView.java | 23 --- .../verwaltung/ui/view/VerwaltungView.java | 44 ++++ 8 files changed, 307 insertions(+), 30 deletions(-) create mode 100644 src/main/java/de/assecutor/votianlt/pages/start/ui/view/AuthenticatedStartView.java delete mode 100644 src/main/java/de/assecutor/votianlt/pages/test/TestView.java create mode 100644 src/main/java/de/assecutor/votianlt/pages/verwaltung/ui/view/VerwaltungView.java diff --git a/src/main/java/de/assecutor/votianlt/pages/add_company/ui/view/AddCompanyView.java b/src/main/java/de/assecutor/votianlt/pages/add_company/ui/view/AddCompanyView.java index 24b5ac1..64e8523 100644 --- a/src/main/java/de/assecutor/votianlt/pages/add_company/ui/view/AddCompanyView.java +++ b/src/main/java/de/assecutor/votianlt/pages/add_company/ui/view/AddCompanyView.java @@ -21,7 +21,7 @@ import java.time.Clock; @Route(value = "add_company", layout = de.assecutor.votianlt.pages.base.ui.view.MainLayout.class) @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") public class AddCompanyView extends Main { private final AddCompanyService addCompanyService; diff --git a/src/main/java/de/assecutor/votianlt/pages/add_customer/ui/view/AddCustomerView.java b/src/main/java/de/assecutor/votianlt/pages/add_customer/ui/view/AddCustomerView.java index 88dd259..3d74a59 100644 --- a/src/main/java/de/assecutor/votianlt/pages/add_customer/ui/view/AddCustomerView.java +++ b/src/main/java/de/assecutor/votianlt/pages/add_customer/ui/view/AddCustomerView.java @@ -21,7 +21,7 @@ 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") +//@Menu(order = 0, icon = "vaadin:clipboard-check", title = "Neuen Kunden anlegen") @RolesAllowed("USER") public class AddCustomerView extends Main { private final AddCustomerService addCustomerService; diff --git a/src/main/java/de/assecutor/votianlt/pages/add_job/ui/view/AddJobView.java b/src/main/java/de/assecutor/votianlt/pages/add_job/ui/view/AddJobView.java index 37efc89..2399c53 100644 --- a/src/main/java/de/assecutor/votianlt/pages/add_job/ui/view/AddJobView.java +++ b/src/main/java/de/assecutor/votianlt/pages/add_job/ui/view/AddJobView.java @@ -42,7 +42,7 @@ import java.util.Optional; @Route(value = "add_job", layout = de.assecutor.votianlt.pages.base.ui.view.MainLayout.class) @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") @Slf4j public class AddJobView extends Main { 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 23f0ff7..849e015 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 @@ -4,6 +4,7 @@ import com.vaadin.flow.component.Component; import com.vaadin.flow.component.applayout.AppLayout; import com.vaadin.flow.component.avatar.Avatar; 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.Span; 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.MenuBarVariant; 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.SideNavItem; import com.vaadin.flow.router.Layout; @@ -20,6 +22,7 @@ import de.assecutor.votianlt.security.SecurityService; import static com.vaadin.flow.theme.lumo.LumoUtility.*; +@Layout public final class MainLayout extends AppLayout { private final SecurityService securityService; @@ -45,11 +48,64 @@ public final class MainLayout extends AppLayout { return header; } - private SideNav createSideNav() { + private Component createSideNav() { var nav = new SideNav(); 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) { diff --git a/src/main/java/de/assecutor/votianlt/pages/start/ui/view/AuthenticatedStartView.java b/src/main/java/de/assecutor/votianlt/pages/start/ui/view/AuthenticatedStartView.java new file mode 100644 index 0000000..e6a574d --- /dev/null +++ b/src/main/java/de/assecutor/votianlt/pages/start/ui/view/AuthenticatedStartView.java @@ -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; + } +} \ No newline at end of file diff --git a/src/main/java/de/assecutor/votianlt/pages/start/ui/view/StartView.java b/src/main/java/de/assecutor/votianlt/pages/start/ui/view/StartView.java index c43d7b2..4eed57e 100644 --- a/src/main/java/de/assecutor/votianlt/pages/start/ui/view/StartView.java +++ b/src/main/java/de/assecutor/votianlt/pages/start/ui/view/StartView.java @@ -14,13 +14,16 @@ 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") @AnonymousAllowed -public class StartView extends VerticalLayout { +public class StartView extends VerticalLayout implements BeforeEnterObserver { private final SecurityService securityService; @@ -47,6 +50,14 @@ public class StartView extends VerticalLayout { add(createFooter()); } + @Override + public void beforeEnter(BeforeEnterEvent event) { + // Redirect authenticated users to dashboard + if (securityService.isUserLoggedIn()) { + event.forwardTo("dashboard"); + } + } + private Component createHeader() { HorizontalLayout header = new HorizontalLayout(); header.setWidthFull(); diff --git a/src/main/java/de/assecutor/votianlt/pages/test/TestView.java b/src/main/java/de/assecutor/votianlt/pages/test/TestView.java deleted file mode 100644 index ad95123..0000000 --- a/src/main/java/de/assecutor/votianlt/pages/test/TestView.java +++ /dev/null @@ -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); - } -} diff --git a/src/main/java/de/assecutor/votianlt/pages/verwaltung/ui/view/VerwaltungView.java b/src/main/java/de/assecutor/votianlt/pages/verwaltung/ui/view/VerwaltungView.java new file mode 100644 index 0000000..3ffe876 --- /dev/null +++ b/src/main/java/de/assecutor/votianlt/pages/verwaltung/ui/view/VerwaltungView.java @@ -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); + } +} \ No newline at end of file