feat: modernisiere UI mit votian-modern Theme und überarbeite Layouts
This commit is contained in:
Binary file not shown.
1726
src/main/frontend/themes/votian-modern/styles.css
Normal file
1726
src/main/frontend/themes/votian-modern/styles.css
Normal file
File diff suppressed because it is too large
Load Diff
9
src/main/frontend/themes/votian-modern/theme.json
Normal file
9
src/main/frontend/themes/votian-modern/theme.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"lumoImports": [
|
||||||
|
"typography",
|
||||||
|
"color",
|
||||||
|
"spacing",
|
||||||
|
"badge",
|
||||||
|
"utility"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -14,7 +14,7 @@ import java.time.Clock;
|
|||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
@EnableAsync
|
@EnableAsync
|
||||||
@EnableScheduling
|
@EnableScheduling
|
||||||
@Theme("default")
|
@Theme("votian-modern")
|
||||||
@Push
|
@Push
|
||||||
public class Application implements AppShellConfigurator {
|
public class Application implements AppShellConfigurator {
|
||||||
|
|
||||||
|
|||||||
@@ -216,6 +216,7 @@ public class DeliveryStationDialog extends Dialog {
|
|||||||
formLayout.setPadding(true);
|
formLayout.setPadding(true);
|
||||||
formLayout.setSpacing(true);
|
formLayout.setSpacing(true);
|
||||||
formLayout.setWidthFull();
|
formLayout.setWidthFull();
|
||||||
|
formLayout.addClassName("dialog-form-panel");
|
||||||
|
|
||||||
// Company with autocomplete
|
// Company with autocomplete
|
||||||
company = new ComboBox<>(translationHelper.getTranslation("profile.company"));
|
company = new ComboBox<>(translationHelper.getTranslation("profile.company"));
|
||||||
@@ -694,6 +695,7 @@ public class DeliveryStationDialog extends Dialog {
|
|||||||
content.setPadding(false);
|
content.setPadding(false);
|
||||||
content.setSpacing(true);
|
content.setSpacing(true);
|
||||||
content.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.STRETCH);
|
content.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.STRETCH);
|
||||||
|
content.addClassName("dialog-content-panel");
|
||||||
|
|
||||||
// Task title with template selection
|
// Task title with template selection
|
||||||
H3 tasksTitle = new H3(translationHelper.getTranslation("addjob.tasks.title"));
|
H3 tasksTitle = new H3(translationHelper.getTranslation("addjob.tasks.title"));
|
||||||
@@ -749,10 +751,7 @@ public class DeliveryStationDialog extends Dialog {
|
|||||||
VerticalLayout taskContainer = new VerticalLayout();
|
VerticalLayout taskContainer = new VerticalLayout();
|
||||||
taskContainer.setPadding(true);
|
taskContainer.setPadding(true);
|
||||||
taskContainer.setSpacing(true);
|
taskContainer.setSpacing(true);
|
||||||
taskContainer.getStyle().set("border", "1px solid var(--lumo-contrast-20pct)");
|
taskContainer.addClassName("dialog-task-card");
|
||||||
taskContainer.getStyle().set("border-radius", "var(--lumo-border-radius-m)");
|
|
||||||
taskContainer.getStyle().set("background-color", "var(--lumo-base-color)");
|
|
||||||
taskContainer.getStyle().set("position", "relative");
|
|
||||||
|
|
||||||
// Task type selection
|
// Task type selection
|
||||||
ComboBox<TaskType> taskTypeCombo = new ComboBox<>(translationHelper.getTranslation("addjob.tasks.tasktype"));
|
ComboBox<TaskType> taskTypeCombo = new ComboBox<>(translationHelper.getTranslation("addjob.tasks.tasktype"));
|
||||||
@@ -769,13 +768,7 @@ public class DeliveryStationDialog extends Dialog {
|
|||||||
// Red X button positioned in top-right corner
|
// Red X button positioned in top-right corner
|
||||||
Button deleteXButton = new Button(new Icon(VaadinIcon.CLOSE_SMALL));
|
Button deleteXButton = new Button(new Icon(VaadinIcon.CLOSE_SMALL));
|
||||||
deleteXButton.addThemeVariants(ButtonVariant.LUMO_ERROR, ButtonVariant.LUMO_TERTIARY);
|
deleteXButton.addThemeVariants(ButtonVariant.LUMO_ERROR, ButtonVariant.LUMO_TERTIARY);
|
||||||
deleteXButton.getStyle().set("position", "absolute");
|
deleteXButton.addClassName("dialog-floating-delete");
|
||||||
deleteXButton.getStyle().set("top", "8px");
|
|
||||||
deleteXButton.getStyle().set("right", "8px");
|
|
||||||
deleteXButton.getStyle().set("z-index", "10");
|
|
||||||
deleteXButton.getStyle().set("padding", "4px");
|
|
||||||
deleteXButton.getStyle().set("min-width", "24px");
|
|
||||||
deleteXButton.getStyle().set("min-height", "24px");
|
|
||||||
deleteXButton.addClickListener(e -> {
|
deleteXButton.addClickListener(e -> {
|
||||||
int idx = tasksList.getChildren().toList().indexOf(taskContainer);
|
int idx = tasksList.getChildren().toList().indexOf(taskContainer);
|
||||||
if (idx >= 0 && idx < tasksState.size()) {
|
if (idx >= 0 && idx < tasksState.size()) {
|
||||||
@@ -852,10 +845,7 @@ public class DeliveryStationDialog extends Dialog {
|
|||||||
VerticalLayout taskContainer = new VerticalLayout();
|
VerticalLayout taskContainer = new VerticalLayout();
|
||||||
taskContainer.setPadding(true);
|
taskContainer.setPadding(true);
|
||||||
taskContainer.setSpacing(true);
|
taskContainer.setSpacing(true);
|
||||||
taskContainer.getStyle().set("border", "1px solid var(--lumo-contrast-20pct)");
|
taskContainer.addClassName("dialog-task-card");
|
||||||
taskContainer.getStyle().set("border-radius", "var(--lumo-border-radius-m)");
|
|
||||||
taskContainer.getStyle().set("background-color", "var(--lumo-base-color)");
|
|
||||||
taskContainer.getStyle().set("position", "relative");
|
|
||||||
|
|
||||||
ComboBox<TaskType> taskTypeCombo = new ComboBox<>(translationHelper.getTranslation("addjob.tasks.tasktype"));
|
ComboBox<TaskType> taskTypeCombo = new ComboBox<>(translationHelper.getTranslation("addjob.tasks.tasktype"));
|
||||||
taskTypeCombo.setItems(TaskType.values());
|
taskTypeCombo.setItems(TaskType.values());
|
||||||
@@ -869,13 +859,7 @@ public class DeliveryStationDialog extends Dialog {
|
|||||||
|
|
||||||
Button deleteXButton = new Button(new Icon(VaadinIcon.CLOSE_SMALL));
|
Button deleteXButton = new Button(new Icon(VaadinIcon.CLOSE_SMALL));
|
||||||
deleteXButton.addThemeVariants(ButtonVariant.LUMO_ERROR, ButtonVariant.LUMO_TERTIARY);
|
deleteXButton.addThemeVariants(ButtonVariant.LUMO_ERROR, ButtonVariant.LUMO_TERTIARY);
|
||||||
deleteXButton.getStyle().set("position", "absolute");
|
deleteXButton.addClassName("dialog-floating-delete");
|
||||||
deleteXButton.getStyle().set("top", "8px");
|
|
||||||
deleteXButton.getStyle().set("right", "8px");
|
|
||||||
deleteXButton.getStyle().set("z-index", "10");
|
|
||||||
deleteXButton.getStyle().set("padding", "4px");
|
|
||||||
deleteXButton.getStyle().set("min-width", "24px");
|
|
||||||
deleteXButton.getStyle().set("min-height", "24px");
|
|
||||||
deleteXButton.addClickListener(e -> {
|
deleteXButton.addClickListener(e -> {
|
||||||
int idx = tasksList.getChildren().toList().indexOf(taskContainer);
|
int idx = tasksList.getChildren().toList().indexOf(taskContainer);
|
||||||
if (idx >= 0 && idx < tasksState.size()) {
|
if (idx >= 0 && idx < tasksState.size()) {
|
||||||
|
|||||||
@@ -67,12 +67,10 @@ public class DeliveryStationTile extends VerticalLayout {
|
|||||||
|
|
||||||
setSpacing(true);
|
setSpacing(true);
|
||||||
setPadding(true);
|
setPadding(true);
|
||||||
setWidth("40%");
|
setWidthFull();
|
||||||
getStyle().set("min-width", "300px");
|
addClassName("delivery-station-card");
|
||||||
getStyle().set("flex-shrink", "0");
|
getStyle().set("min-width", "0");
|
||||||
getStyle().set("border", "1px solid var(--lumo-contrast-20pct)");
|
getStyle().set("box-sizing", "border-box");
|
||||||
getStyle().set("border-radius", "var(--lumo-border-radius-m)");
|
|
||||||
getStyle().set("background-color", "var(--lumo-base-color)");
|
|
||||||
|
|
||||||
// Header with title, collapse button and delete button on one line
|
// Header with title, collapse button and delete button on one line
|
||||||
title = new H3(translationHelper.getTranslation("addjob.station.delivery", stationNumber));
|
title = new H3(translationHelper.getTranslation("addjob.station.delivery", stationNumber));
|
||||||
@@ -150,8 +148,9 @@ public class DeliveryStationTile extends VerticalLayout {
|
|||||||
HorizontalLayout streetLayout = new HorizontalLayout();
|
HorizontalLayout streetLayout = new HorizontalLayout();
|
||||||
streetLayout.setWidthFull();
|
streetLayout.setWidthFull();
|
||||||
streetLayout.setSpacing(true);
|
streetLayout.setSpacing(true);
|
||||||
street.setWidth("70%");
|
streetLayout.getStyle().set("flex-wrap", "wrap");
|
||||||
houseNumber.setWidth("30%");
|
street.getStyle().set("flex", "3 1 150px").set("min-width", "0");
|
||||||
|
houseNumber.getStyle().set("flex", "1 1 60px").set("min-width", "0");
|
||||||
streetLayout.add(street, houseNumber);
|
streetLayout.add(street, houseNumber);
|
||||||
add(streetLayout);
|
add(streetLayout);
|
||||||
|
|
||||||
@@ -174,8 +173,9 @@ public class DeliveryStationTile extends VerticalLayout {
|
|||||||
HorizontalLayout zipCityLayout = new HorizontalLayout();
|
HorizontalLayout zipCityLayout = new HorizontalLayout();
|
||||||
zipCityLayout.setWidthFull();
|
zipCityLayout.setWidthFull();
|
||||||
zipCityLayout.setSpacing(true);
|
zipCityLayout.setSpacing(true);
|
||||||
zip.setWidth("30%");
|
zipCityLayout.getStyle().set("flex-wrap", "wrap");
|
||||||
city.setWidth("70%");
|
zip.getStyle().set("flex", "1 1 80px").set("min-width", "0");
|
||||||
|
city.getStyle().set("flex", "3 1 150px").set("min-width", "0");
|
||||||
zipCityLayout.add(zip, city);
|
zipCityLayout.add(zip, city);
|
||||||
add(zipCityLayout);
|
add(zipCityLayout);
|
||||||
|
|
||||||
@@ -192,7 +192,7 @@ public class DeliveryStationTile extends VerticalLayout {
|
|||||||
// stays visible)
|
// stays visible)
|
||||||
expandedOnlyComponents = getChildren().filter(c -> c != titleLayout).toList();
|
expandedOnlyComponents = getChildren().filter(c -> c != titleLayout).toList();
|
||||||
|
|
||||||
getStyle().set("transition", "width 0.3s ease, min-width 0.3s ease");
|
getStyle().set("transition", "opacity 0.2s ease");
|
||||||
|
|
||||||
// Collapsed content (initially hidden)
|
// Collapsed content (initially hidden)
|
||||||
collapsedContent = new VerticalLayout();
|
collapsedContent = new VerticalLayout();
|
||||||
@@ -446,14 +446,10 @@ public class DeliveryStationTile extends VerticalLayout {
|
|||||||
updateCollapsedContent();
|
updateCollapsedContent();
|
||||||
expandedOnlyComponents.forEach(c -> c.setVisible(false));
|
expandedOnlyComponents.forEach(c -> c.setVisible(false));
|
||||||
collapsedContent.setVisible(true);
|
collapsedContent.setVisible(true);
|
||||||
setWidth("25%");
|
|
||||||
getStyle().set("min-width", "150px");
|
|
||||||
collapseButton.setIcon(new Icon(VaadinIcon.ANGLE_DOUBLE_RIGHT));
|
collapseButton.setIcon(new Icon(VaadinIcon.ANGLE_DOUBLE_RIGHT));
|
||||||
} else {
|
} else {
|
||||||
expandedOnlyComponents.forEach(c -> c.setVisible(true));
|
expandedOnlyComponents.forEach(c -> c.setVisible(true));
|
||||||
collapsedContent.setVisible(false);
|
collapsedContent.setVisible(false);
|
||||||
setWidth("40%");
|
|
||||||
getStyle().set("min-width", "300px");
|
|
||||||
collapseButton.setIcon(new Icon(VaadinIcon.ANGLE_DOUBLE_LEFT));
|
collapseButton.setIcon(new Icon(VaadinIcon.ANGLE_DOUBLE_LEFT));
|
||||||
}
|
}
|
||||||
if (collapseListener != null) {
|
if (collapseListener != null) {
|
||||||
|
|||||||
@@ -267,6 +267,7 @@ public class PickupStationDialog extends Dialog {
|
|||||||
formLayout.setPadding(true);
|
formLayout.setPadding(true);
|
||||||
formLayout.setSpacing(true);
|
formLayout.setSpacing(true);
|
||||||
formLayout.setWidthFull();
|
formLayout.setWidthFull();
|
||||||
|
formLayout.addClassName("dialog-form-panel");
|
||||||
|
|
||||||
// Customer selection
|
// Customer selection
|
||||||
customerComboBox = new ComboBox<>(translationHelper.getTranslation("addjob.customer.label"));
|
customerComboBox = new ComboBox<>(translationHelper.getTranslation("addjob.customer.label"));
|
||||||
@@ -760,6 +761,7 @@ public class PickupStationDialog extends Dialog {
|
|||||||
content.setSpacing(true);
|
content.setSpacing(true);
|
||||||
content.setWidth("720px");
|
content.setWidth("720px");
|
||||||
content.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.STRETCH);
|
content.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.STRETCH);
|
||||||
|
content.addClassName("dialog-content-panel");
|
||||||
|
|
||||||
// Digital processing + App user
|
// Digital processing + App user
|
||||||
digitalProcessingCheckbox = new Checkbox(translationHelper.getTranslation("profile.settings.digitalprocess"));
|
digitalProcessingCheckbox = new Checkbox(translationHelper.getTranslation("profile.settings.digitalprocess"));
|
||||||
@@ -827,8 +829,7 @@ public class PickupStationDialog extends Dialog {
|
|||||||
|
|
||||||
// Info about delivery dates
|
// Info about delivery dates
|
||||||
Span deliveryInfoLabel = new Span(translationHelper.getTranslation("addjob.appointment.delivery.info"));
|
Span deliveryInfoLabel = new Span(translationHelper.getTranslation("addjob.appointment.delivery.info"));
|
||||||
deliveryInfoLabel.getStyle().set("color", "var(--lumo-secondary-text-color)");
|
deliveryInfoLabel.addClassName("inline-caption");
|
||||||
deliveryInfoLabel.getStyle().set("font-style", "italic");
|
|
||||||
deliveryInfoLabel.getStyle().set("margin-top", "var(--lumo-space-m)");
|
deliveryInfoLabel.getStyle().set("margin-top", "var(--lumo-space-m)");
|
||||||
content.add(deliveryInfoLabel);
|
content.add(deliveryInfoLabel);
|
||||||
|
|
||||||
@@ -853,10 +854,7 @@ public class PickupStationDialog extends Dialog {
|
|||||||
VerticalLayout cargoAreaContainer = new VerticalLayout();
|
VerticalLayout cargoAreaContainer = new VerticalLayout();
|
||||||
cargoAreaContainer.setWidthFull();
|
cargoAreaContainer.setWidthFull();
|
||||||
cargoAreaContainer.setSpacing(true);
|
cargoAreaContainer.setSpacing(true);
|
||||||
cargoAreaContainer.getStyle().set("background", "var(--lumo-base-color)");
|
cargoAreaContainer.addClassName("dialog-cargo-card");
|
||||||
cargoAreaContainer.getStyle().set("border", "1px solid var(--lumo-contrast-20pct)");
|
|
||||||
cargoAreaContainer.getStyle().set("border-radius", "var(--lumo-border-radius-m)");
|
|
||||||
cargoAreaContainer.getStyle().set("padding", "var(--lumo-space-m)");
|
|
||||||
|
|
||||||
H3 cargoTitle = new H3(translationHelper.getTranslation("addjob.tab.cargo"));
|
H3 cargoTitle = new H3(translationHelper.getTranslation("addjob.tab.cargo"));
|
||||||
|
|
||||||
|
|||||||
@@ -43,9 +43,7 @@ public class StationTile extends VerticalLayout {
|
|||||||
|
|
||||||
setPadding(false);
|
setPadding(false);
|
||||||
setSpacing(false);
|
setSpacing(false);
|
||||||
getStyle().set("border", "1px solid var(--lumo-contrast-20pct)");
|
addClassName("station-tile");
|
||||||
getStyle().set("border-radius", "var(--lumo-border-radius-m)");
|
|
||||||
getStyle().set("background-color", "var(--lumo-base-color)");
|
|
||||||
getStyle().set("cursor", "pointer");
|
getStyle().set("cursor", "pointer");
|
||||||
getStyle().set("aspect-ratio", "1 / 1");
|
getStyle().set("aspect-ratio", "1 / 1");
|
||||||
getStyle().set("overflow", "hidden");
|
getStyle().set("overflow", "hidden");
|
||||||
@@ -196,11 +194,9 @@ public class StationTile extends VerticalLayout {
|
|||||||
|
|
||||||
public void setAddressValidated(boolean validated) {
|
public void setAddressValidated(boolean validated) {
|
||||||
if (validated) {
|
if (validated) {
|
||||||
getStyle().set("background-color", "rgba(76, 175, 80, 0.15)");
|
addClassName("validated");
|
||||||
getStyle().set("border", "1px solid rgba(76, 175, 80, 0.3)");
|
|
||||||
} else {
|
} else {
|
||||||
getStyle().set("background-color", "var(--lumo-base-color)");
|
removeClassName("validated");
|
||||||
getStyle().set("border", "1px solid var(--lumo-contrast-20pct)");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ public final class ViewToolbar extends Composite<Header> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ViewToolbar(String viewTitle, boolean showDrawerToggle, Component... components) {
|
public ViewToolbar(String viewTitle, boolean showDrawerToggle, Component... components) {
|
||||||
|
addClassName("view-toolbar-host");
|
||||||
|
getContent().addClassName("view-toolbar");
|
||||||
addClassNames(Display.FLEX, FlexDirection.COLUMN, JustifyContent.BETWEEN, AlignItems.STRETCH, Gap.MEDIUM,
|
addClassNames(Display.FLEX, FlexDirection.COLUMN, JustifyContent.BETWEEN, AlignItems.STRETCH, Gap.MEDIUM,
|
||||||
FlexDirection.Breakpoint.Medium.ROW, AlignItems.Breakpoint.Medium.CENTER);
|
FlexDirection.Breakpoint.Medium.ROW, AlignItems.Breakpoint.Medium.CENTER);
|
||||||
|
|
||||||
@@ -22,21 +24,26 @@ public final class ViewToolbar extends Composite<Header> {
|
|||||||
if (showDrawerToggle) {
|
if (showDrawerToggle) {
|
||||||
var drawerToggle = new DrawerToggle();
|
var drawerToggle = new DrawerToggle();
|
||||||
drawerToggle.addClassNames(Margin.NONE);
|
drawerToggle.addClassNames(Margin.NONE);
|
||||||
|
drawerToggle.addClassName("view-toolbar-toggle");
|
||||||
var title = new H1(viewTitle);
|
var title = new H1(viewTitle);
|
||||||
title.addClassNames(FontSize.XLARGE, Margin.NONE, FontWeight.LIGHT);
|
title.addClassNames(FontSize.XLARGE, Margin.NONE, FontWeight.LIGHT);
|
||||||
|
title.addClassName("view-toolbar-title");
|
||||||
toggleAndTitle = new Div(drawerToggle, title);
|
toggleAndTitle = new Div(drawerToggle, title);
|
||||||
} else {
|
} else {
|
||||||
var title = new H1(viewTitle);
|
var title = new H1(viewTitle);
|
||||||
title.addClassNames(FontSize.XLARGE, Margin.NONE, FontWeight.LIGHT);
|
title.addClassNames(FontSize.XLARGE, Margin.NONE, FontWeight.LIGHT);
|
||||||
|
title.addClassName("view-toolbar-title");
|
||||||
toggleAndTitle = new Div(title);
|
toggleAndTitle = new Div(title);
|
||||||
}
|
}
|
||||||
toggleAndTitle.addClassNames(Display.FLEX, AlignItems.CENTER);
|
toggleAndTitle.addClassNames(Display.FLEX, AlignItems.CENTER);
|
||||||
|
toggleAndTitle.addClassName("view-toolbar-title-row");
|
||||||
getContent().add(toggleAndTitle);
|
getContent().add(toggleAndTitle);
|
||||||
|
|
||||||
if (components.length > 0) {
|
if (components.length > 0) {
|
||||||
var actions = new Div(components);
|
var actions = new Div(components);
|
||||||
actions.addClassNames(Display.FLEX, FlexDirection.COLUMN, JustifyContent.END, Flex.GROW, Gap.SMALL,
|
actions.addClassNames(Display.FLEX, FlexDirection.COLUMN, JustifyContent.END, Flex.GROW, Gap.SMALL,
|
||||||
FlexDirection.Breakpoint.Medium.ROW);
|
FlexDirection.Breakpoint.Medium.ROW);
|
||||||
|
actions.addClassName("view-toolbar-actions");
|
||||||
getContent().add(actions);
|
getContent().add(actions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -45,6 +52,7 @@ public final class ViewToolbar extends Composite<Header> {
|
|||||||
var group = new Div(components);
|
var group = new Div(components);
|
||||||
group.addClassNames(Display.FLEX, FlexDirection.COLUMN, AlignItems.STRETCH, Gap.SMALL,
|
group.addClassNames(Display.FLEX, FlexDirection.COLUMN, AlignItems.STRETCH, Gap.SMALL,
|
||||||
FlexDirection.Breakpoint.Medium.ROW, AlignItems.Breakpoint.Medium.CENTER);
|
FlexDirection.Breakpoint.Medium.ROW, AlignItems.Breakpoint.Medium.CENTER);
|
||||||
|
group.addClassName("view-toolbar-group");
|
||||||
return group;
|
return group;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,12 +33,16 @@ public final class AdminLayout extends AppLayout {
|
|||||||
|
|
||||||
public AdminLayout(SecurityService securityService) {
|
public AdminLayout(SecurityService securityService) {
|
||||||
this.securityService = securityService;
|
this.securityService = securityService;
|
||||||
|
addClassName("admin-shell");
|
||||||
setPrimarySection(Section.DRAWER);
|
setPrimarySection(Section.DRAWER);
|
||||||
|
getStyle().set("--vaadin-app-layout-drawer-width", "336px");
|
||||||
|
|
||||||
// Always build the drawer; keep references and toggle visibility on attach and
|
// Always build the drawer; keep references and toggle visibility on attach and
|
||||||
// after navigation
|
// after navigation
|
||||||
headerRef = createHeader();
|
headerRef = createHeader();
|
||||||
navRef = new Scroller(createSideNav());
|
navRef = new Scroller(createSideNav());
|
||||||
|
navRef.addClassName("app-drawer-scroll");
|
||||||
|
navRef.setScrollDirection(Scroller.ScrollDirection.VERTICAL);
|
||||||
userMenuRef = createUserMenu();
|
userMenuRef = createUserMenu();
|
||||||
addToDrawer(headerRef, navRef, userMenuRef);
|
addToDrawer(headerRef, navRef, userMenuRef);
|
||||||
|
|
||||||
@@ -62,18 +66,21 @@ public final class AdminLayout extends AppLayout {
|
|||||||
private Div createHeader() {
|
private Div createHeader() {
|
||||||
var appLogo = VaadinIcon.SHIELD.create();
|
var appLogo = VaadinIcon.SHIELD.create();
|
||||||
appLogo.addClassNames(TextColor.PRIMARY, IconSize.LARGE);
|
appLogo.addClassNames(TextColor.PRIMARY, IconSize.LARGE);
|
||||||
|
appLogo.addClassName("app-drawer-logo-icon");
|
||||||
|
|
||||||
var appName = new Span("VotianLT Admin");
|
var appName = new Span("VotianLT Admin");
|
||||||
appName.addClassNames(FontWeight.SEMIBOLD, FontSize.LARGE);
|
appName.addClassNames(FontWeight.SEMIBOLD, FontSize.LARGE);
|
||||||
|
appName.addClassName("app-drawer-title");
|
||||||
|
|
||||||
var header = new Div(appLogo, appName);
|
var header = new Div(appLogo, appName);
|
||||||
header.addClassNames(Display.FLEX, Padding.MEDIUM, Gap.MEDIUM, AlignItems.CENTER);
|
header.addClassNames(Display.FLEX, Padding.MEDIUM, Gap.MEDIUM, AlignItems.CENTER);
|
||||||
|
header.addClassName("app-drawer-header");
|
||||||
return header;
|
return header;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Component createSideNav() {
|
private Component createSideNav() {
|
||||||
var nav = new SideNav();
|
var nav = new SideNav();
|
||||||
nav.addClassNames(Margin.Horizontal.MEDIUM);
|
nav.addClassName("app-side-nav");
|
||||||
|
|
||||||
// Only admin-specific menu items
|
// Only admin-specific menu items
|
||||||
SideNavItem dashboard = new SideNavItem("Dashboard", "admin-dashboard", new Icon(VaadinIcon.DASHBOARD));
|
SideNavItem dashboard = new SideNavItem("Dashboard", "admin-dashboard", new Icon(VaadinIcon.DASHBOARD));
|
||||||
@@ -98,6 +105,8 @@ public final class AdminLayout extends AppLayout {
|
|||||||
VerticalLayout navContainer = new VerticalLayout();
|
VerticalLayout navContainer = new VerticalLayout();
|
||||||
navContainer.setPadding(false);
|
navContainer.setPadding(false);
|
||||||
navContainer.setSpacing(false);
|
navContainer.setSpacing(false);
|
||||||
|
navContainer.setWidthFull();
|
||||||
|
navContainer.addClassName("app-nav-container");
|
||||||
navContainer.add(nav);
|
navContainer.add(nav);
|
||||||
|
|
||||||
return navContainer;
|
return navContainer;
|
||||||
@@ -106,15 +115,17 @@ public final class AdminLayout extends AppLayout {
|
|||||||
private Component createUserMenu() {
|
private Component createUserMenu() {
|
||||||
var userMenu = new MenuBar();
|
var userMenu = new MenuBar();
|
||||||
userMenu.addThemeVariants(MenuBarVariant.LUMO_TERTIARY_INLINE);
|
userMenu.addThemeVariants(MenuBarVariant.LUMO_TERTIARY_INLINE);
|
||||||
userMenu.addClassNames(Margin.MEDIUM);
|
userMenu.addClassName("app-user-menu");
|
||||||
|
|
||||||
// Dynamically updatable components
|
// Dynamically updatable components
|
||||||
var avatar = new Avatar();
|
var avatar = new Avatar();
|
||||||
avatar.addThemeVariants(AvatarVariant.LUMO_XSMALL);
|
avatar.addThemeVariants(AvatarVariant.LUMO_XSMALL);
|
||||||
avatar.addClassNames(Margin.Right.SMALL);
|
avatar.addClassNames(Margin.Right.SMALL);
|
||||||
avatar.setColorIndex(1); // Different color for admin
|
avatar.setColorIndex(1); // Different color for admin
|
||||||
|
avatar.addClassName("app-user-avatar");
|
||||||
|
|
||||||
var userNameSpan = new Span();
|
var userNameSpan = new Span();
|
||||||
|
userNameSpan.addClassName("app-user-name");
|
||||||
|
|
||||||
var userMenuItem = userMenu.addItem(avatar);
|
var userMenuItem = userMenu.addItem(avatar);
|
||||||
userMenuItem.add(userNameSpan);
|
userMenuItem.add(userNameSpan);
|
||||||
|
|||||||
@@ -3,10 +3,12 @@ package de.assecutor.votianlt.pages.base.ui.view;
|
|||||||
import com.vaadin.flow.component.AttachEvent;
|
import com.vaadin.flow.component.AttachEvent;
|
||||||
import com.vaadin.flow.component.Component;
|
import com.vaadin.flow.component.Component;
|
||||||
import com.vaadin.flow.component.DetachEvent;
|
import com.vaadin.flow.component.DetachEvent;
|
||||||
|
import com.vaadin.flow.component.HasElement;
|
||||||
import com.vaadin.flow.component.UI;
|
import com.vaadin.flow.component.UI;
|
||||||
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.grid.GridVariant;
|
||||||
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;
|
||||||
@@ -24,9 +26,9 @@ import com.vaadin.flow.router.Layout;
|
|||||||
import com.vaadin.flow.server.auth.AnonymousAllowed;
|
import com.vaadin.flow.server.auth.AnonymousAllowed;
|
||||||
import com.vaadin.flow.shared.Registration;
|
import com.vaadin.flow.shared.Registration;
|
||||||
import de.assecutor.votianlt.model.User;
|
import de.assecutor.votianlt.model.User;
|
||||||
|
import de.assecutor.votianlt.model.Language;
|
||||||
import de.assecutor.votianlt.pages.service.AppUserService;
|
import de.assecutor.votianlt.pages.service.AppUserService;
|
||||||
import de.assecutor.votianlt.pages.view.EditProfileView;
|
import de.assecutor.votianlt.pages.view.EditProfileView;
|
||||||
import de.assecutor.votianlt.model.Language;
|
|
||||||
import de.assecutor.votianlt.security.SecurityService;
|
import de.assecutor.votianlt.security.SecurityService;
|
||||||
import de.assecutor.votianlt.service.MessageBadgeUpdateService;
|
import de.assecutor.votianlt.service.MessageBadgeUpdateService;
|
||||||
import de.assecutor.votianlt.service.MessageService;
|
import de.assecutor.votianlt.service.MessageService;
|
||||||
@@ -51,8 +53,9 @@ public final class MainLayout extends AppLayout {
|
|||||||
private Scroller navRef;
|
private Scroller navRef;
|
||||||
private Component userMenuRef;
|
private Component userMenuRef;
|
||||||
private TreeGrid<MenuTreeItem> tree;
|
private TreeGrid<MenuTreeItem> tree;
|
||||||
private MenuTreeItem messagesTreeItem;
|
private long unreadMessagesCount;
|
||||||
private Registration badgeUpdateRegistration;
|
private Registration badgeUpdateRegistration;
|
||||||
|
private final Div viewContainer = new Div();
|
||||||
|
|
||||||
public MainLayout(SecurityService securityService, MessageService messageService,
|
public MainLayout(SecurityService securityService, MessageService messageService,
|
||||||
MessageBadgeUpdateService messageBadgeUpdateService, AppUserService appUserService) {
|
MessageBadgeUpdateService messageBadgeUpdateService, AppUserService appUserService) {
|
||||||
@@ -60,10 +63,10 @@ public final class MainLayout extends AppLayout {
|
|||||||
this.messageService = messageService;
|
this.messageService = messageService;
|
||||||
this.messageBadgeUpdateService = messageBadgeUpdateService;
|
this.messageBadgeUpdateService = messageBadgeUpdateService;
|
||||||
this.appUserService = appUserService;
|
this.appUserService = appUserService;
|
||||||
|
addClassName("app-shell");
|
||||||
setPrimarySection(Section.DRAWER);
|
setPrimarySection(Section.DRAWER);
|
||||||
|
|
||||||
// Drawer Styles für volle Höhe
|
getStyle().set("--vaadin-app-layout-drawer-width", "336px");
|
||||||
getStyle().set("--vaadin-app-layout-drawer-width", "286px");
|
|
||||||
|
|
||||||
// Always build the drawer; keep references and toggle visibility on attach and
|
// Always build the drawer; keep references and toggle visibility on attach and
|
||||||
// after navigation
|
// after navigation
|
||||||
@@ -72,10 +75,15 @@ public final class MainLayout extends AppLayout {
|
|||||||
// Scroller für Navigation mit maximaler Höhe
|
// Scroller für Navigation mit maximaler Höhe
|
||||||
Component sideNav = createSideNav();
|
Component sideNav = createSideNav();
|
||||||
navRef = new Scroller(sideNav);
|
navRef = new Scroller(sideNav);
|
||||||
|
navRef.addClassName("app-drawer-scroll");
|
||||||
|
navRef.setScrollDirection(Scroller.ScrollDirection.VERTICAL);
|
||||||
|
|
||||||
userMenuRef = createUserMenu();
|
userMenuRef = createUserMenu();
|
||||||
addToDrawer(headerRef, navRef, userMenuRef);
|
addToDrawer(headerRef, navRef, userMenuRef);
|
||||||
|
|
||||||
|
viewContainer.addClassName("view-container");
|
||||||
|
getElement().appendChild(viewContainer.getElement());
|
||||||
|
|
||||||
updateDrawerVisibility();
|
updateDrawerVisibility();
|
||||||
|
|
||||||
// Re-check on attach (new UI/session) and on every navigation cycle
|
// Re-check on attach (new UI/session) and on every navigation cycle
|
||||||
@@ -96,12 +104,15 @@ public final class MainLayout extends AppLayout {
|
|||||||
private Div createHeader() {
|
private Div createHeader() {
|
||||||
var appLogo = VaadinIcon.CUBES.create();
|
var appLogo = VaadinIcon.CUBES.create();
|
||||||
appLogo.addClassNames(TextColor.PRIMARY, IconSize.LARGE);
|
appLogo.addClassNames(TextColor.PRIMARY, IconSize.LARGE);
|
||||||
|
appLogo.addClassName("app-drawer-logo-icon");
|
||||||
|
|
||||||
var appName = new Span("VotianLT");
|
var appName = new Span("VotianLT");
|
||||||
appName.addClassNames(FontWeight.SEMIBOLD, FontSize.LARGE);
|
appName.addClassNames(FontWeight.SEMIBOLD, FontSize.LARGE);
|
||||||
|
appName.addClassName("app-drawer-title");
|
||||||
|
|
||||||
var header = new Div(appLogo, appName);
|
var header = new Div(appLogo, appName);
|
||||||
header.addClassNames(Display.FLEX, Padding.MEDIUM, Gap.MEDIUM, AlignItems.CENTER);
|
header.addClassNames(Display.FLEX, Padding.MEDIUM, Gap.MEDIUM, AlignItems.CENTER);
|
||||||
|
header.addClassName("app-drawer-header");
|
||||||
return header;
|
return header;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,9 +128,6 @@ public final class MainLayout extends AppLayout {
|
|||||||
MenuTreeItem verwaltungItem = new MenuTreeItem(getTranslation("nav.management"), null, VaadinIcon.COG);
|
MenuTreeItem verwaltungItem = new MenuTreeItem(getTranslation("nav.management"), null, VaadinIcon.COG);
|
||||||
MenuTreeItem benutzerItem = new MenuTreeItem(getTranslation("nav.users"), null, VaadinIcon.USER);
|
MenuTreeItem benutzerItem = new MenuTreeItem(getTranslation("nav.users"), null, VaadinIcon.USER);
|
||||||
|
|
||||||
// Store reference to messages item for badge updates
|
|
||||||
messagesTreeItem = nachrichtenItem;
|
|
||||||
|
|
||||||
// Add root items
|
// Add root items
|
||||||
treeData.addItem(null, auftragserstellungItem);
|
treeData.addItem(null, auftragserstellungItem);
|
||||||
treeData.addItem(null, nachrichtenItem);
|
treeData.addItem(null, nachrichtenItem);
|
||||||
@@ -147,50 +155,55 @@ public final class MainLayout extends AppLayout {
|
|||||||
// Create Tree
|
// Create Tree
|
||||||
tree = new TreeGrid<>();
|
tree = new TreeGrid<>();
|
||||||
tree.setDataProvider(new TreeDataProvider<>(treeData));
|
tree.setDataProvider(new TreeDataProvider<>(treeData));
|
||||||
tree.addClassNames(Margin.Horizontal.MEDIUM);
|
tree.addClassName("app-nav-tree");
|
||||||
tree.setHeight("435px");
|
tree.addThemeVariants(GridVariant.LUMO_NO_BORDER, GridVariant.LUMO_NO_ROW_BORDERS);
|
||||||
|
tree.setAllRowsVisible(true);
|
||||||
|
tree.setWidthFull();
|
||||||
|
|
||||||
// Custom item renderer to show icon and label with badge
|
// Custom item renderer to show icon and label with badge
|
||||||
tree.addComponentHierarchyColumn(item -> {
|
var navColumn = tree.addComponentHierarchyColumn(item -> {
|
||||||
HorizontalLayout row = new HorizontalLayout();
|
HorizontalLayout row = new HorizontalLayout();
|
||||||
row.setAlignItems(FlexComponent.Alignment.CENTER);
|
row.setAlignItems(FlexComponent.Alignment.CENTER);
|
||||||
row.setSpacing(true);
|
row.setSpacing(true);
|
||||||
row.setPadding(false);
|
row.setPadding(false);
|
||||||
row.setMargin(false);
|
row.setMargin(false);
|
||||||
row.setWidthFull();
|
row.getStyle().set("max-width", "calc(100% - 4px)");
|
||||||
|
row.addClassName("app-nav-row");
|
||||||
|
|
||||||
// Icon
|
// Icon
|
||||||
if (item.icon() != null) {
|
if (item.icon() != null) {
|
||||||
Icon icon = item.icon().create();
|
Icon icon = item.icon().create();
|
||||||
icon.setSize("var(--lumo-icon-size-s)");
|
icon.setSize("var(--lumo-icon-size-s)");
|
||||||
icon.getStyle().set("min-width", "var(--lumo-icon-size-s)");
|
icon.getStyle().set("min-width", "var(--lumo-icon-size-s)");
|
||||||
|
icon.addClassName("app-nav-icon");
|
||||||
row.add(icon);
|
row.add(icon);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Label
|
// Label
|
||||||
Span label = new Span(item.label());
|
Span label = new Span(item.label());
|
||||||
label.getStyle().set("font-size", "var(--lumo-font-size-s)");
|
label.getStyle().set("font-size", "var(--lumo-font-size-s)");
|
||||||
|
label.addClassName("app-nav-label");
|
||||||
row.add(label);
|
row.add(label);
|
||||||
row.setFlexGrow(1, label);
|
row.setFlexGrow(1, label);
|
||||||
|
|
||||||
// Badge for messages
|
// Badge for messages
|
||||||
if (item == messagesTreeItem && item.badgeCount() > 0) {
|
if ("messages".equals(item.path()) && unreadMessagesCount > 0) {
|
||||||
Span badge = new Span(String.valueOf(item.badgeCount()));
|
Span badge = new Span(String.valueOf(unreadMessagesCount));
|
||||||
badge.getElement().getThemeList().add("badge");
|
badge.getElement().getThemeList().add("badge");
|
||||||
badge.getStyle().set("background-color", "var(--lumo-primary-color)");
|
badge.addClassName("app-nav-badge");
|
||||||
badge.getStyle().set("color", "#ffffff");
|
|
||||||
badge.getStyle().set("border-radius", "12px");
|
|
||||||
badge.getStyle().set("padding", "2px 8px");
|
|
||||||
badge.getStyle().set("font-size", "11px");
|
|
||||||
badge.getStyle().set("font-weight", "bold");
|
|
||||||
badge.getStyle().set("min-width", "18px");
|
|
||||||
badge.getStyle().set("text-align", "center");
|
|
||||||
badge.getStyle().set("margin-left", "auto");
|
|
||||||
row.add(badge);
|
row.add(badge);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Expand/collapse indicator for parent nodes
|
||||||
|
Icon chevron = VaadinIcon.ANGLE_RIGHT.create();
|
||||||
|
chevron.setSize("var(--lumo-icon-size-s)");
|
||||||
|
chevron.addClassName("nav-expand-icon");
|
||||||
|
row.add(chevron);
|
||||||
|
|
||||||
return row;
|
return row;
|
||||||
});
|
});
|
||||||
|
navColumn.setAutoWidth(false);
|
||||||
|
navColumn.setFlexGrow(1);
|
||||||
|
|
||||||
// Handle selection/navigation
|
// Handle selection/navigation
|
||||||
tree.addSelectionListener(event -> {
|
tree.addSelectionListener(event -> {
|
||||||
@@ -208,6 +221,8 @@ public final class MainLayout extends AppLayout {
|
|||||||
VerticalLayout navContainer = new VerticalLayout();
|
VerticalLayout navContainer = new VerticalLayout();
|
||||||
navContainer.setPadding(false);
|
navContainer.setPadding(false);
|
||||||
navContainer.setSpacing(false);
|
navContainer.setSpacing(false);
|
||||||
|
navContainer.setWidthFull();
|
||||||
|
navContainer.addClassName("app-nav-container");
|
||||||
navContainer.add(tree);
|
navContainer.add(tree);
|
||||||
|
|
||||||
return navContainer;
|
return navContainer;
|
||||||
@@ -221,31 +236,14 @@ public final class MainLayout extends AppLayout {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
long unreadCount = resolveUnreadMessageCount();
|
unreadMessagesCount = resolveUnreadMessageCount();
|
||||||
|
tree.getDataProvider().refreshAll();
|
||||||
// Get current data provider and update the messages item
|
|
||||||
TreeDataProvider<MenuTreeItem> dataProvider = (TreeDataProvider<MenuTreeItem>) tree.getDataProvider();
|
|
||||||
TreeData<MenuTreeItem> treeData = dataProvider.getTreeData();
|
|
||||||
|
|
||||||
// Find and update the messages item with new badge count
|
|
||||||
treeData.getChildren(null).stream().filter(item -> "messages".equals(item.path())).findFirst()
|
|
||||||
.ifPresent(oldItem -> {
|
|
||||||
MenuTreeItem newItem = new MenuTreeItem(getTranslation("nav.messages"), "messages",
|
|
||||||
VaadinIcon.ENVELOPE, unreadCount);
|
|
||||||
messagesTreeItem = newItem;
|
|
||||||
// Refresh to show updated badge
|
|
||||||
dataProvider.refreshAll();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Record representing a menu item in the tree
|
* Record representing a menu item in the tree
|
||||||
*/
|
*/
|
||||||
private record MenuTreeItem(String label, String path, VaadinIcon icon, long badgeCount) {
|
private record MenuTreeItem(String label, String path, VaadinIcon icon) {
|
||||||
MenuTreeItem(String label, String path, VaadinIcon icon) {
|
|
||||||
this(label, path, icon, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o)
|
if (this == o)
|
||||||
@@ -294,15 +292,17 @@ public final class MainLayout extends AppLayout {
|
|||||||
private Component createUserMenu() {
|
private Component createUserMenu() {
|
||||||
var userMenu = new MenuBar();
|
var userMenu = new MenuBar();
|
||||||
userMenu.addThemeVariants(MenuBarVariant.LUMO_TERTIARY_INLINE);
|
userMenu.addThemeVariants(MenuBarVariant.LUMO_TERTIARY_INLINE);
|
||||||
userMenu.addClassNames(Margin.MEDIUM);
|
userMenu.addClassName("app-user-menu");
|
||||||
|
|
||||||
// Dynamisch aktualisierbare Komponenten
|
// Dynamisch aktualisierbare Komponenten
|
||||||
var avatar = new Avatar();
|
var avatar = new Avatar();
|
||||||
avatar.addThemeVariants(AvatarVariant.LUMO_XSMALL);
|
avatar.addThemeVariants(AvatarVariant.LUMO_XSMALL);
|
||||||
avatar.addClassNames(Margin.Right.SMALL);
|
avatar.addClassNames(Margin.Right.SMALL);
|
||||||
avatar.setColorIndex(5);
|
avatar.setColorIndex(5);
|
||||||
|
avatar.addClassName("app-user-avatar");
|
||||||
|
|
||||||
var userNameSpan = new Span();
|
var userNameSpan = new Span();
|
||||||
|
userNameSpan.addClassName("app-user-name");
|
||||||
|
|
||||||
var userMenuItem = userMenu.addItem(avatar);
|
var userMenuItem = userMenu.addItem(avatar);
|
||||||
userMenuItem.add(userNameSpan);
|
userMenuItem.add(userNameSpan);
|
||||||
@@ -335,12 +335,19 @@ public final class MainLayout extends AppLayout {
|
|||||||
// Drawer-Layout anpassen nach kurzer Verzögerung
|
// Drawer-Layout anpassen nach kurzer Verzögerung
|
||||||
ui.access(() -> {
|
ui.access(() -> {
|
||||||
getElement().executeJs(
|
getElement().executeJs(
|
||||||
"setTimeout(() => {" + " const drawer = this.shadowRoot?.querySelector('[part=drawer]');"
|
"setTimeout(() => {"
|
||||||
+ " if (drawer) {" + " drawer.style.display = 'flex';"
|
+ " const drawer = this.shadowRoot?.querySelector('[part=drawer]');"
|
||||||
+ " drawer.style.flexDirection = 'column';" + " drawer.style.height = '100vh';"
|
+ " if (drawer) {"
|
||||||
+ " const scroller = drawer.querySelector('vaadin-scroller');" + " if (scroller) {"
|
+ " drawer.style.display = 'flex';"
|
||||||
+ " scroller.style.flex = '1 1 auto';" + " scroller.style.minHeight = '0';"
|
+ " drawer.style.flexDirection = 'column';"
|
||||||
+ " }" + " }" + "}, 100);");
|
+ " drawer.style.height = '100vh';"
|
||||||
|
+ " const scroller = drawer.querySelector('vaadin-scroller');"
|
||||||
|
+ " if (scroller) {"
|
||||||
|
+ " scroller.style.flex = '1 1 auto';"
|
||||||
|
+ " scroller.style.minHeight = '0';"
|
||||||
|
+ " }"
|
||||||
|
+ " }"
|
||||||
|
+ "}, 100);");
|
||||||
});
|
});
|
||||||
|
|
||||||
// Apply user's preferred language immediately after login
|
// Apply user's preferred language immediately after login
|
||||||
@@ -393,6 +400,14 @@ public final class MainLayout extends AppLayout {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void showRouterLayoutContent(HasElement content) {
|
||||||
|
viewContainer.getElement().removeAllChildren();
|
||||||
|
if (content != null) {
|
||||||
|
viewContainer.getElement().appendChild(content.getElement());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDetach(DetachEvent detachEvent) {
|
protected void onDetach(DetachEvent detachEvent) {
|
||||||
if (badgeUpdateRegistration != null) {
|
if (badgeUpdateRegistration != null) {
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ public class AddAppUserView extends VerticalLayout implements HasDynamicTitle {
|
|||||||
setSizeFull();
|
setSizeFull();
|
||||||
setPadding(true);
|
setPadding(true);
|
||||||
setSpacing(true);
|
setSpacing(true);
|
||||||
|
addClassName("form-page");
|
||||||
|
|
||||||
// Set field labels via i18n
|
// Set field labels via i18n
|
||||||
designationField.setLabel(getTranslation("addappuser.designation"));
|
designationField.setLabel(getTranslation("addappuser.designation"));
|
||||||
@@ -62,17 +63,17 @@ public class AddAppUserView extends VerticalLayout implements HasDynamicTitle {
|
|||||||
contentContainer.setMaxWidth("90%");
|
contentContainer.setMaxWidth("90%");
|
||||||
contentContainer.setSpacing(true);
|
contentContainer.setSpacing(true);
|
||||||
contentContainer.setPadding(true);
|
contentContainer.setPadding(true);
|
||||||
contentContainer.getStyle().set("background", "var(--lumo-contrast-5pct)");
|
contentContainer.addClassNames("form-shell", "form-card");
|
||||||
contentContainer.getStyle().set("border-radius", "var(--lumo-border-radius)");
|
|
||||||
contentContainer.getStyle().set("box-shadow", "var(--lumo-box-shadow-s)");
|
|
||||||
|
|
||||||
// Header with title and back button
|
// Header with title and back button
|
||||||
HorizontalLayout header = new HorizontalLayout();
|
HorizontalLayout header = new HorizontalLayout();
|
||||||
header.setAlignItems(FlexComponent.Alignment.CENTER);
|
header.setAlignItems(FlexComponent.Alignment.CENTER);
|
||||||
header.setSpacing(true);
|
header.setSpacing(true);
|
||||||
|
header.addClassName("form-header");
|
||||||
|
|
||||||
H2 title = new H2(getTranslation("addappuser.title"));
|
H2 title = new H2(getTranslation("addappuser.title"));
|
||||||
title.getStyle().set("margin", "0");
|
title.getStyle().set("margin", "0");
|
||||||
|
title.addClassName("form-title");
|
||||||
|
|
||||||
Button backButton = new Button(getTranslation("button.back"), new Icon(VaadinIcon.ARROW_LEFT));
|
Button backButton = new Button(getTranslation("button.back"), new Icon(VaadinIcon.ARROW_LEFT));
|
||||||
backButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
|
backButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
|
||||||
|
|||||||
@@ -73,11 +73,14 @@ public class AddCompanyView extends Main implements HasDynamicTitle {
|
|||||||
// Zentriere die Inhalte vertikal und horizontal
|
// Zentriere die Inhalte vertikal und horizontal
|
||||||
formLayout.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.CENTER);
|
formLayout.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.CENTER);
|
||||||
formLayout.setSpacing(true);
|
formLayout.setSpacing(true);
|
||||||
formLayout.setSizeUndefined(); // Inhalt eng setzen
|
formLayout.setWidthFull();
|
||||||
|
formLayout.setMaxWidth("620px");
|
||||||
|
formLayout.addClassNames("form-shell", "form-card");
|
||||||
|
|
||||||
setSizeFull();
|
setSizeFull();
|
||||||
addClassNames(LumoUtility.BoxSizing.BORDER, LumoUtility.Display.FLEX, LumoUtility.FlexDirection.COLUMN,
|
addClassNames(LumoUtility.BoxSizing.BORDER, LumoUtility.Display.FLEX, LumoUtility.FlexDirection.COLUMN,
|
||||||
LumoUtility.Padding.MEDIUM, LumoUtility.Gap.SMALL);
|
LumoUtility.Padding.MEDIUM, LumoUtility.Gap.SMALL);
|
||||||
|
addClassName("form-page");
|
||||||
|
|
||||||
add(new ViewToolbar(getTranslation("addcompany.title")));
|
add(new ViewToolbar(getTranslation("addcompany.title")));
|
||||||
add(formLayout);
|
add(formLayout);
|
||||||
|
|||||||
@@ -150,11 +150,13 @@ public class AddCustomerView extends Main implements HasDynamicTitle {
|
|||||||
VerticalLayout container = new VerticalLayout();
|
VerticalLayout container = new VerticalLayout();
|
||||||
container.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.CENTER);
|
container.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.CENTER);
|
||||||
container.setSpacing(true);
|
container.setSpacing(true);
|
||||||
|
container.addClassNames("form-shell", "form-card");
|
||||||
container.add(formLayout);
|
container.add(formLayout);
|
||||||
|
|
||||||
setSizeFull();
|
setSizeFull();
|
||||||
addClassNames(LumoUtility.BoxSizing.BORDER, LumoUtility.Display.FLEX, LumoUtility.FlexDirection.COLUMN,
|
addClassNames(LumoUtility.BoxSizing.BORDER, LumoUtility.Display.FLEX, LumoUtility.FlexDirection.COLUMN,
|
||||||
LumoUtility.Padding.MEDIUM, LumoUtility.Gap.SMALL);
|
LumoUtility.Padding.MEDIUM, LumoUtility.Gap.SMALL);
|
||||||
|
addClassName("form-page");
|
||||||
|
|
||||||
add(new ViewToolbar(getTranslation("addcustomer.title")));
|
add(new ViewToolbar(getTranslation("addcustomer.title")));
|
||||||
add(container);
|
add(container);
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import com.vaadin.flow.component.UI;
|
|||||||
|
|
||||||
import com.vaadin.flow.component.datepicker.DatePicker;
|
import com.vaadin.flow.component.datepicker.DatePicker;
|
||||||
import com.vaadin.flow.component.html.Div;
|
import com.vaadin.flow.component.html.Div;
|
||||||
import com.vaadin.flow.component.html.H2;
|
|
||||||
import com.vaadin.flow.component.timepicker.TimePicker;
|
import com.vaadin.flow.component.timepicker.TimePicker;
|
||||||
import com.vaadin.flow.component.html.H3;
|
import com.vaadin.flow.component.html.H3;
|
||||||
import com.vaadin.flow.component.html.Main;
|
import com.vaadin.flow.component.html.Main;
|
||||||
@@ -67,6 +66,7 @@ import de.assecutor.votianlt.pages.base.ui.component.DeliveryStationTile;
|
|||||||
import de.assecutor.votianlt.pages.base.ui.component.StationTile;
|
import de.assecutor.votianlt.pages.base.ui.component.StationTile;
|
||||||
import de.assecutor.votianlt.pages.base.ui.component.PickupStationDialog;
|
import de.assecutor.votianlt.pages.base.ui.component.PickupStationDialog;
|
||||||
import de.assecutor.votianlt.pages.base.ui.component.DeliveryStationDialog;
|
import de.assecutor.votianlt.pages.base.ui.component.DeliveryStationDialog;
|
||||||
|
import de.assecutor.votianlt.pages.base.ui.component.ViewToolbar;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalTime;
|
import java.time.LocalTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
@@ -338,9 +338,9 @@ public class AddJobView extends Main implements HasDynamicTitle {
|
|||||||
setSizeFull();
|
setSizeFull();
|
||||||
addClassNames(LumoUtility.BoxSizing.BORDER, LumoUtility.Display.FLEX, LumoUtility.FlexDirection.COLUMN,
|
addClassNames(LumoUtility.BoxSizing.BORDER, LumoUtility.Display.FLEX, LumoUtility.FlexDirection.COLUMN,
|
||||||
LumoUtility.Padding.MEDIUM, LumoUtility.Gap.SMALL);
|
LumoUtility.Padding.MEDIUM, LumoUtility.Gap.SMALL);
|
||||||
|
addClassNames("form-page", "add-job-view");
|
||||||
|
|
||||||
H2 title = new H2(getTranslation("addjob.title"));
|
add(new ViewToolbar(getTranslation("addjob.title")));
|
||||||
add(title);
|
|
||||||
|
|
||||||
// Add content directly (no tabs)
|
// Add content directly (no tabs)
|
||||||
add(createCustomerAndAddressesTab());
|
add(createCustomerAndAddressesTab());
|
||||||
@@ -350,6 +350,7 @@ public class AddJobView extends Main implements HasDynamicTitle {
|
|||||||
submitButtonLayout.setWidthFull();
|
submitButtonLayout.setWidthFull();
|
||||||
submitButtonLayout.setJustifyContentMode(FlexComponent.JustifyContentMode.CENTER);
|
submitButtonLayout.setJustifyContentMode(FlexComponent.JustifyContentMode.CENTER);
|
||||||
submitButtonLayout.setPadding(true);
|
submitButtonLayout.setPadding(true);
|
||||||
|
submitButtonLayout.addClassNames("surface-panel", "form-shell");
|
||||||
submitButtonLayout.add(submitButton);
|
submitButtonLayout.add(submitButton);
|
||||||
submitButtonLayout.setVisible(false);
|
submitButtonLayout.setVisible(false);
|
||||||
|
|
||||||
@@ -361,14 +362,13 @@ public class AddJobView extends Main implements HasDynamicTitle {
|
|||||||
tabContent.setWidthFull();
|
tabContent.setWidthFull();
|
||||||
tabContent.setPadding(true);
|
tabContent.setPadding(true);
|
||||||
tabContent.setSpacing(true);
|
tabContent.setSpacing(true);
|
||||||
|
tabContent.addClassNames("form-shell", "form-card");
|
||||||
|
|
||||||
// 3x3 Grid container for station tiles
|
// 3x3 Grid container for station tiles
|
||||||
stationsGridContainer = new Div();
|
stationsGridContainer = new Div();
|
||||||
stationsGridContainer.getStyle().set("display", "grid");
|
|
||||||
stationsGridContainer.getStyle().set("grid-template-columns", "repeat(4, 1fr)");
|
|
||||||
stationsGridContainer.getStyle().set("gap", "var(--lumo-space-m)");
|
|
||||||
stationsGridContainer.getStyle().set("padding", "var(--lumo-space-s)");
|
stationsGridContainer.getStyle().set("padding", "var(--lumo-space-s)");
|
||||||
stationsGridContainer.setWidthFull();
|
stationsGridContainer.setWidthFull();
|
||||||
|
stationsGridContainer.addClassName("stations-grid");
|
||||||
|
|
||||||
// Pickup tile (always present)
|
// Pickup tile (always present)
|
||||||
pickupTile = new StationTile(StationTile.StationType.PICKUP, 0, getTranslation("addjob.section.pickup"), false);
|
pickupTile = new StationTile(StationTile.StationType.PICKUP, 0, getTranslation("addjob.section.pickup"), false);
|
||||||
@@ -394,6 +394,7 @@ public class AddJobView extends Main implements HasDynamicTitle {
|
|||||||
priceAndDetailsSection.setPadding(false);
|
priceAndDetailsSection.setPadding(false);
|
||||||
priceAndDetailsSection.setSpacing(true);
|
priceAndDetailsSection.setSpacing(true);
|
||||||
priceAndDetailsSection.setVisible(false);
|
priceAndDetailsSection.setVisible(false);
|
||||||
|
priceAndDetailsSection.addClassName("content-panel");
|
||||||
|
|
||||||
// "Stationen übernehmen" Button
|
// "Stationen übernehmen" Button
|
||||||
applyStationsButton = new Button(getTranslation("addjob.stations.apply"));
|
applyStationsButton = new Button(getTranslation("addjob.stations.apply"));
|
||||||
@@ -413,6 +414,7 @@ public class AddJobView extends Main implements HasDynamicTitle {
|
|||||||
routeInfoBox.setWidthFull();
|
routeInfoBox.setWidthFull();
|
||||||
routeInfoBox.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.STRETCH);
|
routeInfoBox.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.STRETCH);
|
||||||
routeInfoBox.setVisible(false); // Initial versteckt
|
routeInfoBox.setVisible(false); // Initial versteckt
|
||||||
|
routeInfoBox.addClassName("route-card");
|
||||||
|
|
||||||
H3 routeTitle = new H3(getTranslation("addjob.route.title"));
|
H3 routeTitle = new H3(getTranslation("addjob.route.title"));
|
||||||
routeTitle.getStyle().set("margin", "0");
|
routeTitle.getStyle().set("margin", "0");
|
||||||
@@ -456,6 +458,7 @@ public class AddJobView extends Main implements HasDynamicTitle {
|
|||||||
manualRouteInputBox.setWidthFull();
|
manualRouteInputBox.setWidthFull();
|
||||||
manualRouteInputBox.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.STRETCH);
|
manualRouteInputBox.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.STRETCH);
|
||||||
manualRouteInputBox.setVisible(true); // Initial sichtbar
|
manualRouteInputBox.setVisible(true); // Initial sichtbar
|
||||||
|
manualRouteInputBox.addClassName("route-card");
|
||||||
|
|
||||||
H3 manualRouteTitle = new H3(getTranslation("addjob.route.manual.title"));
|
H3 manualRouteTitle = new H3(getTranslation("addjob.route.manual.title"));
|
||||||
manualRouteTitle.getStyle().set("margin", "0");
|
manualRouteTitle.getStyle().set("margin", "0");
|
||||||
@@ -514,6 +517,7 @@ public class AddJobView extends Main implements HasDynamicTitle {
|
|||||||
servicesGrid.setWidthFull();
|
servicesGrid.setWidthFull();
|
||||||
servicesGrid.setHeight("250px");
|
servicesGrid.setHeight("250px");
|
||||||
servicesGrid.setItems(selectedServices);
|
servicesGrid.setItems(selectedServices);
|
||||||
|
servicesGrid.addClassName("data-grid");
|
||||||
|
|
||||||
servicesGrid.addColumn(entry -> entry.getService().getName()).setHeader(getTranslation("common.service"))
|
servicesGrid.addColumn(entry -> entry.getService().getName()).setHeader(getTranslation("common.service"))
|
||||||
.setSortable(true);
|
.setSortable(true);
|
||||||
@@ -585,6 +589,7 @@ public class AddJobView extends Main implements HasDynamicTitle {
|
|||||||
summaryLayout.getStyle().set("background-color", "var(--lumo-contrast-5pct)");
|
summaryLayout.getStyle().set("background-color", "var(--lumo-contrast-5pct)");
|
||||||
summaryLayout.setWidthFull();
|
summaryLayout.setWidthFull();
|
||||||
summaryLayout.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.STRETCH);
|
summaryLayout.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.STRETCH);
|
||||||
|
summaryLayout.addClassName("summary-card");
|
||||||
|
|
||||||
H3 summaryTitle = new H3(getTranslation("addjob.summary.title"));
|
H3 summaryTitle = new H3(getTranslation("addjob.summary.title"));
|
||||||
summaryTitle.getStyle().set("margin", "0");
|
summaryTitle.getStyle().set("margin", "0");
|
||||||
@@ -635,6 +640,7 @@ public class AddJobView extends Main implements HasDynamicTitle {
|
|||||||
|
|
||||||
private Div createAddStationButton() {
|
private Div createAddStationButton() {
|
||||||
Div button = new Div();
|
Div button = new Div();
|
||||||
|
button.addClassName("add-station-tile");
|
||||||
button.getStyle().set("border", "2px dashed var(--lumo-contrast-30pct)");
|
button.getStyle().set("border", "2px dashed var(--lumo-contrast-30pct)");
|
||||||
button.getStyle().set("border-radius", "var(--lumo-border-radius-m)");
|
button.getStyle().set("border-radius", "var(--lumo-border-radius-m)");
|
||||||
button.getStyle().set("display", "flex");
|
button.getStyle().set("display", "flex");
|
||||||
@@ -664,12 +670,14 @@ public class AddJobView extends Main implements HasDynamicTitle {
|
|||||||
|
|
||||||
private Div createStationSlot(Component content, Span distanceChip) {
|
private Div createStationSlot(Component content, Span distanceChip) {
|
||||||
Div slot = new Div();
|
Div slot = new Div();
|
||||||
|
slot.addClassName("station-slot");
|
||||||
slot.getStyle().set("display", "flex");
|
slot.getStyle().set("display", "flex");
|
||||||
slot.getStyle().set("flex-direction", "column");
|
slot.getStyle().set("flex-direction", "column");
|
||||||
slot.getStyle().set("align-items", "center");
|
slot.getStyle().set("align-items", "center");
|
||||||
slot.getStyle().set("gap", "var(--lumo-space-s)");
|
slot.getStyle().set("gap", "var(--lumo-space-s)");
|
||||||
slot.setWidthFull();
|
slot.setWidthFull();
|
||||||
|
|
||||||
|
content.getElement().getClassList().add("station-slot-content");
|
||||||
content.getElement().getStyle().set("width", "100%");
|
content.getElement().getStyle().set("width", "100%");
|
||||||
slot.add(content);
|
slot.add(content);
|
||||||
|
|
||||||
|
|||||||
@@ -61,24 +61,29 @@ public class AdminDashboardView extends Main implements HasDynamicTitle {
|
|||||||
setSizeFull();
|
setSizeFull();
|
||||||
addClassNames(LumoUtility.BoxSizing.BORDER, LumoUtility.Display.FLEX, LumoUtility.FlexDirection.COLUMN,
|
addClassNames(LumoUtility.BoxSizing.BORDER, LumoUtility.Display.FLEX, LumoUtility.FlexDirection.COLUMN,
|
||||||
LumoUtility.Padding.MEDIUM);
|
LumoUtility.Padding.MEDIUM);
|
||||||
|
addClassName("dashboard-view");
|
||||||
|
|
||||||
// Header
|
// Header
|
||||||
H1 title = new H1(getTranslation("admindashboard.title"));
|
H1 title = new H1(getTranslation("admindashboard.title"));
|
||||||
title.addClassNames(LumoUtility.Margin.Bottom.MEDIUM, LumoUtility.Margin.Top.NONE);
|
title.addClassNames(LumoUtility.Margin.Bottom.MEDIUM, LumoUtility.Margin.Top.NONE);
|
||||||
|
title.addClassName("dashboard-title");
|
||||||
|
|
||||||
HorizontalLayout header = new HorizontalLayout(title);
|
HorizontalLayout header = new HorizontalLayout(title);
|
||||||
header.setAlignItems(FlexComponent.Alignment.CENTER);
|
header.setAlignItems(FlexComponent.Alignment.CENTER);
|
||||||
header.setWidthFull();
|
header.setWidthFull();
|
||||||
|
header.addClassNames("surface-panel", "dashboard-header");
|
||||||
|
|
||||||
// Statistics container
|
// Statistics container
|
||||||
statisticsContainer = new Div();
|
statisticsContainer = new Div();
|
||||||
statisticsContainer.setSizeFull();
|
statisticsContainer.setSizeFull();
|
||||||
|
statisticsContainer.addClassName("dashboard-content");
|
||||||
|
|
||||||
// Content container
|
// Content container
|
||||||
VerticalLayout content = new VerticalLayout(header, statisticsContainer);
|
VerticalLayout content = new VerticalLayout(header, statisticsContainer);
|
||||||
content.setSizeFull();
|
content.setSizeFull();
|
||||||
content.setPadding(false);
|
content.setPadding(false);
|
||||||
content.setSpacing(true);
|
content.setSpacing(true);
|
||||||
|
content.addClassName("dashboard-sections");
|
||||||
|
|
||||||
add(content);
|
add(content);
|
||||||
|
|
||||||
@@ -114,6 +119,7 @@ public class AdminDashboardView extends Main implements HasDynamicTitle {
|
|||||||
VerticalLayout mainLayout = new VerticalLayout();
|
VerticalLayout mainLayout = new VerticalLayout();
|
||||||
mainLayout.setPadding(false);
|
mainLayout.setPadding(false);
|
||||||
mainLayout.setSpacing(true);
|
mainLayout.setSpacing(true);
|
||||||
|
mainLayout.addClassName("dashboard-sections");
|
||||||
|
|
||||||
// System overview section
|
// System overview section
|
||||||
mainLayout.add(createSystemOverviewSection());
|
mainLayout.add(createSystemOverviewSection());
|
||||||
@@ -136,15 +142,13 @@ public class AdminDashboardView extends Main implements HasDynamicTitle {
|
|||||||
private VerticalLayout createSystemOverviewSection() {
|
private VerticalLayout createSystemOverviewSection() {
|
||||||
VerticalLayout section = new VerticalLayout();
|
VerticalLayout section = new VerticalLayout();
|
||||||
section.setPadding(false);
|
section.setPadding(false);
|
||||||
section.addClassName(LumoUtility.Background.CONTRAST_5);
|
section.addClassNames("surface-panel", "dashboard-section");
|
||||||
section.getStyle().set("border-radius", "8px").set("padding", "1rem");
|
|
||||||
|
|
||||||
H3 title = new H3(getTranslation("admindashboard.section.overview"));
|
H3 title = new H3(getTranslation("admindashboard.section.overview"));
|
||||||
title.addClassName(LumoUtility.Margin.Bottom.MEDIUM);
|
title.addClassNames(LumoUtility.Margin.Bottom.MEDIUM, "dashboard-section-title");
|
||||||
|
|
||||||
HorizontalLayout cards = new HorizontalLayout();
|
Div cards = new Div();
|
||||||
cards.setWidthFull();
|
cards.addClassName("dashboard-grid");
|
||||||
cards.setSpacing(true);
|
|
||||||
|
|
||||||
// Total jobs card
|
// Total jobs card
|
||||||
long totalJobs = jobRepository.count();
|
long totalJobs = jobRepository.count();
|
||||||
@@ -173,15 +177,13 @@ public class AdminDashboardView extends Main implements HasDynamicTitle {
|
|||||||
private VerticalLayout createJobStatisticsSection() {
|
private VerticalLayout createJobStatisticsSection() {
|
||||||
VerticalLayout section = new VerticalLayout();
|
VerticalLayout section = new VerticalLayout();
|
||||||
section.setPadding(false);
|
section.setPadding(false);
|
||||||
section.addClassName(LumoUtility.Background.CONTRAST_5);
|
section.addClassNames("surface-panel", "dashboard-section");
|
||||||
section.getStyle().set("border-radius", "8px").set("padding", "1rem");
|
|
||||||
|
|
||||||
H3 title = new H3(getTranslation("admindashboard.section.jobs"));
|
H3 title = new H3(getTranslation("admindashboard.section.jobs"));
|
||||||
title.addClassName(LumoUtility.Margin.Bottom.MEDIUM);
|
title.addClassNames(LumoUtility.Margin.Bottom.MEDIUM, "dashboard-section-title");
|
||||||
|
|
||||||
HorizontalLayout cards = new HorizontalLayout();
|
Div cards = new Div();
|
||||||
cards.setWidthFull();
|
cards.addClassName("dashboard-grid");
|
||||||
cards.setSpacing(true);
|
|
||||||
|
|
||||||
// Jobs by status
|
// Jobs by status
|
||||||
try {
|
try {
|
||||||
@@ -214,15 +216,13 @@ public class AdminDashboardView extends Main implements HasDynamicTitle {
|
|||||||
private VerticalLayout createTaskStatisticsSection() {
|
private VerticalLayout createTaskStatisticsSection() {
|
||||||
VerticalLayout section = new VerticalLayout();
|
VerticalLayout section = new VerticalLayout();
|
||||||
section.setPadding(false);
|
section.setPadding(false);
|
||||||
section.addClassName(LumoUtility.Background.CONTRAST_5);
|
section.addClassNames("surface-panel", "dashboard-section");
|
||||||
section.getStyle().set("border-radius", "8px").set("padding", "1rem");
|
|
||||||
|
|
||||||
H3 title = new H3(getTranslation("admindashboard.section.tasks"));
|
H3 title = new H3(getTranslation("admindashboard.section.tasks"));
|
||||||
title.addClassName(LumoUtility.Margin.Bottom.MEDIUM);
|
title.addClassNames(LumoUtility.Margin.Bottom.MEDIUM, "dashboard-section-title");
|
||||||
|
|
||||||
HorizontalLayout cards = new HorizontalLayout();
|
Div cards = new Div();
|
||||||
cards.setWidthFull();
|
cards.addClassName("dashboard-grid");
|
||||||
cards.setSpacing(true);
|
|
||||||
|
|
||||||
// Total tasks
|
// Total tasks
|
||||||
long totalTasks = taskRepository.count();
|
long totalTasks = taskRepository.count();
|
||||||
@@ -251,15 +251,13 @@ public class AdminDashboardView extends Main implements HasDynamicTitle {
|
|||||||
private VerticalLayout createUserStatisticsSection() {
|
private VerticalLayout createUserStatisticsSection() {
|
||||||
VerticalLayout section = new VerticalLayout();
|
VerticalLayout section = new VerticalLayout();
|
||||||
section.setPadding(false);
|
section.setPadding(false);
|
||||||
section.addClassName(LumoUtility.Background.CONTRAST_5);
|
section.addClassNames("surface-panel", "dashboard-section");
|
||||||
section.getStyle().set("border-radius", "8px").set("padding", "1rem");
|
|
||||||
|
|
||||||
H3 title = new H3(getTranslation("admindashboard.section.users"));
|
H3 title = new H3(getTranslation("admindashboard.section.users"));
|
||||||
title.addClassName(LumoUtility.Margin.Bottom.MEDIUM);
|
title.addClassNames(LumoUtility.Margin.Bottom.MEDIUM, "dashboard-section-title");
|
||||||
|
|
||||||
HorizontalLayout cards = new HorizontalLayout();
|
Div cards = new Div();
|
||||||
cards.setWidthFull();
|
cards.addClassName("dashboard-grid");
|
||||||
cards.setSpacing(true);
|
|
||||||
|
|
||||||
// Content statistics
|
// Content statistics
|
||||||
long totalPhotos = photoRepository.count();
|
long totalPhotos = photoRepository.count();
|
||||||
@@ -285,15 +283,13 @@ public class AdminDashboardView extends Main implements HasDynamicTitle {
|
|||||||
private VerticalLayout createSystemHealthSection() {
|
private VerticalLayout createSystemHealthSection() {
|
||||||
VerticalLayout section = new VerticalLayout();
|
VerticalLayout section = new VerticalLayout();
|
||||||
section.setPadding(false);
|
section.setPadding(false);
|
||||||
section.addClassName(LumoUtility.Background.CONTRAST_5);
|
section.addClassNames("surface-panel", "dashboard-section");
|
||||||
section.getStyle().set("border-radius", "8px").set("padding", "1rem");
|
|
||||||
|
|
||||||
H3 title = new H3(getTranslation("admindashboard.section.health"));
|
H3 title = new H3(getTranslation("admindashboard.section.health"));
|
||||||
title.addClassName(LumoUtility.Margin.Bottom.MEDIUM);
|
title.addClassNames(LumoUtility.Margin.Bottom.MEDIUM, "dashboard-section-title");
|
||||||
|
|
||||||
HorizontalLayout cards = new HorizontalLayout();
|
Div cards = new Div();
|
||||||
cards.setWidthFull();
|
cards.addClassName("dashboard-grid");
|
||||||
cards.setSpacing(true);
|
|
||||||
|
|
||||||
// Database connection status
|
// Database connection status
|
||||||
try {
|
try {
|
||||||
@@ -327,31 +323,31 @@ public class AdminDashboardView extends Main implements HasDynamicTitle {
|
|||||||
|
|
||||||
private Div createStatCard(String title, String value, VaadinIcon icon, String color) {
|
private Div createStatCard(String title, String value, VaadinIcon icon, String color) {
|
||||||
Div card = new Div();
|
Div card = new Div();
|
||||||
card.addClassName(LumoUtility.Background.BASE);
|
card.addClassNames("dashboard-stat-card", "accent-" + color);
|
||||||
card.getStyle().set("border-radius", "8px").set("padding", "1rem")
|
|
||||||
.set("box-shadow", "0 2px 4px rgba(0,0,0,0.1)").set("min-width", "200px")
|
|
||||||
.set("border-left", "4px solid var(--lumo-" + color + "-color, #007bff)");
|
|
||||||
|
|
||||||
HorizontalLayout header = new HorizontalLayout();
|
HorizontalLayout header = new HorizontalLayout();
|
||||||
header.setAlignItems(FlexComponent.Alignment.CENTER);
|
header.setAlignItems(FlexComponent.Alignment.CENTER);
|
||||||
header.setJustifyContentMode(FlexComponent.JustifyContentMode.BETWEEN);
|
header.setJustifyContentMode(FlexComponent.JustifyContentMode.BETWEEN);
|
||||||
|
header.addClassName("dashboard-stat-card-header");
|
||||||
|
|
||||||
Icon cardIcon = icon.create();
|
Icon cardIcon = icon.create();
|
||||||
cardIcon.getStyle().set("color", "var(--lumo-" + color + "-color, #007bff)");
|
cardIcon.addClassNames("dashboard-stat-icon", "accent-" + color);
|
||||||
|
|
||||||
Span titleSpan = new Span(title);
|
Span titleSpan = new Span(title);
|
||||||
titleSpan.addClassName(LumoUtility.FontSize.SMALL);
|
titleSpan.addClassName(LumoUtility.FontSize.SMALL);
|
||||||
titleSpan.getStyle().set("color", "var(--lumo-secondary-text-color)");
|
titleSpan.addClassName("dashboard-stat-title");
|
||||||
|
|
||||||
header.add(titleSpan, cardIcon);
|
header.add(titleSpan, cardIcon);
|
||||||
|
|
||||||
Span valueSpan = new Span(value);
|
Span valueSpan = new Span(value);
|
||||||
valueSpan.addClassName(LumoUtility.FontSize.XLARGE);
|
valueSpan.addClassName(LumoUtility.FontSize.XLARGE);
|
||||||
valueSpan.addClassName(LumoUtility.FontWeight.BOLD);
|
valueSpan.addClassName(LumoUtility.FontWeight.BOLD);
|
||||||
|
valueSpan.addClassName("dashboard-stat-value");
|
||||||
|
|
||||||
VerticalLayout content = new VerticalLayout(header, valueSpan);
|
VerticalLayout content = new VerticalLayout(header, valueSpan);
|
||||||
content.setPadding(false);
|
content.setPadding(false);
|
||||||
content.setSpacing(false);
|
content.setSpacing(false);
|
||||||
|
content.addClassName("dashboard-stat-content");
|
||||||
|
|
||||||
card.add(content);
|
card.add(content);
|
||||||
return card;
|
return card;
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
package de.assecutor.votianlt.pages.view;
|
package de.assecutor.votianlt.pages.view;
|
||||||
|
|
||||||
import com.vaadin.flow.component.button.Button;
|
import com.vaadin.flow.component.button.Button;
|
||||||
import com.vaadin.flow.component.html.H2;
|
import com.vaadin.flow.component.html.Div;
|
||||||
import com.vaadin.flow.component.notification.Notification;
|
import com.vaadin.flow.component.notification.Notification;
|
||||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
||||||
import com.vaadin.flow.component.textfield.TextField;
|
import com.vaadin.flow.component.textfield.TextField;
|
||||||
import com.vaadin.flow.router.HasDynamicTitle;
|
import com.vaadin.flow.router.HasDynamicTitle;
|
||||||
import com.vaadin.flow.router.Route;
|
import com.vaadin.flow.router.Route;
|
||||||
import de.assecutor.votianlt.model.PriceTable;
|
import de.assecutor.votianlt.model.PriceTable;
|
||||||
|
import de.assecutor.votianlt.pages.base.ui.component.ViewToolbar;
|
||||||
import de.assecutor.votianlt.pages.base.ui.view.AdminLayout;
|
import de.assecutor.votianlt.pages.base.ui.view.AdminLayout;
|
||||||
import de.assecutor.votianlt.repository.PriceTableRepository;
|
import de.assecutor.votianlt.repository.PriceTableRepository;
|
||||||
import jakarta.annotation.security.RolesAllowed;
|
import jakarta.annotation.security.RolesAllowed;
|
||||||
@@ -28,13 +29,13 @@ public class AdminPricetableView extends VerticalLayout implements HasDynamicTit
|
|||||||
setPadding(false);
|
setPadding(false);
|
||||||
getStyle().set("margin", "14px");
|
getStyle().set("margin", "14px");
|
||||||
setWidth("90%");
|
setWidth("90%");
|
||||||
|
addClassName("admin-form-view");
|
||||||
H2 title = new H2(getTranslation("adminpricetable.title"));
|
add(new ViewToolbar(getTranslation("adminpricetable.title")));
|
||||||
add(title);
|
|
||||||
|
|
||||||
VerticalLayout fieldsLayout = new VerticalLayout();
|
VerticalLayout fieldsLayout = new VerticalLayout();
|
||||||
fieldsLayout.setSpacing(true);
|
fieldsLayout.setSpacing(true);
|
||||||
fieldsLayout.setPadding(false);
|
fieldsLayout.setPadding(false);
|
||||||
|
fieldsLayout.addClassNames("form-shell", "form-card");
|
||||||
|
|
||||||
monthlyBasePackage = new TextField();
|
monthlyBasePackage = new TextField();
|
||||||
monthlyBasePackage.setLabel(getTranslation("adminpricetable.field.monthly"));
|
monthlyBasePackage.setLabel(getTranslation("adminpricetable.field.monthly"));
|
||||||
@@ -58,8 +59,12 @@ public class AdminPricetableView extends VerticalLayout implements HasDynamicTit
|
|||||||
Button saveButton = new Button(getTranslation("button.savechanges"));
|
Button saveButton = new Button(getTranslation("button.savechanges"));
|
||||||
saveButton.getStyle().set("margin-top", "20px");
|
saveButton.getStyle().set("margin-top", "20px");
|
||||||
saveButton.addClickListener(e -> savePriceTable());
|
saveButton.addClickListener(e -> savePriceTable());
|
||||||
|
saveButton.addThemeVariants(com.vaadin.flow.component.button.ButtonVariant.LUMO_PRIMARY);
|
||||||
|
|
||||||
add(saveButton);
|
Div actions = new Div(saveButton);
|
||||||
|
actions.addClassNames("form-shell", "simple-card");
|
||||||
|
|
||||||
|
add(fieldsLayout, actions);
|
||||||
|
|
||||||
// Load existing data
|
// Load existing data
|
||||||
loadPriceTable();
|
loadPriceTable();
|
||||||
|
|||||||
@@ -3,15 +3,14 @@ package de.assecutor.votianlt.pages.view;
|
|||||||
import com.vaadin.flow.component.button.Button;
|
import com.vaadin.flow.component.button.Button;
|
||||||
import com.vaadin.flow.component.button.ButtonVariant;
|
import com.vaadin.flow.component.button.ButtonVariant;
|
||||||
import com.vaadin.flow.component.grid.Grid;
|
import com.vaadin.flow.component.grid.Grid;
|
||||||
import com.vaadin.flow.component.html.H2;
|
import com.vaadin.flow.component.html.Div;
|
||||||
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.orderedlayout.FlexComponent;
|
|
||||||
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.router.HasDynamicTitle;
|
import com.vaadin.flow.router.HasDynamicTitle;
|
||||||
import com.vaadin.flow.router.Route;
|
import com.vaadin.flow.router.Route;
|
||||||
import de.assecutor.votianlt.model.AppUser;
|
import de.assecutor.votianlt.model.AppUser;
|
||||||
|
import de.assecutor.votianlt.pages.base.ui.component.ViewToolbar;
|
||||||
import de.assecutor.votianlt.pages.service.AppUserService;
|
import de.assecutor.votianlt.pages.service.AppUserService;
|
||||||
import jakarta.annotation.security.RolesAllowed;
|
import jakarta.annotation.security.RolesAllowed;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@@ -30,26 +29,18 @@ public class AppUserView extends VerticalLayout implements HasDynamicTitle {
|
|||||||
setSizeFull();
|
setSizeFull();
|
||||||
setPadding(true);
|
setPadding(true);
|
||||||
setSpacing(true);
|
setSpacing(true);
|
||||||
|
addClassName("data-view");
|
||||||
// Header mit Titel und Button
|
|
||||||
HorizontalLayout header = new HorizontalLayout();
|
|
||||||
header.setWidthFull();
|
|
||||||
header.setAlignItems(FlexComponent.Alignment.CENTER);
|
|
||||||
|
|
||||||
H2 title = new H2(getTranslation("appuser.title"));
|
|
||||||
title.getStyle().set("margin", "0");
|
|
||||||
|
|
||||||
Button addButton = new Button(getTranslation("appuser.button.add"), new Icon(VaadinIcon.PLUS));
|
Button addButton = new Button(getTranslation("appuser.button.add"), new Icon(VaadinIcon.PLUS));
|
||||||
addButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
addButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
||||||
addButton.addClickListener(e -> navigateToAddAppUser());
|
addButton.addClickListener(e -> navigateToAddAppUser());
|
||||||
|
|
||||||
header.add(title, addButton);
|
add(new ViewToolbar(getTranslation("appuser.title"), addButton));
|
||||||
header.setJustifyContentMode(FlexComponent.JustifyContentMode.BETWEEN);
|
|
||||||
add(header);
|
|
||||||
|
|
||||||
// Grid für App-Nutzer
|
// Grid für App-Nutzer
|
||||||
appUserGrid = new Grid<>(AppUser.class, false);
|
appUserGrid = new Grid<>(AppUser.class, false);
|
||||||
appUserGrid.setSizeFull();
|
appUserGrid.setSizeFull();
|
||||||
|
appUserGrid.addClassName("data-grid");
|
||||||
|
|
||||||
// Grid-Spalten konfigurieren
|
// Grid-Spalten konfigurieren
|
||||||
appUserGrid.addColumn(AppUser::getBezeichnung).setHeader(getTranslation("appuser.column.designation"))
|
appUserGrid.addColumn(AppUser::getBezeichnung).setHeader(getTranslation("appuser.column.designation"))
|
||||||
@@ -75,7 +66,11 @@ public class AppUserView extends VerticalLayout implements HasDynamicTitle {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
add(appUserGrid);
|
Div gridPanel = new Div(appUserGrid);
|
||||||
|
gridPanel.addClassNames("surface-panel", "data-grid-panel");
|
||||||
|
gridPanel.setSizeFull();
|
||||||
|
|
||||||
|
add(gridPanel);
|
||||||
|
|
||||||
// Daten laden
|
// Daten laden
|
||||||
loadAppUsers();
|
loadAppUsers();
|
||||||
|
|||||||
@@ -31,8 +31,9 @@ public class AuthenticatedStartView extends VerticalLayout implements HasDynamic
|
|||||||
|
|
||||||
setSizeFull();
|
setSizeFull();
|
||||||
setPadding(false);
|
setPadding(false);
|
||||||
setSpacing(false);
|
setSpacing(true);
|
||||||
setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.STRETCH);
|
setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.STRETCH);
|
||||||
|
addClassName("dashboard-home-view");
|
||||||
|
|
||||||
// Hero Section for authenticated users
|
// Hero Section for authenticated users
|
||||||
add(createAuthenticatedHeroSection());
|
add(createAuthenticatedHeroSection());
|
||||||
@@ -53,10 +54,8 @@ public class AuthenticatedStartView extends VerticalLayout implements HasDynamic
|
|||||||
heroSection.setPadding(true);
|
heroSection.setPadding(true);
|
||||||
heroSection.setSpacing(true);
|
heroSection.setSpacing(true);
|
||||||
heroSection.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.CENTER);
|
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);
|
heroSection.setJustifyContentMode(FlexComponent.JustifyContentMode.CENTER);
|
||||||
|
heroSection.addClassNames("page-shell", "hero-panel", "landing-hero");
|
||||||
|
|
||||||
// Welcome message for authenticated user
|
// Welcome message for authenticated user
|
||||||
User currentUser = securityService.getCurrentDatabaseUser();
|
User currentUser = securityService.getCurrentDatabaseUser();
|
||||||
@@ -64,14 +63,10 @@ public class AuthenticatedStartView extends VerticalLayout implements HasDynamic
|
|||||||
? currentUser.getFirstname() + " " + currentUser.getName()
|
? currentUser.getFirstname() + " " + currentUser.getName()
|
||||||
: securityService.getCurrentUsername();
|
: securityService.getCurrentUsername();
|
||||||
H1 welcomeTitle = new H1(getTranslation("dashboard.welcome", displayName));
|
H1 welcomeTitle = new H1(getTranslation("dashboard.welcome", displayName));
|
||||||
welcomeTitle.getStyle().set("text-align", "center");
|
welcomeTitle.addClassName("hero-panel-title");
|
||||||
welcomeTitle.getStyle().set("color", "var(--lumo-primary-text-color)");
|
|
||||||
welcomeTitle.getStyle().set("margin-bottom", "var(--lumo-space-l)");
|
|
||||||
|
|
||||||
Paragraph welcomeDescription = new Paragraph(getTranslation("dashboard.description"));
|
Paragraph welcomeDescription = new Paragraph(getTranslation("dashboard.description"));
|
||||||
welcomeDescription.getStyle().set("text-align", "center");
|
welcomeDescription.addClassName("hero-panel-text");
|
||||||
welcomeDescription.getStyle().set("max-width", "600px");
|
|
||||||
welcomeDescription.getStyle().set("font-size", "var(--lumo-font-size-l)");
|
|
||||||
|
|
||||||
heroSection.add(welcomeTitle, welcomeDescription);
|
heroSection.add(welcomeTitle, welcomeDescription);
|
||||||
return heroSection;
|
return heroSection;
|
||||||
@@ -83,26 +78,18 @@ public class AuthenticatedStartView extends VerticalLayout implements HasDynamic
|
|||||||
systemSection.setPadding(true);
|
systemSection.setPadding(true);
|
||||||
systemSection.setSpacing(true);
|
systemSection.setSpacing(true);
|
||||||
systemSection.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.CENTER);
|
systemSection.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.CENTER);
|
||||||
systemSection.getStyle().set("background-color", "var(--lumo-base-color)");
|
systemSection.addClassNames("page-shell", "surface-panel", "section-panel");
|
||||||
|
|
||||||
// Section Header
|
// Section Header
|
||||||
H2 systemTitle = new H2(getTranslation("dashboard.system.title"));
|
H2 systemTitle = new H2(getTranslation("dashboard.system.title"));
|
||||||
systemTitle.getStyle().set("color", "var(--lumo-primary-color)");
|
systemTitle.addClassName("section-title");
|
||||||
systemTitle.getStyle().set("text-align", "center");
|
|
||||||
|
|
||||||
Paragraph systemIntro = new Paragraph(getTranslation("dashboard.system.intro"));
|
Paragraph systemIntro = new Paragraph(getTranslation("dashboard.system.intro"));
|
||||||
systemIntro.getStyle().set("text-align", "center");
|
systemIntro.addClassName("section-intro");
|
||||||
systemIntro.getStyle().set("max-width", "800px");
|
|
||||||
systemIntro.getStyle().set("margin-bottom", "var(--lumo-space-xl)");
|
|
||||||
|
|
||||||
// Features Grid - using Div with CSS flexbox for proper centering
|
// Features Grid - using Div with CSS flexbox for proper centering
|
||||||
Div featuresGrid = new Div();
|
Div featuresGrid = new Div();
|
||||||
featuresGrid.getStyle().set("display", "flex");
|
featuresGrid.addClassName("tile-grid");
|
||||||
featuresGrid.getStyle().set("flex-wrap", "wrap");
|
|
||||||
featuresGrid.getStyle().set("justify-content", "center");
|
|
||||||
featuresGrid.getStyle().set("align-items", "stretch");
|
|
||||||
featuresGrid.getStyle().set("gap", "var(--lumo-space-l)");
|
|
||||||
featuresGrid.getStyle().set("width", "100%");
|
|
||||||
|
|
||||||
// Feature Cards
|
// Feature Cards
|
||||||
featuresGrid.add(
|
featuresGrid.add(
|
||||||
@@ -122,32 +109,18 @@ public class AuthenticatedStartView extends VerticalLayout implements HasDynamic
|
|||||||
card.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.CENTER);
|
card.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.CENTER);
|
||||||
card.setPadding(true);
|
card.setPadding(true);
|
||||||
card.setSpacing(true);
|
card.setSpacing(true);
|
||||||
card.getStyle().set("background-color", "var(--lumo-base-color)");
|
card.setWidthFull();
|
||||||
card.getStyle().set("border", "1px solid var(--lumo-contrast-20pct)");
|
card.addClassName("feature-card");
|
||||||
card.getStyle().set("border-radius", "var(--lumo-border-radius-m)");
|
|
||||||
card.getStyle().set("box-shadow", "var(--lumo-box-shadow-s)");
|
|
||||||
card.setWidth("300px");
|
|
||||||
card.setMinWidth("300px");
|
|
||||||
card.setMaxWidth("300px");
|
|
||||||
card.setHeight("100%");
|
|
||||||
card.getStyle().set("flex-grow", "0");
|
|
||||||
card.getStyle().set("flex-shrink", "0");
|
|
||||||
|
|
||||||
Icon icon = iconType.create();
|
Icon icon = iconType.create();
|
||||||
icon.setSize("48px");
|
icon.setSize("48px");
|
||||||
icon.getStyle().set("color", "var(--lumo-primary-color)");
|
icon.addClassName("feature-card-icon");
|
||||||
icon.getStyle().set("margin-bottom", "var(--lumo-space-m)");
|
|
||||||
|
|
||||||
H3 cardTitle = new H3(title);
|
H3 cardTitle = new H3(title);
|
||||||
cardTitle.getStyle().set("text-align", "center");
|
cardTitle.addClassName("feature-card-title");
|
||||||
cardTitle.getStyle().set("margin", "0");
|
|
||||||
cardTitle.getStyle().set("color", "var(--lumo-primary-text-color)");
|
|
||||||
|
|
||||||
Paragraph cardDescription = new Paragraph(description);
|
Paragraph cardDescription = new Paragraph(description);
|
||||||
cardDescription.getStyle().set("text-align", "center");
|
cardDescription.addClassName("feature-card-description");
|
||||||
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);
|
card.add(icon, cardTitle, cardDescription);
|
||||||
return card;
|
return card;
|
||||||
@@ -159,15 +132,13 @@ public class AuthenticatedStartView extends VerticalLayout implements HasDynamic
|
|||||||
appSection.setPadding(true);
|
appSection.setPadding(true);
|
||||||
appSection.setSpacing(true);
|
appSection.setSpacing(true);
|
||||||
appSection.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.CENTER);
|
appSection.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.CENTER);
|
||||||
appSection.getStyle().set("background-color", "var(--lumo-contrast-5pct)");
|
appSection.addClassNames("page-shell", "surface-panel", "app-overview-panel");
|
||||||
|
|
||||||
H2 appTitle = new H2(getTranslation("dashboard.app.title"));
|
H2 appTitle = new H2(getTranslation("dashboard.app.title"));
|
||||||
appTitle.getStyle().set("color", "var(--lumo-primary-color)");
|
appTitle.addClassName("section-title");
|
||||||
appTitle.getStyle().set("text-align", "center");
|
|
||||||
|
|
||||||
Paragraph appDescription = new Paragraph(getTranslation("dashboard.app.description"));
|
Paragraph appDescription = new Paragraph(getTranslation("dashboard.app.description"));
|
||||||
appDescription.getStyle().set("text-align", "center");
|
appDescription.addClassName("section-intro");
|
||||||
appDescription.getStyle().set("max-width", "600px");
|
|
||||||
|
|
||||||
appSection.add(appTitle, appDescription);
|
appSection.add(appTitle, appDescription);
|
||||||
return appSection;
|
return appSection;
|
||||||
@@ -179,18 +150,14 @@ public class AuthenticatedStartView extends VerticalLayout implements HasDynamic
|
|||||||
footer.setPadding(true);
|
footer.setPadding(true);
|
||||||
footer.setSpacing(true);
|
footer.setSpacing(true);
|
||||||
footer.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.CENTER);
|
footer.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.CENTER);
|
||||||
footer.getStyle().set("background-color", "var(--lumo-contrast-10pct)");
|
footer.addClassNames("page-shell", "surface-panel", "footer-panel");
|
||||||
footer.getStyle().set("border-top", "1px solid var(--lumo-contrast-20pct)");
|
|
||||||
footer.getStyle().set("margin-top", "auto");
|
|
||||||
|
|
||||||
HorizontalLayout footerContent = new HorizontalLayout();
|
HorizontalLayout footerContent = new HorizontalLayout();
|
||||||
footerContent.setDefaultVerticalComponentAlignment(FlexComponent.Alignment.CENTER);
|
footerContent.setDefaultVerticalComponentAlignment(FlexComponent.Alignment.CENTER);
|
||||||
footerContent.setJustifyContentMode(FlexComponent.JustifyContentMode.CENTER);
|
footerContent.setJustifyContentMode(FlexComponent.JustifyContentMode.CENTER);
|
||||||
|
|
||||||
Paragraph copyright = new Paragraph(getTranslation("dashboard.footer.copyright"));
|
Paragraph copyright = new Paragraph(getTranslation("dashboard.footer.copyright"));
|
||||||
copyright.getStyle().set("color", "var(--lumo-secondary-text-color)");
|
copyright.addClassName("footer-version");
|
||||||
copyright.getStyle().set("font-size", "var(--lumo-font-size-s)");
|
|
||||||
copyright.getStyle().set("margin", "0");
|
|
||||||
|
|
||||||
footerContent.add(copyright);
|
footerContent.add(copyright);
|
||||||
footer.add(footerContent);
|
footer.add(footerContent);
|
||||||
|
|||||||
@@ -128,6 +128,7 @@ public class CreateInvoiceView extends VerticalLayout implements HasUrlParameter
|
|||||||
setSizeFull();
|
setSizeFull();
|
||||||
setPadding(true);
|
setPadding(true);
|
||||||
setSpacing(true);
|
setSpacing(true);
|
||||||
|
addClassName("data-view");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -216,9 +217,9 @@ public class CreateInvoiceView extends VerticalLayout implements HasUrlParameter
|
|||||||
|
|
||||||
private Div createJobDetailsSection() {
|
private Div createJobDetailsSection() {
|
||||||
Div section = new Div();
|
Div section = new Div();
|
||||||
section.getStyle().set("border", "1px solid var(--lumo-contrast-20pct)")
|
section.setWidthFull();
|
||||||
.set("border-radius", "var(--lumo-border-radius-m)").set("padding", "var(--lumo-space-m)")
|
section.addClassName("invoice-summary-card");
|
||||||
.set("margin-bottom", "var(--lumo-space-m)").set("width", "100%").set("box-sizing", "border-box");
|
section.getStyle().set("margin-bottom", "var(--lumo-space-m)");
|
||||||
|
|
||||||
H3 sectionTitle = new H3(getTranslation("createinvoice.section.job"));
|
H3 sectionTitle = new H3(getTranslation("createinvoice.section.job"));
|
||||||
section.add(sectionTitle);
|
section.add(sectionTitle);
|
||||||
@@ -243,13 +244,11 @@ public class CreateInvoiceView extends VerticalLayout implements HasUrlParameter
|
|||||||
|
|
||||||
private Div createRouteInfoSection() {
|
private Div createRouteInfoSection() {
|
||||||
Div section = new Div();
|
Div section = new Div();
|
||||||
section.getStyle().set("border", "1px solid var(--lumo-primary-color-50pct)")
|
section.setWidthFull();
|
||||||
.set("border-radius", "var(--lumo-border-radius-m)").set("padding", "var(--lumo-space-m)")
|
section.addClassNames("detail-card", "detail-card--accent");
|
||||||
.set("margin-bottom", "var(--lumo-space-m)").set("width", "100%").set("box-sizing", "border-box")
|
section.getStyle().set("margin-bottom", "var(--lumo-space-m)");
|
||||||
.set("background-color", "var(--lumo-primary-color-10pct)");
|
|
||||||
|
|
||||||
H3 sectionTitle = new H3(getTranslation("createinvoice.section.route"));
|
H3 sectionTitle = new H3(getTranslation("createinvoice.section.route"));
|
||||||
sectionTitle.getStyle().set("color", "var(--lumo-primary-text-color)");
|
|
||||||
section.add(sectionTitle);
|
section.add(sectionTitle);
|
||||||
|
|
||||||
VerticalLayout routeInfo = new VerticalLayout();
|
VerticalLayout routeInfo = new VerticalLayout();
|
||||||
@@ -275,9 +274,9 @@ public class CreateInvoiceView extends VerticalLayout implements HasUrlParameter
|
|||||||
|
|
||||||
private Div createServicesSelectionSection() {
|
private Div createServicesSelectionSection() {
|
||||||
servicesSection = new Div();
|
servicesSection = new Div();
|
||||||
servicesSection.getStyle().set("border", "1px solid var(--lumo-contrast-20pct)")
|
servicesSection.setWidthFull();
|
||||||
.set("border-radius", "var(--lumo-border-radius-m)").set("padding", "var(--lumo-space-m)")
|
servicesSection.addClassName("services-panel");
|
||||||
.set("margin-bottom", "var(--lumo-space-m)").set("width", "100%").set("box-sizing", "border-box");
|
servicesSection.getStyle().set("margin-bottom", "var(--lumo-space-m)");
|
||||||
|
|
||||||
H3 sectionTitle = new H3(getTranslation("createinvoice.section.services"));
|
H3 sectionTitle = new H3(getTranslation("createinvoice.section.services"));
|
||||||
servicesSection.add(sectionTitle);
|
servicesSection.add(sectionTitle);
|
||||||
@@ -286,6 +285,7 @@ public class CreateInvoiceView extends VerticalLayout implements HasUrlParameter
|
|||||||
servicesGrid = new Grid<>();
|
servicesGrid = new Grid<>();
|
||||||
servicesGrid.setWidthFull();
|
servicesGrid.setWidthFull();
|
||||||
servicesGrid.setAllRowsVisible(true);
|
servicesGrid.setAllRowsVisible(true);
|
||||||
|
servicesGrid.addClassName("data-grid");
|
||||||
|
|
||||||
// Service name column (read-only)
|
// Service name column (read-only)
|
||||||
servicesGrid.addColumn(row -> {
|
servicesGrid.addColumn(row -> {
|
||||||
@@ -333,9 +333,9 @@ public class CreateInvoiceView extends VerticalLayout implements HasUrlParameter
|
|||||||
|
|
||||||
private Div createSummarySection() {
|
private Div createSummarySection() {
|
||||||
Div section = new Div();
|
Div section = new Div();
|
||||||
section.getStyle().set("border", "1px solid var(--lumo-contrast-20pct)")
|
section.setWidthFull();
|
||||||
.set("border-radius", "var(--lumo-border-radius-m)").set("padding", "var(--lumo-space-m)")
|
section.addClassName("invoice-section-card");
|
||||||
.set("margin-bottom", "var(--lumo-space-m)").set("width", "100%").set("box-sizing", "border-box");
|
section.getStyle().set("margin-bottom", "var(--lumo-space-m)");
|
||||||
|
|
||||||
H3 sectionTitle = new H3(getTranslation("createinvoice.section.summary"));
|
H3 sectionTitle = new H3(getTranslation("createinvoice.section.summary"));
|
||||||
section.add(sectionTitle);
|
section.add(sectionTitle);
|
||||||
@@ -347,7 +347,7 @@ public class CreateInvoiceView extends VerticalLayout implements HasUrlParameter
|
|||||||
BigDecimal totalAmount = netAmount.add(vatAmount);
|
BigDecimal totalAmount = netAmount.add(vatAmount);
|
||||||
|
|
||||||
Div priceTable = new Div();
|
Div priceTable = new Div();
|
||||||
priceTable.getStyle().set("width", "100%");
|
priceTable.addClassName("price-table");
|
||||||
|
|
||||||
priceTable.add(createPriceRow(getTranslation("createinvoice.summary.net") + ":",
|
priceTable.add(createPriceRow(getTranslation("createinvoice.summary.net") + ":",
|
||||||
netAmount.setScale(2, RoundingMode.HALF_UP) + " €", false));
|
netAmount.setScale(2, RoundingMode.HALF_UP) + " €", false));
|
||||||
@@ -360,17 +360,16 @@ public class CreateInvoiceView extends VerticalLayout implements HasUrlParameter
|
|||||||
|
|
||||||
private Div createPriceRow(String label, String value, boolean bold) {
|
private Div createPriceRow(String label, String value, boolean bold) {
|
||||||
Div row = new Div();
|
Div row = new Div();
|
||||||
row.getStyle().set("display", "flex").set("justify-content", "space-between").set("padding", "4px 0");
|
row.addClassName("price-row");
|
||||||
|
if (bold) {
|
||||||
|
row.addClassName("price-row--strong");
|
||||||
|
}
|
||||||
|
|
||||||
Span labelSpan = new Span(label);
|
Span labelSpan = new Span(label);
|
||||||
labelSpan.getStyle().set("padding-right", "8px");
|
labelSpan.addClassName("price-row-label");
|
||||||
|
|
||||||
Span valueSpan = new Span(value);
|
Span valueSpan = new Span(value);
|
||||||
valueSpan.getStyle().set("white-space", "nowrap");
|
valueSpan.addClassName("price-row-value");
|
||||||
if (bold) {
|
|
||||||
labelSpan.getStyle().set("font-weight", "bold");
|
|
||||||
valueSpan.getStyle().set("font-weight", "bold");
|
|
||||||
}
|
|
||||||
|
|
||||||
row.add(labelSpan, valueSpan);
|
row.add(labelSpan, valueSpan);
|
||||||
return row;
|
return row;
|
||||||
@@ -668,10 +667,10 @@ public class CreateInvoiceView extends VerticalLayout implements HasUrlParameter
|
|||||||
IFrame pdfFrame = new IFrame();
|
IFrame pdfFrame = new IFrame();
|
||||||
pdfFrame.setWidth("100%");
|
pdfFrame.setWidth("100%");
|
||||||
pdfFrame.setHeight("100%");
|
pdfFrame.setHeight("100%");
|
||||||
|
pdfFrame.addClassName("invoice-generator-frame");
|
||||||
|
|
||||||
String base64Pdf = java.util.Base64.getEncoder().encodeToString(pdfBytes);
|
String base64Pdf = java.util.Base64.getEncoder().encodeToString(pdfBytes);
|
||||||
pdfFrame.getElement().setAttribute("src", "data:application/pdf;base64," + base64Pdf);
|
pdfFrame.getElement().setAttribute("src", "data:application/pdf;base64," + base64Pdf);
|
||||||
pdfFrame.getStyle().set("border", "none");
|
|
||||||
|
|
||||||
Button closeButton = new Button(getTranslation("button.close"), e -> pdfDialog.close());
|
Button closeButton = new Button(getTranslation("button.close"), e -> pdfDialog.close());
|
||||||
closeButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
|
closeButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
|
||||||
@@ -705,10 +704,10 @@ public class CreateInvoiceView extends VerticalLayout implements HasUrlParameter
|
|||||||
IFrame pdfFrame = new IFrame();
|
IFrame pdfFrame = new IFrame();
|
||||||
pdfFrame.setWidth("100%");
|
pdfFrame.setWidth("100%");
|
||||||
pdfFrame.setHeight("100%");
|
pdfFrame.setHeight("100%");
|
||||||
|
pdfFrame.addClassName("invoice-generator-frame");
|
||||||
|
|
||||||
String base64Pdf = java.util.Base64.getEncoder().encodeToString(pdfBytes);
|
String base64Pdf = java.util.Base64.getEncoder().encodeToString(pdfBytes);
|
||||||
pdfFrame.getElement().setAttribute("src", "data:application/pdf;base64," + base64Pdf);
|
pdfFrame.getElement().setAttribute("src", "data:application/pdf;base64," + base64Pdf);
|
||||||
pdfFrame.getStyle().set("border", "none");
|
|
||||||
|
|
||||||
Button downloadButton = new Button("Herunterladen", e -> {
|
Button downloadButton = new Button("Herunterladen", e -> {
|
||||||
parent.getElement()
|
parent.getElement()
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import com.vaadin.flow.component.UI;
|
|||||||
import com.vaadin.flow.component.button.Button;
|
import com.vaadin.flow.component.button.Button;
|
||||||
import com.vaadin.flow.component.button.ButtonVariant;
|
import com.vaadin.flow.component.button.ButtonVariant;
|
||||||
import com.vaadin.flow.component.grid.Grid;
|
import com.vaadin.flow.component.grid.Grid;
|
||||||
import com.vaadin.flow.component.html.H2;
|
import com.vaadin.flow.component.html.Div;
|
||||||
import com.vaadin.flow.component.html.Main;
|
import com.vaadin.flow.component.html.Main;
|
||||||
import com.vaadin.flow.component.textfield.TextField;
|
import com.vaadin.flow.component.textfield.TextField;
|
||||||
import com.vaadin.flow.router.HasDynamicTitle;
|
import com.vaadin.flow.router.HasDynamicTitle;
|
||||||
@@ -12,6 +12,7 @@ import com.vaadin.flow.router.Menu;
|
|||||||
import com.vaadin.flow.router.Route;
|
import com.vaadin.flow.router.Route;
|
||||||
import com.vaadin.flow.theme.lumo.LumoUtility;
|
import com.vaadin.flow.theme.lumo.LumoUtility;
|
||||||
import de.assecutor.votianlt.model.Customer;
|
import de.assecutor.votianlt.model.Customer;
|
||||||
|
import de.assecutor.votianlt.pages.base.ui.component.ViewToolbar;
|
||||||
import de.assecutor.votianlt.pages.service.CustomerService;
|
import de.assecutor.votianlt.pages.service.CustomerService;
|
||||||
|
|
||||||
import java.time.Clock;
|
import java.time.Clock;
|
||||||
@@ -38,15 +39,21 @@ public class CustomersView extends Main implements HasDynamicTitle {
|
|||||||
todoGrid.setItems(query -> todoService.list(toSpringPageRequest(query)).stream());
|
todoGrid.setItems(query -> todoService.list(toSpringPageRequest(query)).stream());
|
||||||
todoGrid.addColumn(Customer::getCompanyName).setHeader(getTranslation("customers.column.company"));
|
todoGrid.addColumn(Customer::getCompanyName).setHeader(getTranslation("customers.column.company"));
|
||||||
todoGrid.setSizeFull();
|
todoGrid.setSizeFull();
|
||||||
|
todoGrid.addClassName("data-grid");
|
||||||
|
|
||||||
setSizeFull();
|
setSizeFull();
|
||||||
addClassNames(LumoUtility.BoxSizing.BORDER, LumoUtility.Display.FLEX, LumoUtility.FlexDirection.COLUMN,
|
addClassNames(LumoUtility.BoxSizing.BORDER, LumoUtility.Display.FLEX, LumoUtility.FlexDirection.COLUMN,
|
||||||
LumoUtility.Padding.MEDIUM, LumoUtility.Gap.SMALL);
|
LumoUtility.Padding.MEDIUM, LumoUtility.Gap.SMALL);
|
||||||
|
addClassName("data-view");
|
||||||
|
|
||||||
H2 title = new H2(getTranslation("customers.title"));
|
ViewToolbar toolbar = new ViewToolbar(getTranslation("customers.title"), createBtn);
|
||||||
add(title);
|
createBtn.addClassName("toolbar-primary-action");
|
||||||
|
|
||||||
add(todoGrid);
|
Div gridPanel = new Div(todoGrid);
|
||||||
|
gridPanel.addClassNames("surface-panel", "data-grid-panel");
|
||||||
|
gridPanel.setSizeFull();
|
||||||
|
|
||||||
|
add(toolbar, gridPanel);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addCustomer() {
|
private void addCustomer() {
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ public class EditAppUserView extends VerticalLayout implements HasUrlParameter<S
|
|||||||
this.appUserService = appUserService;
|
this.appUserService = appUserService;
|
||||||
setSizeFull();
|
setSizeFull();
|
||||||
setPadding(true);
|
setPadding(true);
|
||||||
|
addClassName("form-page");
|
||||||
setSpacing(true);
|
setSpacing(true);
|
||||||
|
|
||||||
// Set field labels via i18n
|
// Set field labels via i18n
|
||||||
@@ -67,17 +68,16 @@ public class EditAppUserView extends VerticalLayout implements HasUrlParameter<S
|
|||||||
contentContainer.setMaxWidth("90%");
|
contentContainer.setMaxWidth("90%");
|
||||||
contentContainer.setSpacing(true);
|
contentContainer.setSpacing(true);
|
||||||
contentContainer.setPadding(true);
|
contentContainer.setPadding(true);
|
||||||
contentContainer.getStyle().set("background", "var(--lumo-contrast-5pct)");
|
contentContainer.addClassNames("form-shell", "form-card");
|
||||||
contentContainer.getStyle().set("border-radius", "var(--lumo-border-radius)");
|
|
||||||
contentContainer.getStyle().set("box-shadow", "var(--lumo-box-shadow-s)");
|
|
||||||
|
|
||||||
// Header with title and back button
|
// Header with title and back button
|
||||||
HorizontalLayout header = new HorizontalLayout();
|
HorizontalLayout header = new HorizontalLayout();
|
||||||
header.setAlignItems(FlexComponent.Alignment.CENTER);
|
header.setAlignItems(FlexComponent.Alignment.CENTER);
|
||||||
header.setSpacing(true);
|
header.setSpacing(true);
|
||||||
|
header.addClassName("form-header");
|
||||||
|
|
||||||
H2 title = new H2(getTranslation("editappuser.title"));
|
H2 title = new H2(getTranslation("editappuser.title"));
|
||||||
title.getStyle().set("margin", "0");
|
title.addClassName("form-title");
|
||||||
|
|
||||||
Button backButton = new Button(getTranslation("button.back"), new Icon(VaadinIcon.ARROW_LEFT));
|
Button backButton = new Button(getTranslation("button.back"), new Icon(VaadinIcon.ARROW_LEFT));
|
||||||
backButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
|
backButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ public class EditCustomerView extends VerticalLayout implements HasUrlParameter<
|
|||||||
this.customerService = customerService;
|
this.customerService = customerService;
|
||||||
setSizeFull();
|
setSizeFull();
|
||||||
setPadding(true);
|
setPadding(true);
|
||||||
|
addClassName("form-page");
|
||||||
setSpacing(true);
|
setSpacing(true);
|
||||||
|
|
||||||
// Set field labels via i18n
|
// Set field labels via i18n
|
||||||
@@ -76,14 +77,12 @@ public class EditCustomerView extends VerticalLayout implements HasUrlParameter<
|
|||||||
contentContainer.setMaxWidth("90%");
|
contentContainer.setMaxWidth("90%");
|
||||||
contentContainer.setSpacing(true);
|
contentContainer.setSpacing(true);
|
||||||
contentContainer.setPadding(true);
|
contentContainer.setPadding(true);
|
||||||
contentContainer.getStyle().set("background", "var(--lumo-contrast-5pct)");
|
contentContainer.addClassNames("form-shell", "form-card");
|
||||||
contentContainer.getStyle().set("border-radius", "var(--lumo-border-radius)");
|
|
||||||
contentContainer.getStyle().set("box-shadow", "var(--lumo-box-shadow-s)");
|
|
||||||
|
|
||||||
// Header
|
// Header
|
||||||
H2 header = new H2(getTranslation("editcustomer.title"));
|
H2 header = new H2(getTranslation("editcustomer.title"));
|
||||||
|
header.addClassName("form-title");
|
||||||
header.getStyle().set("text-align", "center");
|
header.getStyle().set("text-align", "center");
|
||||||
header.getStyle().set("margin", "0");
|
|
||||||
contentContainer.add(header);
|
contentContainer.add(header);
|
||||||
|
|
||||||
// Form layout
|
// Form layout
|
||||||
|
|||||||
@@ -96,6 +96,7 @@ public class EditProfileView extends HorizontalLayout implements HasDynamicTitle
|
|||||||
setSizeFull();
|
setSizeFull();
|
||||||
setPadding(true);
|
setPadding(true);
|
||||||
setSpacing(true);
|
setSpacing(true);
|
||||||
|
addClassName("form-page");
|
||||||
setJustifyContentMode(JustifyContentMode.CENTER);
|
setJustifyContentMode(JustifyContentMode.CENTER);
|
||||||
// 2% Abstand zwischen den Spalten
|
// 2% Abstand zwischen den Spalten
|
||||||
setWidthFull();
|
setWidthFull();
|
||||||
|
|||||||
@@ -34,15 +34,18 @@ public class ForgetPasswordView extends VerticalLayout implements BeforeEnterObs
|
|||||||
setSizeFull();
|
setSizeFull();
|
||||||
setJustifyContentMode(FlexComponent.JustifyContentMode.CENTER);
|
setJustifyContentMode(FlexComponent.JustifyContentMode.CENTER);
|
||||||
setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.CENTER);
|
setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.CENTER);
|
||||||
|
addClassName("form-page");
|
||||||
|
|
||||||
VerticalLayout container = new VerticalLayout();
|
VerticalLayout container = new VerticalLayout();
|
||||||
container.setWidth("400px");
|
container.setWidth("400px");
|
||||||
|
container.setMaxWidth("100%");
|
||||||
container.setPadding(true);
|
container.setPadding(true);
|
||||||
container.setSpacing(true);
|
container.setSpacing(true);
|
||||||
container.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.STRETCH);
|
container.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.STRETCH);
|
||||||
|
container.addClassNames("form-shell", "narrow", "form-card");
|
||||||
|
|
||||||
H1 title = new H1("Neues Passwort festlegen");
|
H1 title = new H1("Neues Passwort festlegen");
|
||||||
title.getStyle().set("text-align", "center");
|
title.addClassName("form-title");
|
||||||
|
|
||||||
newPassword = new PasswordField("Neues Passwort");
|
newPassword = new PasswordField("Neues Passwort");
|
||||||
confirmPassword = new PasswordField("Passwort bestätigen");
|
confirmPassword = new PasswordField("Passwort bestätigen");
|
||||||
|
|||||||
@@ -24,15 +24,18 @@ public class ForgotPasswordRequestView extends VerticalLayout implements HasDyna
|
|||||||
setJustifyContentMode(FlexComponent.JustifyContentMode.CENTER);
|
setJustifyContentMode(FlexComponent.JustifyContentMode.CENTER);
|
||||||
setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.CENTER);
|
setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.CENTER);
|
||||||
setPadding(true);
|
setPadding(true);
|
||||||
|
addClassName("form-page");
|
||||||
|
|
||||||
VerticalLayout container = new VerticalLayout();
|
VerticalLayout container = new VerticalLayout();
|
||||||
container.setWidth("400px");
|
container.setWidth("400px");
|
||||||
|
container.setMaxWidth("100%");
|
||||||
container.setPadding(true);
|
container.setPadding(true);
|
||||||
container.setSpacing(true);
|
container.setSpacing(true);
|
||||||
container.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.STRETCH);
|
container.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.STRETCH);
|
||||||
|
container.addClassNames("form-shell", "narrow", "form-card");
|
||||||
|
|
||||||
H1 title = new H1("Passwort zurücksetzen");
|
H1 title = new H1("Passwort zurücksetzen");
|
||||||
title.getStyle().set("text-align", "center");
|
title.addClassName("form-title");
|
||||||
|
|
||||||
EmailField emailField = new EmailField("E-Mail-Adresse");
|
EmailField emailField = new EmailField("E-Mail-Adresse");
|
||||||
emailField.setWidthFull();
|
emailField.setWidthFull();
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ public class ImprintView extends VerticalLayout implements HasDynamicTitle {
|
|||||||
setPadding(true);
|
setPadding(true);
|
||||||
setSpacing(true);
|
setSpacing(true);
|
||||||
setAlignItems(Alignment.CENTER);
|
setAlignItems(Alignment.CENTER);
|
||||||
|
addClassNames("data-view", "form-shell");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Load HTML content from resources
|
// Load HTML content from resources
|
||||||
@@ -23,6 +24,7 @@ public class ImprintView extends VerticalLayout implements HasDynamicTitle {
|
|||||||
|
|
||||||
// Create a Div to hold the HTML content
|
// Create a Div to hold the HTML content
|
||||||
Div imprintDiv = new Div();
|
Div imprintDiv = new Div();
|
||||||
|
imprintDiv.addClassNames("form-card", "form-shell");
|
||||||
imprintDiv.getElement().setProperty("innerHTML", htmlContent);
|
imprintDiv.getElement().setProperty("innerHTML", htmlContent);
|
||||||
|
|
||||||
add(imprintDiv);
|
add(imprintDiv);
|
||||||
@@ -30,6 +32,7 @@ public class ImprintView extends VerticalLayout implements HasDynamicTitle {
|
|||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// Fallback content in case of error
|
// Fallback content in case of error
|
||||||
Div errorDiv = new Div();
|
Div errorDiv = new Div();
|
||||||
|
errorDiv.addClassNames("form-card", "form-shell");
|
||||||
errorDiv.setText(getTranslation("imprint.error", e.getMessage()));
|
errorDiv.setText(getTranslation("imprint.error", e.getMessage()));
|
||||||
add(errorDiv);
|
add(errorDiv);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ public class InvoiceGeneratorView extends VerticalLayout implements HasDynamicTi
|
|||||||
this.customerInvoiceService = customerInvoiceService;
|
this.customerInvoiceService = customerInvoiceService;
|
||||||
|
|
||||||
setId("invoice-generator-view");
|
setId("invoice-generator-view");
|
||||||
getElement().setAttribute("class", "invoice-generator-view");
|
addClassNames("invoice-generator-view", "admin-form-view");
|
||||||
|
|
||||||
setSpacing(false);
|
setSpacing(false);
|
||||||
setPadding(false);
|
setPadding(false);
|
||||||
@@ -57,6 +57,7 @@ public class InvoiceGeneratorView extends VerticalLayout implements HasDynamicTi
|
|||||||
mainLayout.setHeight("calc(100vh - 60px)"); // Abzug für Action-Buttons
|
mainLayout.setHeight("calc(100vh - 60px)"); // Abzug für Action-Buttons
|
||||||
mainLayout.setSpacing(true);
|
mainLayout.setSpacing(true);
|
||||||
mainLayout.getStyle().set("overflow", "hidden");
|
mainLayout.getStyle().set("overflow", "hidden");
|
||||||
|
mainLayout.addClassName("invoice-generator-main");
|
||||||
|
|
||||||
// Linke Seite: Templates/Bausteine
|
// Linke Seite: Templates/Bausteine
|
||||||
VerticalLayout leftPanel = createTemplatesPanel();
|
VerticalLayout leftPanel = createTemplatesPanel();
|
||||||
@@ -84,7 +85,7 @@ public class InvoiceGeneratorView extends VerticalLayout implements HasDynamicTi
|
|||||||
// Aktions-Buttons unter dem Canvas (fixe Höhe)
|
// Aktions-Buttons unter dem Canvas (fixe Höhe)
|
||||||
HorizontalLayout actionLayout = createActionButtons();
|
HorizontalLayout actionLayout = createActionButtons();
|
||||||
actionLayout.setHeight("60px");
|
actionLayout.setHeight("60px");
|
||||||
actionLayout.getStyle().set("flex-shrink", "0").set("padding", "0 var(--lumo-space-m)");
|
actionLayout.getStyle().set("flex-shrink", "0");
|
||||||
add(actionLayout);
|
add(actionLayout);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,11 +103,11 @@ public class InvoiceGeneratorView extends VerticalLayout implements HasDynamicTi
|
|||||||
panel.setPadding(true);
|
panel.setPadding(true);
|
||||||
panel.setSpacing(true);
|
panel.setSpacing(true);
|
||||||
panel.setHeightFull();
|
panel.setHeightFull();
|
||||||
panel.getStyle().set("background-color", "var(--lumo-contrast-5pct)")
|
panel.getStyle().set("overflow", "auto");
|
||||||
.set("border-radius", "var(--lumo-border-radius-m)").set("overflow", "auto");
|
panel.addClassName("invoice-generator-panel");
|
||||||
|
|
||||||
Span header = new Span(getTranslation("invoicegenerator.panel.templates"));
|
Span header = new Span(getTranslation("invoicegenerator.panel.templates"));
|
||||||
header.getStyle().set("font-weight", "bold").set("font-size", "var(--lumo-font-size-l)");
|
header.addClassName("invoice-generator-panel-title");
|
||||||
|
|
||||||
// Draggable Templates
|
// Draggable Templates
|
||||||
Div textBlock = createDraggableTemplate(getTranslation("invoicegenerator.template.text"), VaadinIcon.TEXT_LABEL,
|
Div textBlock = createDraggableTemplate(getTranslation("invoicegenerator.template.text"), VaadinIcon.TEXT_LABEL,
|
||||||
@@ -137,10 +138,7 @@ public class InvoiceGeneratorView extends VerticalLayout implements HasDynamicTi
|
|||||||
private Div createDraggableTemplate(String label, VaadinIcon icon, String type) {
|
private Div createDraggableTemplate(String label, VaadinIcon icon, String type) {
|
||||||
Div template = new Div();
|
Div template = new Div();
|
||||||
template.setText(label);
|
template.setText(label);
|
||||||
template.getStyle().set("padding", "var(--lumo-space-m)").set("margin", "var(--lumo-space-xs) 0")
|
template.addClassName("invoice-generator-template");
|
||||||
.set("background-color", "var(--lumo-base-color)").set("border", "1px solid var(--lumo-contrast-20pct)")
|
|
||||||
.set("border-radius", "var(--lumo-border-radius-m)").set("cursor", "grab").set("display", "flex")
|
|
||||||
.set("align-items", "center").set("gap", "var(--lumo-space-s)").set("user-select", "none");
|
|
||||||
|
|
||||||
// Icon hinzufügen
|
// Icon hinzufügen
|
||||||
Icon templateIcon = icon.create();
|
Icon templateIcon = icon.create();
|
||||||
@@ -174,10 +172,7 @@ public class InvoiceGeneratorView extends VerticalLayout implements HasDynamicTi
|
|||||||
canvasContainer.setId("invoice-canvas-container");
|
canvasContainer.setId("invoice-canvas-container");
|
||||||
canvasContainer.setWidth("100%");
|
canvasContainer.setWidth("100%");
|
||||||
canvasContainer.setHeight("100%");
|
canvasContainer.setHeight("100%");
|
||||||
canvasContainer.getStyle().set("background-color", "#e8e8e8")
|
canvasContainer.addClassName("invoice-generator-canvas");
|
||||||
.set("border", "2px dashed var(--lumo-contrast-30pct)")
|
|
||||||
.set("border-radius", "var(--lumo-border-radius-m)").set("position", "relative")
|
|
||||||
.set("overflow", "hidden").set("cursor", "default");
|
|
||||||
|
|
||||||
// Drop Zone Event Listener
|
// Drop Zone Event Listener
|
||||||
canvasContainer.getElement()
|
canvasContainer.getElement()
|
||||||
@@ -206,17 +201,16 @@ public class InvoiceGeneratorView extends VerticalLayout implements HasDynamicTi
|
|||||||
panel.setPadding(true);
|
panel.setPadding(true);
|
||||||
panel.setSpacing(true);
|
panel.setSpacing(true);
|
||||||
panel.setHeightFull();
|
panel.setHeightFull();
|
||||||
panel.getStyle().set("background-color", "var(--lumo-contrast-5pct)")
|
panel.getStyle().set("overflow", "auto");
|
||||||
.set("border-radius", "var(--lumo-border-radius-m)").set("overflow", "auto");
|
panel.addClassName("invoice-generator-panel");
|
||||||
|
|
||||||
Span header = new Span(getTranslation("invoicegenerator.panel.properties"));
|
Span header = new Span(getTranslation("invoicegenerator.panel.properties"));
|
||||||
header.getStyle().set("font-weight", "bold").set("font-size", "var(--lumo-font-size-l)");
|
header.addClassName("invoice-generator-panel-title");
|
||||||
|
|
||||||
// Info-Text wenn kein Element ausgewählt
|
// Info-Text wenn kein Element ausgewählt
|
||||||
selectedElementInfo = new Div();
|
selectedElementInfo = new Div();
|
||||||
selectedElementInfo.setText(getTranslation("invoicegenerator.properties.info"));
|
selectedElementInfo.setText(getTranslation("invoicegenerator.properties.info"));
|
||||||
selectedElementInfo.getStyle().set("color", "var(--lumo-secondary-text-color)").set("font-size",
|
selectedElementInfo.addClassName("invoice-generator-info");
|
||||||
"var(--lumo-font-size-s)");
|
|
||||||
|
|
||||||
panel.add(header, selectedElementInfo);
|
panel.add(header, selectedElementInfo);
|
||||||
|
|
||||||
@@ -230,6 +224,7 @@ public class InvoiceGeneratorView extends VerticalLayout implements HasDynamicTi
|
|||||||
layout.setPadding(false);
|
layout.setPadding(false);
|
||||||
layout.setSpacing(true);
|
layout.setSpacing(true);
|
||||||
layout.setAlignItems(Alignment.CENTER);
|
layout.setAlignItems(Alignment.CENTER);
|
||||||
|
layout.addClassName("invoice-generator-actionbar");
|
||||||
|
|
||||||
Button clearButton = new Button(getTranslation("invoicegenerator.button.clear"), new Icon(VaadinIcon.TRASH));
|
Button clearButton = new Button(getTranslation("invoicegenerator.button.clear"), new Icon(VaadinIcon.TRASH));
|
||||||
clearButton.addThemeVariants(ButtonVariant.LUMO_ERROR, ButtonVariant.LUMO_TERTIARY);
|
clearButton.addThemeVariants(ButtonVariant.LUMO_ERROR, ButtonVariant.LUMO_TERTIARY);
|
||||||
@@ -325,7 +320,7 @@ public class InvoiceGeneratorView extends VerticalLayout implements HasDynamicTi
|
|||||||
Div pdfContainer = new Div();
|
Div pdfContainer = new Div();
|
||||||
pdfContainer.setWidth("100%");
|
pdfContainer.setWidth("100%");
|
||||||
pdfContainer.setHeight("100%");
|
pdfContainer.setHeight("100%");
|
||||||
pdfContainer.getStyle().set("display", "flex").set("flex-direction", "column").set("overflow", "hidden");
|
pdfContainer.addClassName("invoice-generator-pdf");
|
||||||
|
|
||||||
// Use an iframe with data URL for PDF display
|
// Use an iframe with data URL for PDF display
|
||||||
String base64Pdf = java.util.Base64.getEncoder().encodeToString(pdfBytes);
|
String base64Pdf = java.util.Base64.getEncoder().encodeToString(pdfBytes);
|
||||||
@@ -335,7 +330,7 @@ public class InvoiceGeneratorView extends VerticalLayout implements HasDynamicTi
|
|||||||
pdfFrame.setWidth("100%");
|
pdfFrame.setWidth("100%");
|
||||||
pdfFrame.setHeight("100%");
|
pdfFrame.setHeight("100%");
|
||||||
pdfFrame.getElement().setAttribute("src", dataUrl);
|
pdfFrame.getElement().setAttribute("src", dataUrl);
|
||||||
pdfFrame.getStyle().set("border", "none").set("flex-grow", "1");
|
pdfFrame.addClassName("invoice-generator-frame");
|
||||||
|
|
||||||
pdfContainer.add(pdfFrame);
|
pdfContainer.add(pdfFrame);
|
||||||
|
|
||||||
@@ -368,11 +363,11 @@ public class InvoiceGeneratorView extends VerticalLayout implements HasDynamicTi
|
|||||||
propertiesPanel.removeAll();
|
propertiesPanel.removeAll();
|
||||||
|
|
||||||
Span header = new Span(getTranslation("invoicegenerator.properties.title"));
|
Span header = new Span(getTranslation("invoicegenerator.properties.title"));
|
||||||
header.getStyle().set("font-weight", "bold").set("font-size", "var(--lumo-font-size-l)");
|
header.addClassName("invoice-generator-panel-title");
|
||||||
|
|
||||||
// Element Typ Anzeige
|
// Element Typ Anzeige
|
||||||
Span typeLabel = new Span(getTranslation("invoicegenerator.properties.type") + ": " + elementType);
|
Span typeLabel = new Span(getTranslation("invoicegenerator.properties.type") + ": " + elementType);
|
||||||
typeLabel.getStyle().set("font-size", "var(--lumo-font-size-s)");
|
typeLabel.addClassName("invoice-generator-info");
|
||||||
|
|
||||||
propertiesPanel.add(header, typeLabel);
|
propertiesPanel.add(header, typeLabel);
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package de.assecutor.votianlt.pages.view;
|
package de.assecutor.votianlt.pages.view;
|
||||||
|
|
||||||
import com.vaadin.flow.component.grid.Grid;
|
import com.vaadin.flow.component.grid.Grid;
|
||||||
import com.vaadin.flow.component.html.H2;
|
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.notification.Notification;
|
import com.vaadin.flow.component.notification.Notification;
|
||||||
import com.vaadin.flow.component.orderedlayout.FlexComponent;
|
import com.vaadin.flow.component.orderedlayout.FlexComponent;
|
||||||
@@ -10,6 +10,7 @@ import com.vaadin.flow.router.HasDynamicTitle;
|
|||||||
import com.vaadin.flow.router.Route;
|
import com.vaadin.flow.router.Route;
|
||||||
import com.vaadin.flow.component.UI;
|
import com.vaadin.flow.component.UI;
|
||||||
import de.assecutor.votianlt.model.invoices.CustomerInvoice;
|
import de.assecutor.votianlt.model.invoices.CustomerInvoice;
|
||||||
|
import de.assecutor.votianlt.pages.base.ui.component.ViewToolbar;
|
||||||
import de.assecutor.votianlt.repository.CustomerInvoiceRepository;
|
import de.assecutor.votianlt.repository.CustomerInvoiceRepository;
|
||||||
import de.assecutor.votianlt.security.SecurityService;
|
import de.assecutor.votianlt.security.SecurityService;
|
||||||
import jakarta.annotation.security.RolesAllowed;
|
import jakarta.annotation.security.RolesAllowed;
|
||||||
@@ -39,12 +40,13 @@ public class InvoicesView extends VerticalLayout implements HasDynamicTitle {
|
|||||||
setSpacing(true);
|
setSpacing(true);
|
||||||
setJustifyContentMode(FlexComponent.JustifyContentMode.CENTER);
|
setJustifyContentMode(FlexComponent.JustifyContentMode.CENTER);
|
||||||
setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.CENTER);
|
setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.CENTER);
|
||||||
|
addClassName("data-view");
|
||||||
|
|
||||||
H2 title = new H2(getTranslation("invoices.title"));
|
add(new ViewToolbar(getTranslation("invoices.title")));
|
||||||
add(title);
|
|
||||||
|
|
||||||
invoiceGrid = new Grid<>(CustomerInvoice.class, false);
|
invoiceGrid = new Grid<>(CustomerInvoice.class, false);
|
||||||
invoiceGrid.setWidthFull();
|
invoiceGrid.setWidthFull();
|
||||||
|
invoiceGrid.addClassName("data-grid");
|
||||||
invoiceGrid.addColumn(invoice -> firstNonBlank(invoice.getInvoiceNumber(), invoice.getId()))
|
invoiceGrid.addColumn(invoice -> firstNonBlank(invoice.getInvoiceNumber(), invoice.getId()))
|
||||||
.setHeader(getTranslation("invoices.column.number")).setAutoWidth(true);
|
.setHeader(getTranslation("invoices.column.number")).setAutoWidth(true);
|
||||||
invoiceGrid.addColumn(this::getRecipientLabel).setHeader(getTranslation("invoices.column.customer"))
|
invoiceGrid.addColumn(this::getRecipientLabel).setHeader(getTranslation("invoices.column.customer"))
|
||||||
@@ -66,7 +68,11 @@ public class InvoicesView extends VerticalLayout implements HasDynamicTitle {
|
|||||||
});
|
});
|
||||||
|
|
||||||
loadInvoices();
|
loadInvoices();
|
||||||
add(invoiceGrid);
|
Div gridPanel = new Div(invoiceGrid);
|
||||||
|
gridPanel.addClassNames("surface-panel", "data-grid-panel");
|
||||||
|
gridPanel.setWidthFull();
|
||||||
|
|
||||||
|
add(gridPanel);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadInvoices() {
|
private void loadInvoices() {
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ public class JobHistoryView extends Main implements HasUrlParameter<String>, Has
|
|||||||
setSizeFull();
|
setSizeFull();
|
||||||
addClassNames(LumoUtility.BoxSizing.BORDER, LumoUtility.Display.FLEX, LumoUtility.FlexDirection.COLUMN,
|
addClassNames(LumoUtility.BoxSizing.BORDER, LumoUtility.Display.FLEX, LumoUtility.FlexDirection.COLUMN,
|
||||||
LumoUtility.Padding.MEDIUM, LumoUtility.Gap.SMALL);
|
LumoUtility.Padding.MEDIUM, LumoUtility.Gap.SMALL);
|
||||||
|
addClassName("data-view");
|
||||||
|
|
||||||
add(new ViewToolbar(getTranslation("jobhistory.title")));
|
add(new ViewToolbar(getTranslation("jobhistory.title")));
|
||||||
|
|
||||||
@@ -62,6 +63,7 @@ public class JobHistoryView extends Main implements HasUrlParameter<String>, Has
|
|||||||
content.setSpacing(true);
|
content.setSpacing(true);
|
||||||
content.setPadding(true);
|
content.setPadding(true);
|
||||||
content.setWidthFull();
|
content.setWidthFull();
|
||||||
|
content.addClassNames("form-shell", "form-card");
|
||||||
add(content);
|
add(content);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,9 +133,8 @@ public class JobHistoryView extends Main implements HasUrlParameter<String>, Has
|
|||||||
|
|
||||||
private Div createJobInfoBox(Job job) {
|
private Div createJobInfoBox(Job job) {
|
||||||
Div infoBox = new Div();
|
Div infoBox = new Div();
|
||||||
infoBox.getStyle().set("border", "1px solid var(--lumo-contrast-20pct)")
|
infoBox.addClassName("summary-card");
|
||||||
.set("border-radius", "var(--lumo-border-radius-m)").set("padding", "var(--lumo-space-m)")
|
infoBox.getStyle().set("margin-bottom", "var(--lumo-space-m)");
|
||||||
.set("background-color", "var(--lumo-base-color)").set("margin-bottom", "var(--lumo-space-m)");
|
|
||||||
|
|
||||||
VerticalLayout infoContent = new VerticalLayout();
|
VerticalLayout infoContent = new VerticalLayout();
|
||||||
infoContent.setPadding(false);
|
infoContent.setPadding(false);
|
||||||
@@ -169,11 +170,9 @@ public class JobHistoryView extends Main implements HasUrlParameter<String>, Has
|
|||||||
|
|
||||||
private Div createHistoryEntryCard(JobHistory entry) {
|
private Div createHistoryEntryCard(JobHistory entry) {
|
||||||
Div card = new Div();
|
Div card = new Div();
|
||||||
card.getStyle().set("border", "1px solid var(--lumo-contrast-10pct)")
|
card.addClassName("timeline-entry-card");
|
||||||
.set("border-left", "4px solid " + getTypeColor(entry.getChangeType()))
|
card.setWidthFull();
|
||||||
.set("border-radius", "var(--lumo-border-radius-s)").set("padding", "var(--lumo-space-m)")
|
card.getStyle().set("--timeline-accent", getTypeColor(entry.getChangeType()));
|
||||||
.set("margin-bottom", "var(--lumo-space-s)").set("background-color", "var(--lumo-base-color)")
|
|
||||||
.set("width", "100%").set("box-sizing", "border-box");
|
|
||||||
|
|
||||||
// Header row with icon, reason and timestamp
|
// Header row with icon, reason and timestamp
|
||||||
HorizontalLayout headerRow = new HorizontalLayout();
|
HorizontalLayout headerRow = new HorizontalLayout();
|
||||||
@@ -185,11 +184,10 @@ public class JobHistoryView extends Main implements HasUrlParameter<String>, Has
|
|||||||
|
|
||||||
Span reason = new Span(
|
Span reason = new Span(
|
||||||
entry.getReason() != null ? entry.getReason() : getTranslation("jobhistory.entry.unknown"));
|
entry.getReason() != null ? entry.getReason() : getTranslation("jobhistory.entry.unknown"));
|
||||||
reason.getStyle().set("font-weight", "500");
|
reason.addClassName("timeline-reason");
|
||||||
|
|
||||||
Span timestamp = new Span(formatDateTime(entry.getTimestamp()));
|
Span timestamp = new Span(formatDateTime(entry.getTimestamp()));
|
||||||
timestamp.getStyle().set("color", "var(--lumo-secondary-text-color)").set("font-size",
|
timestamp.addClassName("timeline-timestamp");
|
||||||
"var(--lumo-font-size-s)");
|
|
||||||
|
|
||||||
HorizontalLayout leftSide = new HorizontalLayout(typeIcon, reason);
|
HorizontalLayout leftSide = new HorizontalLayout(typeIcon, reason);
|
||||||
leftSide.setAlignItems(HorizontalLayout.Alignment.CENTER);
|
leftSide.setAlignItems(HorizontalLayout.Alignment.CENTER);
|
||||||
@@ -206,8 +204,7 @@ public class JobHistoryView extends Main implements HasUrlParameter<String>, Has
|
|||||||
// Description
|
// Description
|
||||||
if (entry.getDescription() != null && !entry.getDescription().isBlank()) {
|
if (entry.getDescription() != null && !entry.getDescription().isBlank()) {
|
||||||
Span description = new Span(entry.getDescription());
|
Span description = new Span(entry.getDescription());
|
||||||
description.getStyle().set("color", "var(--lumo-body-text-color)").set("margin-top", "var(--lumo-space-xs)")
|
description.addClassName("timeline-description");
|
||||||
.set("display", "block");
|
|
||||||
cardContent.add(description);
|
cardContent.add(description);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -465,11 +462,8 @@ public class JobHistoryView extends Main implements HasUrlParameter<String>, Has
|
|||||||
|
|
||||||
private Div createBarcodeBox(String barcodeValue) {
|
private Div createBarcodeBox(String barcodeValue) {
|
||||||
Div barcodeBox = new Div();
|
Div barcodeBox = new Div();
|
||||||
barcodeBox.getStyle().set("border", "1px solid var(--lumo-contrast-20pct)")
|
barcodeBox.addClassNames("detail-card", "detail-card--code");
|
||||||
.set("border-radius", "var(--lumo-border-radius-s)").set("padding", "var(--lumo-space-xs)")
|
barcodeBox.getStyle().set("font-size", "var(--lumo-font-size-s)").set("margin-bottom", "var(--lumo-space-xs)");
|
||||||
.set("background-color", "var(--lumo-contrast-5pct)").set("font-family", "monospace")
|
|
||||||
.set("font-size", "var(--lumo-font-size-s)").set("margin-bottom", "var(--lumo-space-xs)")
|
|
||||||
.set("word-break", "break-all");
|
|
||||||
|
|
||||||
barcodeBox.add(new Span(barcodeValue));
|
barcodeBox.add(new Span(barcodeValue));
|
||||||
return barcodeBox;
|
return barcodeBox;
|
||||||
|
|||||||
@@ -120,11 +120,13 @@ public class JobSummaryView extends Main implements HasUrlParameter<String>, Has
|
|||||||
setSizeFull();
|
setSizeFull();
|
||||||
addClassNames(LumoUtility.BoxSizing.BORDER, LumoUtility.Display.FLEX, LumoUtility.FlexDirection.COLUMN,
|
addClassNames(LumoUtility.BoxSizing.BORDER, LumoUtility.Display.FLEX, LumoUtility.FlexDirection.COLUMN,
|
||||||
LumoUtility.Padding.MEDIUM, LumoUtility.Gap.SMALL);
|
LumoUtility.Padding.MEDIUM, LumoUtility.Gap.SMALL);
|
||||||
|
addClassName("data-view");
|
||||||
|
|
||||||
content = new VerticalLayout();
|
content = new VerticalLayout();
|
||||||
content.setSpacing(true);
|
content.setSpacing(true);
|
||||||
content.setPadding(true);
|
content.setPadding(true);
|
||||||
content.setWidthFull();
|
content.setWidthFull();
|
||||||
|
content.addClassNames("form-shell", "form-card");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -282,7 +284,7 @@ public class JobSummaryView extends Main implements HasUrlParameter<String>, Has
|
|||||||
PriceCalculationResult priceResult = calculatePriceFromServices(job);
|
PriceCalculationResult priceResult = calculatePriceFromServices(job);
|
||||||
|
|
||||||
Div priceTable = new Div();
|
Div priceTable = new Div();
|
||||||
priceTable.getStyle().set("width", "100%");
|
priceTable.addClassName("price-table");
|
||||||
|
|
||||||
priceTable.add(createPriceRow(getTranslation("jobsummary.info.netto") + ":",
|
priceTable.add(createPriceRow(getTranslation("jobsummary.info.netto") + ":",
|
||||||
formatPrice(priceResult.netAmount()), false));
|
formatPrice(priceResult.netAmount()), false));
|
||||||
@@ -359,9 +361,7 @@ public class JobSummaryView extends Main implements HasUrlParameter<String>, Has
|
|||||||
|
|
||||||
private VerticalLayout borderedBox() {
|
private VerticalLayout borderedBox() {
|
||||||
VerticalLayout box = new VerticalLayout();
|
VerticalLayout box = new VerticalLayout();
|
||||||
box.getStyle().set("border", "1px solid var(--lumo-contrast-20pct)");
|
box.addClassName("summary-card");
|
||||||
box.getStyle().set("border-radius", "var(--lumo-border-radius-m)");
|
|
||||||
box.getStyle().set("background-color", "var(--lumo-base-color)");
|
|
||||||
box.setPadding(true);
|
box.setPadding(true);
|
||||||
box.setSpacing(false);
|
box.setSpacing(false);
|
||||||
return box;
|
return box;
|
||||||
@@ -369,10 +369,8 @@ public class JobSummaryView extends Main implements HasUrlParameter<String>, Has
|
|||||||
|
|
||||||
private Div createStationTilesSection(Job job, List<CargoItem> cargoItems, List<BaseTask> tasks) {
|
private Div createStationTilesSection(Job job, List<CargoItem> cargoItems, List<BaseTask> tasks) {
|
||||||
Div stationGrid = new Div();
|
Div stationGrid = new Div();
|
||||||
stationGrid.getStyle().set("display", "grid");
|
|
||||||
stationGrid.getStyle().set("grid-template-columns", "repeat(auto-fit, minmax(220px, 1fr))");
|
|
||||||
stationGrid.getStyle().set("gap", "var(--lumo-space-m)");
|
|
||||||
stationGrid.setWidthFull();
|
stationGrid.setWidthFull();
|
||||||
|
stationGrid.addClassName("stations-grid");
|
||||||
|
|
||||||
stationGrid.add(createPickupSummaryTile(job, cargoItems, tasks));
|
stationGrid.add(createPickupSummaryTile(job, cargoItems, tasks));
|
||||||
|
|
||||||
@@ -683,6 +681,7 @@ public class JobSummaryView extends Main implements HasUrlParameter<String>, Has
|
|||||||
dialogContent.setSpacing(true);
|
dialogContent.setSpacing(true);
|
||||||
dialogContent.setWidthFull();
|
dialogContent.setWidthFull();
|
||||||
dialogContent.getStyle().set("min-width", "0");
|
dialogContent.getStyle().set("min-width", "0");
|
||||||
|
dialogContent.addClassName("dialog-content-panel");
|
||||||
|
|
||||||
H4 header = new H4(getTranslation("jobsummary.section.tasks"));
|
H4 header = new H4(getTranslation("jobsummary.section.tasks"));
|
||||||
header.getStyle().set("margin", "0");
|
header.getStyle().set("margin", "0");
|
||||||
@@ -775,17 +774,16 @@ public class JobSummaryView extends Main implements HasUrlParameter<String>, Has
|
|||||||
|
|
||||||
private Div createPriceRow(String label, String value, boolean bold) {
|
private Div createPriceRow(String label, String value, boolean bold) {
|
||||||
Div row = new Div();
|
Div row = new Div();
|
||||||
row.getStyle().set("display", "flex").set("justify-content", "space-between").set("padding", "4px 0");
|
row.addClassName("price-row");
|
||||||
|
if (bold) {
|
||||||
|
row.addClassName("price-row--strong");
|
||||||
|
}
|
||||||
|
|
||||||
Span labelSpan = new Span(label);
|
Span labelSpan = new Span(label);
|
||||||
labelSpan.getStyle().set("padding-right", "8px");
|
labelSpan.addClassName("price-row-label");
|
||||||
|
|
||||||
Span valueSpan = new Span(value);
|
Span valueSpan = new Span(value);
|
||||||
valueSpan.getStyle().set("white-space", "nowrap");
|
valueSpan.addClassName("price-row-value");
|
||||||
if (bold) {
|
|
||||||
labelSpan.getStyle().set("font-weight", "bold");
|
|
||||||
valueSpan.getStyle().set("font-weight", "bold");
|
|
||||||
}
|
|
||||||
|
|
||||||
row.add(labelSpan, valueSpan);
|
row.add(labelSpan, valueSpan);
|
||||||
return row;
|
return row;
|
||||||
@@ -860,15 +858,11 @@ public class JobSummaryView extends Main implements HasUrlParameter<String>, Has
|
|||||||
Div map = new Div();
|
Div map = new Div();
|
||||||
map.setWidthFull();
|
map.setWidthFull();
|
||||||
map.setHeight("520px");
|
map.setHeight("520px");
|
||||||
map.getStyle().set("border", "1px solid var(--lumo-contrast-20pct)");
|
map.addClassName("job-summary-map");
|
||||||
map.getStyle().set("border-radius", "var(--lumo-border-radius-m)");
|
|
||||||
|
|
||||||
Div routeInfo = new Div();
|
Div routeInfo = new Div();
|
||||||
routeInfo.setWidthFull();
|
routeInfo.setWidthFull();
|
||||||
routeInfo.getStyle().set("border", "1px solid var(--lumo-contrast-20pct)");
|
routeInfo.addClassName("route-card");
|
||||||
routeInfo.getStyle().set("border-radius", "var(--lumo-border-radius-m)");
|
|
||||||
routeInfo.getStyle().set("padding", "var(--lumo-space-m)");
|
|
||||||
routeInfo.getStyle().set("background-color", "var(--lumo-base-color)");
|
|
||||||
|
|
||||||
content.add(map, routeInfo);
|
content.add(map, routeInfo);
|
||||||
|
|
||||||
@@ -1110,6 +1104,7 @@ public class JobSummaryView extends Main implements HasUrlParameter<String>, Has
|
|||||||
VerticalLayout dialogContent = new VerticalLayout();
|
VerticalLayout dialogContent = new VerticalLayout();
|
||||||
dialogContent.setPadding(true);
|
dialogContent.setPadding(true);
|
||||||
dialogContent.setSpacing(true);
|
dialogContent.setSpacing(true);
|
||||||
|
dialogContent.addClassName("dialog-content-panel");
|
||||||
|
|
||||||
// Header
|
// Header
|
||||||
H4 header = new H4("Aufgaben-Details");
|
H4 header = new H4("Aufgaben-Details");
|
||||||
@@ -1238,12 +1233,9 @@ public class JobSummaryView extends Main implements HasUrlParameter<String>, Has
|
|||||||
if (svgContent != null && !svgContent.isBlank()) {
|
if (svgContent != null && !svgContent.isBlank()) {
|
||||||
// Create a div to hold the SVG
|
// Create a div to hold the SVG
|
||||||
Div svgContainer = new Div();
|
Div svgContainer = new Div();
|
||||||
svgContainer.getStyle().set("border", "1px solid var(--lumo-contrast-20pct)")
|
svgContainer.addClassNames("detail-card", "detail-card--asset");
|
||||||
.set("border-radius", "var(--lumo-border-radius-m)")
|
svgContainer.getStyle().set("width", "100%").set("max-width", "450px").set("overflow",
|
||||||
.set("padding", "var(--lumo-space-s)").set("background-color", "white")
|
"hidden");
|
||||||
.set("width", "100%").set("max-width", "450px").set("overflow", "hidden")
|
|
||||||
.set("display", "flex").set("align-items", "center")
|
|
||||||
.set("justify-content", "center");
|
|
||||||
|
|
||||||
// Process SVG to make it responsive
|
// Process SVG to make it responsive
|
||||||
String responsiveSvg = makeResponsiveSvg(svgContent);
|
String responsiveSvg = makeResponsiveSvg(svgContent);
|
||||||
@@ -1276,12 +1268,9 @@ public class JobSummaryView extends Main implements HasUrlParameter<String>, Has
|
|||||||
if (barcodeValue != null && !barcodeValue.isBlank()) {
|
if (barcodeValue != null && !barcodeValue.isBlank()) {
|
||||||
// Create a styled container for each barcode
|
// Create a styled container for each barcode
|
||||||
Div barcodeContainer = new Div();
|
Div barcodeContainer = new Div();
|
||||||
barcodeContainer.getStyle().set("border", "1px solid var(--lumo-contrast-20pct)")
|
barcodeContainer.addClassNames("detail-card", "detail-card--code");
|
||||||
.set("border-radius", "var(--lumo-border-radius-s)")
|
barcodeContainer.getStyle().set("margin", "var(--lumo-space-xs) 0").set("font-size",
|
||||||
.set("padding", "var(--lumo-space-s)").set("margin", "var(--lumo-space-xs) 0")
|
"var(--lumo-font-size-s)");
|
||||||
.set("background-color", "var(--lumo-contrast-5pct)")
|
|
||||||
.set("font-family", "monospace").set("font-size", "var(--lumo-font-size-s)")
|
|
||||||
.set("word-break", "break-all");
|
|
||||||
|
|
||||||
Span barcodeSpan = new Span((i + 1) + ". " + barcodeValue);
|
Span barcodeSpan = new Span((i + 1) + ". " + barcodeValue);
|
||||||
barcodeContainer.add(barcodeSpan);
|
barcodeContainer.add(barcodeSpan);
|
||||||
@@ -1315,10 +1304,8 @@ public class JobSummaryView extends Main implements HasUrlParameter<String>, Has
|
|||||||
|
|
||||||
for (Comment comment : comments) {
|
for (Comment comment : comments) {
|
||||||
Div commentContainer = new Div();
|
Div commentContainer = new Div();
|
||||||
commentContainer.getStyle().set("background-color", "#f5f5f5")
|
commentContainer.addClassNames("detail-card", "detail-card--comment");
|
||||||
.set("border", "1px solid #ddd").set("border-radius", "4px").set("padding", "8px")
|
commentContainer.getStyle().set("margin", "4px 0");
|
||||||
.set("margin", "4px 0").set("font-family", "monospace")
|
|
||||||
.set("white-space", "pre-wrap");
|
|
||||||
|
|
||||||
Span commentText = new Span(comment.getCommentText());
|
Span commentText = new Span(comment.getCommentText());
|
||||||
commentContainer.add(commentText);
|
commentContainer.add(commentText);
|
||||||
@@ -1338,48 +1325,28 @@ public class JobSummaryView extends Main implements HasUrlParameter<String>, Has
|
|||||||
|
|
||||||
private Div createTaskCard(BaseTask task, String displayName) {
|
private Div createTaskCard(BaseTask task, String displayName) {
|
||||||
Div taskCard = new Div();
|
Div taskCard = new Div();
|
||||||
|
taskCard.addClassName("job-task-card");
|
||||||
// Card styling with fixed width
|
if (task.isCompleted()) {
|
||||||
taskCard.getStyle().set("border", "1px solid var(--lumo-contrast-20pct)")
|
taskCard.addClassName("completed");
|
||||||
.set("border-radius", "var(--lumo-border-radius-m)").set("padding", "var(--lumo-space-m)")
|
}
|
||||||
.set("margin", "var(--lumo-space-xs) 0").set("background-color", "var(--lumo-base-color)")
|
|
||||||
.set("cursor", "pointer").set("transition", "all 0.2s ease")
|
|
||||||
.set("box-shadow", "0 1px 3px rgba(0, 0, 0, 0.1)").set("display", "flex").set("align-items", "center")
|
|
||||||
.set("gap", "var(--lumo-space-m)").set("width", "100%").set("box-sizing", "border-box");
|
|
||||||
|
|
||||||
// Hover effects
|
|
||||||
taskCard.getElement().addEventListener("mouseenter", e -> {
|
|
||||||
taskCard.getStyle().set("transform", "translateY(-2px)").set("box-shadow", "0 4px 12px rgba(0, 0, 0, 0.15)")
|
|
||||||
.set("border-color", "var(--lumo-primary-color-50pct)");
|
|
||||||
});
|
|
||||||
|
|
||||||
taskCard.getElement().addEventListener("mouseleave", e -> {
|
|
||||||
taskCard.getStyle().set("transform", "translateY(0)").set("box-shadow", "0 1px 3px rgba(0, 0, 0, 0.1)")
|
|
||||||
.set("border-color", "var(--lumo-contrast-20pct)");
|
|
||||||
});
|
|
||||||
|
|
||||||
// Task icon based on type
|
// Task icon based on type
|
||||||
Icon taskIcon = getTaskIcon(task);
|
Icon taskIcon = getTaskIcon(task);
|
||||||
taskIcon.getStyle().set("color",
|
taskIcon.addClassName("job-task-icon");
|
||||||
task.isCompleted() ? "var(--lumo-success-color)" : "var(--lumo-primary-color)");
|
|
||||||
|
|
||||||
// Task content
|
// Task content
|
||||||
VerticalLayout taskContent = new VerticalLayout();
|
VerticalLayout taskContent = new VerticalLayout();
|
||||||
taskContent.setPadding(false);
|
taskContent.setPadding(false);
|
||||||
taskContent.setSpacing(false);
|
taskContent.setSpacing(false);
|
||||||
taskContent.getStyle().set("flex-grow", "1");
|
taskContent.addClassName("job-task-content");
|
||||||
|
|
||||||
// Task name with order number (display as 1-based instead of 0-based)
|
// Task name with order number (display as 1-based instead of 0-based)
|
||||||
String taskNameWithOrder = (task.getTaskOrder() != null ? (task.getTaskOrder() + 1) + ". " : "") + displayName;
|
String taskNameWithOrder = (task.getTaskOrder() != null ? (task.getTaskOrder() + 1) + ". " : "") + displayName;
|
||||||
Span taskName = new Span(taskNameWithOrder);
|
Span taskName = new Span(taskNameWithOrder);
|
||||||
taskName.getStyle().set("font-weight", "500").set("font-size", "var(--lumo-font-size-m)").set("color",
|
taskName.addClassName("job-task-name");
|
||||||
task.isCompleted() ? "var(--lumo-success-text-color)" : "var(--lumo-body-text-color)");
|
|
||||||
|
|
||||||
Span statusBadge = new Span(getTaskStatusLabel(task));
|
Span statusBadge = new Span(getTaskStatusLabel(task));
|
||||||
statusBadge.getStyle().set("font-size", "var(--lumo-font-size-xs)").set("font-weight", "600")
|
statusBadge.addClassNames("job-task-status", task.isCompleted() ? "completed" : "open");
|
||||||
.set("padding", "0.2rem 0.55rem").set("border-radius", "999px")
|
|
||||||
.set("background-color", task.isCompleted() ? "rgba(76, 175, 80, 0.15)" : "rgba(244, 67, 54, 0.12)")
|
|
||||||
.set("color", task.isCompleted() ? "var(--lumo-success-text-color)" : "var(--lumo-error-text-color)");
|
|
||||||
|
|
||||||
HorizontalLayout headerRow = new HorizontalLayout(taskName, statusBadge);
|
HorizontalLayout headerRow = new HorizontalLayout(taskName, statusBadge);
|
||||||
headerRow.setWidthFull();
|
headerRow.setWidthFull();
|
||||||
@@ -1390,24 +1357,18 @@ public class JobSummaryView extends Main implements HasUrlParameter<String>, Has
|
|||||||
|
|
||||||
// Task status/description
|
// Task status/description
|
||||||
Span taskDescription = new Span(getTaskDescription(task));
|
Span taskDescription = new Span(getTaskDescription(task));
|
||||||
taskDescription.getStyle().set("font-size", "var(--lumo-font-size-s)")
|
taskDescription.addClassName("job-task-description");
|
||||||
.set("color", "var(--lumo-secondary-text-color)").set("margin-top", "var(--lumo-space-xs)");
|
|
||||||
|
|
||||||
taskContent.add(headerRow, taskDescription);
|
taskContent.add(headerRow, taskDescription);
|
||||||
|
|
||||||
// Status indicator
|
// Status indicator
|
||||||
Div statusIndicator = new Div();
|
Div statusIndicator = new Div();
|
||||||
statusIndicator.getStyle().set("width", "8px").set("height", "8px").set("border-radius", "50%")
|
statusIndicator.addClassName("job-task-indicator");
|
||||||
.set("background-color", task.isCompleted() ? "var(--lumo-success-color)" : "var(--lumo-error-color)");
|
|
||||||
|
|
||||||
taskCard.add(taskIcon, taskContent, statusIndicator);
|
taskCard.add(taskIcon, taskContent, statusIndicator);
|
||||||
|
|
||||||
// Click handler with hover state reset
|
|
||||||
taskCard.addClickListener(event -> {
|
taskCard.addClickListener(event -> {
|
||||||
showTaskDetailsDialog(task);
|
showTaskDetailsDialog(task);
|
||||||
// Reset hover state after dialog interaction
|
|
||||||
taskCard.getStyle().set("transform", "translateY(0)").set("box-shadow", "0 1px 3px rgba(0, 0, 0, 0.1)")
|
|
||||||
.set("border-color", "var(--lumo-contrast-20pct)");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return taskCard;
|
return taskCard;
|
||||||
@@ -1480,17 +1441,11 @@ public class JobSummaryView extends Main implements HasUrlParameter<String>, Has
|
|||||||
|
|
||||||
private Div createPhotoGallery(List<String> photos) {
|
private Div createPhotoGallery(List<String> photos) {
|
||||||
Div galleryContainer = new Div();
|
Div galleryContainer = new Div();
|
||||||
galleryContainer.getStyle().set("border", "1px solid var(--lumo-contrast-20pct)")
|
galleryContainer.addClassName("job-photo-gallery");
|
||||||
.set("border-radius", "var(--lumo-border-radius-m)").set("padding", "var(--lumo-space-m)")
|
|
||||||
.set("background-color", "white").set("max-width", "600px").set("min-height", "500px")
|
|
||||||
.set("height", "500px").set("position", "relative").set("display", "flex").set("align-items", "center")
|
|
||||||
.set("justify-content", "center");
|
|
||||||
|
|
||||||
if (photos.size() == 1) {
|
if (photos.size() == 1) {
|
||||||
// Single photo - no navigation needed
|
// Single photo - no navigation needed
|
||||||
Div photoContainer = createPhotoContainer(photos.get(0));
|
Div photoContainer = createPhotoContainer(photos.get(0));
|
||||||
photoContainer.getStyle().set("flex", "1").set("display", "flex").set("align-items", "center")
|
|
||||||
.set("justify-content", "center");
|
|
||||||
galleryContainer.add(photoContainer);
|
galleryContainer.add(photoContainer);
|
||||||
} else {
|
} else {
|
||||||
// Multiple photos - add navigation
|
// Multiple photos - add navigation
|
||||||
@@ -1498,11 +1453,7 @@ public class JobSummaryView extends Main implements HasUrlParameter<String>, Has
|
|||||||
|
|
||||||
// Photo counter
|
// Photo counter
|
||||||
Span photoCounter = new Span((currentIndex[0] + 1) + " / " + photos.size());
|
Span photoCounter = new Span((currentIndex[0] + 1) + " / " + photos.size());
|
||||||
photoCounter.getStyle().set("position", "absolute").set("top", "var(--lumo-space-s)")
|
photoCounter.addClassName("job-photo-counter");
|
||||||
.set("right", "var(--lumo-space-s)").set("background-color", "rgba(0, 0, 0, 0.6)")
|
|
||||||
.set("color", "white").set("padding", "var(--lumo-space-xs) var(--lumo-space-s)")
|
|
||||||
.set("border-radius", "var(--lumo-border-radius-s)").set("font-size", "var(--lumo-font-size-s)")
|
|
||||||
.set("z-index", "10");
|
|
||||||
|
|
||||||
// Photo container
|
// Photo container
|
||||||
Div photoContainer = createPhotoContainer(photos.get(0));
|
Div photoContainer = createPhotoContainer(photos.get(0));
|
||||||
@@ -1513,16 +1464,12 @@ public class JobSummaryView extends Main implements HasUrlParameter<String>, Has
|
|||||||
// Previous button
|
// Previous button
|
||||||
Button prevButton = new Button(new Icon(VaadinIcon.CHEVRON_LEFT));
|
Button prevButton = new Button(new Icon(VaadinIcon.CHEVRON_LEFT));
|
||||||
prevButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY, ButtonVariant.LUMO_ICON);
|
prevButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY, ButtonVariant.LUMO_ICON);
|
||||||
prevButton.getStyle().set("position", "absolute").set("left", "var(--lumo-space-s)").set("top", "50%")
|
prevButton.addClassNames("job-photo-nav", "prev");
|
||||||
.set("transform", "translateY(-50%)").set("background-color", "rgba(255, 255, 255, 0.8)")
|
|
||||||
.set("border-radius", "50%").set("z-index", "10");
|
|
||||||
|
|
||||||
// Next button
|
// Next button
|
||||||
Button nextButton = new Button(new Icon(VaadinIcon.CHEVRON_RIGHT));
|
Button nextButton = new Button(new Icon(VaadinIcon.CHEVRON_RIGHT));
|
||||||
nextButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY, ButtonVariant.LUMO_ICON);
|
nextButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY, ButtonVariant.LUMO_ICON);
|
||||||
nextButton.getStyle().set("position", "absolute").set("right", "var(--lumo-space-s)").set("top", "50%")
|
nextButton.addClassNames("job-photo-nav", "next");
|
||||||
.set("transform", "translateY(-50%)").set("background-color", "rgba(255, 255, 255, 0.8)")
|
|
||||||
.set("border-radius", "50%").set("z-index", "10");
|
|
||||||
|
|
||||||
// Navigation logic
|
// Navigation logic
|
||||||
prevButton.addClickListener(e -> {
|
prevButton.addClickListener(e -> {
|
||||||
@@ -1557,8 +1504,7 @@ public class JobSummaryView extends Main implements HasUrlParameter<String>, Has
|
|||||||
|
|
||||||
private Div createPhotoContainer(String base64Photo) {
|
private Div createPhotoContainer(String base64Photo) {
|
||||||
Div photoContainer = new Div();
|
Div photoContainer = new Div();
|
||||||
photoContainer.getStyle().set("width", "100%").set("height", "100%").set("display", "flex")
|
photoContainer.addClassName("job-photo-container");
|
||||||
.set("align-items", "center").set("justify-content", "center").set("overflow", "hidden");
|
|
||||||
|
|
||||||
// Create image element
|
// Create image element
|
||||||
String imgSrc = base64Photo.startsWith("data:") ? base64Photo : "data:image/jpeg;base64," + base64Photo;
|
String imgSrc = base64Photo.startsWith("data:") ? base64Photo : "data:image/jpeg;base64," + base64Photo;
|
||||||
@@ -1628,10 +1574,7 @@ public class JobSummaryView extends Main implements HasUrlParameter<String>, Has
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void resetAllTaskCardHoverStates() {
|
private void resetAllTaskCardHoverStates() {
|
||||||
for (Div taskCard : taskCards) {
|
// Hover states are handled purely in CSS.
|
||||||
taskCard.getStyle().set("transform", "translateY(0)").set("box-shadow", "0 1px 3px rgba(0, 0, 0, 0.1)")
|
|
||||||
.set("border-color", "var(--lumo-contrast-20pct)");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getGoogleMapsApiKey() {
|
private String getGoogleMapsApiKey() {
|
||||||
|
|||||||
@@ -5,7 +5,10 @@ import com.vaadin.flow.component.button.Button;
|
|||||||
import com.vaadin.flow.component.button.ButtonVariant;
|
import com.vaadin.flow.component.button.ButtonVariant;
|
||||||
import com.vaadin.flow.component.html.Div;
|
import com.vaadin.flow.component.html.Div;
|
||||||
import com.vaadin.flow.component.html.H1;
|
import com.vaadin.flow.component.html.H1;
|
||||||
|
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.VaadinIcon;
|
||||||
import com.vaadin.flow.component.login.LoginForm;
|
import com.vaadin.flow.component.login.LoginForm;
|
||||||
import com.vaadin.flow.component.login.LoginI18n;
|
import com.vaadin.flow.component.login.LoginI18n;
|
||||||
import com.vaadin.flow.component.textfield.TextField;
|
import com.vaadin.flow.component.textfield.TextField;
|
||||||
@@ -96,30 +99,48 @@ public class LoginView extends VerticalLayout implements BeforeEnterObserver, Af
|
|||||||
loginForm.addForgotPasswordListener(e -> UI.getCurrent().navigate(ForgotPasswordRequestView.class));
|
loginForm.addForgotPasswordListener(e -> UI.getCurrent().navigate(ForgotPasswordRequestView.class));
|
||||||
|
|
||||||
H1 title = new H1(getTranslation("login.votianlt"));
|
H1 title = new H1(getTranslation("login.votianlt"));
|
||||||
title.getStyle().set("color", "var(--lumo-primary-color)");
|
title.addClassName("login-card-title");
|
||||||
|
|
||||||
Button registerButton = new Button(getTranslation("login.register"), e -> UI.getCurrent().navigate("register"));
|
Button registerButton = new Button(getTranslation("login.register"), e -> UI.getCurrent().navigate("register"));
|
||||||
registerButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
|
registerButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
|
||||||
|
registerButton.addClassName("login-register-button");
|
||||||
|
|
||||||
// Version display - will be set in @PostConstruct
|
// Version display - will be set in @PostConstruct
|
||||||
versionSpan = new Span("");
|
versionSpan = new Span("");
|
||||||
versionSpan.getStyle().set("color", "var(--lumo-secondary-text-color)")
|
versionSpan.addClassName("login-version");
|
||||||
.set("font-size", "var(--lumo-font-size-s)").set("margin-top", "var(--lumo-space-m)");
|
|
||||||
|
|
||||||
// Inline flash message box (hidden by default)
|
// Inline flash message box (hidden by default)
|
||||||
flashBox.getStyle().set("background", "var(--lumo-error-color-10pct)")
|
flashBox.addClassName("inline-flash");
|
||||||
.set("color", "var(--lumo-error-text-color)").set("border", "1px solid var(--lumo-error-color)")
|
flashBox.getStyle().set("display", "none");
|
||||||
.set("border-radius", "var(--lumo-border-radius-m)").set("padding", "var(--lumo-space-m)")
|
|
||||||
.set("width", "100%").set("display", "none");
|
|
||||||
|
|
||||||
VerticalLayout loginLayout = new VerticalLayout();
|
VerticalLayout loginLayout = new VerticalLayout();
|
||||||
loginLayout.setAlignItems(FlexComponent.Alignment.CENTER);
|
loginLayout.setAlignItems(FlexComponent.Alignment.CENTER);
|
||||||
|
loginLayout.addClassName("login-card");
|
||||||
|
|
||||||
loginLayout.add(flashBox, title, loginForm, twoFaField, verify2faButton, registerButton, versionSpan);
|
loginLayout.add(flashBox, title, loginForm, twoFaField, verify2faButton, registerButton, versionSpan);
|
||||||
loginLayout.setMaxWidth("400px");
|
|
||||||
loginLayout.setPadding(true);
|
loginLayout.setPadding(true);
|
||||||
|
|
||||||
add(loginLayout);
|
Div loginHighlight = new Div();
|
||||||
|
loginHighlight.addClassName("login-highlight");
|
||||||
|
|
||||||
|
Span loginEyebrow = new Span(getTranslation("login.title"));
|
||||||
|
loginEyebrow.addClassName("eyebrow-chip");
|
||||||
|
|
||||||
|
Icon introIcon = VaadinIcon.CUBES.create();
|
||||||
|
introIcon.addClassName("login-highlight-icon");
|
||||||
|
|
||||||
|
H1 introTitle = new H1(getTranslation("login.votianlt"));
|
||||||
|
introTitle.addClassName("login-highlight-title");
|
||||||
|
|
||||||
|
Paragraph introText = new Paragraph(getTranslation("start.hero.description"));
|
||||||
|
introText.addClassName("login-highlight-text");
|
||||||
|
|
||||||
|
loginHighlight.add(loginEyebrow, introIcon, introTitle, introText);
|
||||||
|
|
||||||
|
Div loginShell = new Div(loginHighlight, loginLayout);
|
||||||
|
loginShell.addClassName("login-shell");
|
||||||
|
|
||||||
|
add(loginShell);
|
||||||
// Login-Schritt 1: Benutzername/Passwort
|
// Login-Schritt 1: Benutzername/Passwort
|
||||||
loginForm.addLoginListener(e -> handlePasswordLogin(e.getUsername(), e.getPassword()));
|
loginForm.addLoginListener(e -> handlePasswordLogin(e.getUsername(), e.getPassword()));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,6 +98,7 @@ public class MessageDetailsView extends Main implements BeforeEnterObserver, Has
|
|||||||
this.messageService = messageService;
|
this.messageService = messageService;
|
||||||
this.messageBroadcaster = messageBroadcaster;
|
this.messageBroadcaster = messageBroadcaster;
|
||||||
this.eventPublisher = eventPublisher;
|
this.eventPublisher = eventPublisher;
|
||||||
|
addClassName("message-hub-view");
|
||||||
|
|
||||||
// Set height to 100% to prevent page from growing beyond viewport
|
// Set height to 100% to prevent page from growing beyond viewport
|
||||||
setHeightFull();
|
setHeightFull();
|
||||||
@@ -109,6 +110,7 @@ public class MessageDetailsView extends Main implements BeforeEnterObserver, Has
|
|||||||
contentLayout.setWidthFull();
|
contentLayout.setWidthFull();
|
||||||
contentLayout.setHeightFull();
|
contentLayout.setHeightFull();
|
||||||
contentLayout.getStyle().set("overflow", "hidden");
|
contentLayout.getStyle().set("overflow", "hidden");
|
||||||
|
contentLayout.addClassName("message-thread-layout");
|
||||||
|
|
||||||
add(contentLayout);
|
add(contentLayout);
|
||||||
}
|
}
|
||||||
@@ -172,8 +174,7 @@ public class MessageDetailsView extends Main implements BeforeEnterObserver, Has
|
|||||||
messagesContainer.setPadding(false);
|
messagesContainer.setPadding(false);
|
||||||
messagesContainer.setSpacing(false);
|
messagesContainer.setSpacing(false);
|
||||||
messagesContainer.setWidthFull();
|
messagesContainer.setWidthFull();
|
||||||
messagesContainer.getStyle().set("background-color", "#f0f0f0").set("border-radius", "8px").set("padding",
|
messagesContainer.addClassName("message-thread");
|
||||||
"15px");
|
|
||||||
|
|
||||||
// Wrap messages container in scroller for vertical scrolling
|
// Wrap messages container in scroller for vertical scrolling
|
||||||
messagesScroller = new Scroller(messagesContainer);
|
messagesScroller = new Scroller(messagesContainer);
|
||||||
@@ -274,7 +275,7 @@ public class MessageDetailsView extends Main implements BeforeEnterObserver, Has
|
|||||||
upload.setWidthFull();
|
upload.setWidthFull();
|
||||||
|
|
||||||
Span helper = new Span("Unterstützte Formate: PNG, JPG, GIF, WebP (max. 32 MB)");
|
Span helper = new Span("Unterstützte Formate: PNG, JPG, GIF, WebP (max. 32 MB)");
|
||||||
helper.getStyle().set("font-size", "12px").set("color", "#666666");
|
helper.addClassName("inline-caption");
|
||||||
|
|
||||||
Image preview = new Image();
|
Image preview = new Image();
|
||||||
preview.setAlt("Vorschau des ausgewählten Bildes");
|
preview.setAlt("Vorschau des ausgewählten Bildes");
|
||||||
@@ -388,14 +389,11 @@ public class MessageDetailsView extends Main implements BeforeEnterObserver, Has
|
|||||||
*/
|
*/
|
||||||
private Div createDateSeparator(LocalDate date) {
|
private Div createDateSeparator(LocalDate date) {
|
||||||
Div separator = new Div();
|
Div separator = new Div();
|
||||||
separator.getStyle().set("display", "flex").set("justify-content", "center").set("text-align", "center")
|
|
||||||
.set("margin", "20px 0");
|
|
||||||
separator.setWidthFull();
|
separator.setWidthFull();
|
||||||
|
separator.addClassName("message-date-separator");
|
||||||
|
|
||||||
Span dateLabel = new Span(DateTimeFormatUtil.formatDate(date));
|
Span dateLabel = new Span(DateTimeFormatUtil.formatDate(date));
|
||||||
dateLabel.getStyle().set("background-color", "#d0d0d0").set("padding", "4px 10px").set("border-radius", "12px")
|
dateLabel.addClassName("message-date-chip");
|
||||||
.set("font-size", "12px").set("font-weight", "500").set("color", "#333333")
|
|
||||||
.set("display", "inline-block");
|
|
||||||
|
|
||||||
separator.add(dateLabel);
|
separator.add(dateLabel);
|
||||||
return separator;
|
return separator;
|
||||||
@@ -413,24 +411,20 @@ public class MessageDetailsView extends Main implements BeforeEnterObserver, Has
|
|||||||
// Container for the message (aligns left or right)
|
// Container for the message (aligns left or right)
|
||||||
Div messageWrapper = new Div();
|
Div messageWrapper = new Div();
|
||||||
String alignment = isServerMessage ? "right" : "left";
|
String alignment = isServerMessage ? "right" : "left";
|
||||||
|
messageWrapper.addClassNames("message-wrapper", isServerMessage ? "server" : "client");
|
||||||
messageWrapper.getStyle().set("display", "flex")
|
|
||||||
.set("justify-content", isServerMessage ? "flex-end" : "flex-start").set("margin", "5px 0")
|
|
||||||
.set("width", "100%");
|
|
||||||
|
|
||||||
// Message bubble
|
// Message bubble
|
||||||
Div bubble = new Div();
|
Div bubble = new Div();
|
||||||
bubble.getStyle().set("background-color", isServerMessage ? "#dcf8c6" : "#ffffff").set("padding", "10px 15px")
|
bubble.addClassNames("message-bubble", isServerMessage ? "server" : "client");
|
||||||
.set("border-radius", "18px").set("max-width", "70%").set("box-shadow", "0 1px 2px rgba(0,0,0,0.1)")
|
bubble.getStyle().set("text-align", alignment);
|
||||||
.set("word-wrap", "break-word").set("white-space", "pre-wrap").set("text-align", alignment);
|
|
||||||
|
|
||||||
// Message content component (text or media)
|
// Message content component (text or media)
|
||||||
Component contentComponent = createContentComponent(message, alignment);
|
Component contentComponent = createContentComponent(message, alignment);
|
||||||
|
|
||||||
// Timestamp
|
// Timestamp
|
||||||
Span timeSpan = new Span(DateTimeFormatUtil.formatTime(timestamp));
|
Span timeSpan = new Span(DateTimeFormatUtil.formatTime(timestamp));
|
||||||
timeSpan.getStyle().set("font-size", "11px").set("color", isServerMessage ? "#666666" : "#999999")
|
timeSpan.addClassName("message-bubble-time");
|
||||||
.set("display", "block").set("text-align", alignment);
|
timeSpan.getStyle().set("text-align", alignment);
|
||||||
|
|
||||||
bubble.add(contentComponent, timeSpan);
|
bubble.add(contentComponent, timeSpan);
|
||||||
messageWrapper.add(bubble);
|
messageWrapper.add(bubble);
|
||||||
@@ -450,15 +444,14 @@ public class MessageDetailsView extends Main implements BeforeEnterObserver, Has
|
|||||||
Div contentDiv = new Div();
|
Div contentDiv = new Div();
|
||||||
String content = Optional.ofNullable(contentValue).filter(value -> !value.isBlank()).orElse("(kein Inhalt)");
|
String content = Optional.ofNullable(contentValue).filter(value -> !value.isBlank()).orElse("(kein Inhalt)");
|
||||||
contentDiv.setText(content);
|
contentDiv.setText(content);
|
||||||
contentDiv.getStyle().set("font-size", "14px").set("color", "#000000").set("margin-bottom", "5px")
|
contentDiv.addClassName("message-bubble-text");
|
||||||
.set("text-align", alignment);
|
contentDiv.getStyle().set("text-align", alignment);
|
||||||
return contentDiv;
|
return contentDiv;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Component createImageContent(String base64Value, String alignment) {
|
private Component createImageContent(String base64Value, String alignment) {
|
||||||
Div wrapper = new Div();
|
Div wrapper = new Div();
|
||||||
wrapper.getStyle().set("margin-bottom", "5px").set("display", "flex").set("justify-content",
|
wrapper.addClassNames("message-image-row", "right".equals(alignment) ? "server" : "client");
|
||||||
"right".equals(alignment) ? "flex-end" : "flex-start");
|
|
||||||
|
|
||||||
String trimmed = Optional.ofNullable(base64Value).map(String::trim).orElse("");
|
String trimmed = Optional.ofNullable(base64Value).map(String::trim).orElse("");
|
||||||
if (trimmed.isEmpty()) {
|
if (trimmed.isEmpty()) {
|
||||||
@@ -473,8 +466,7 @@ public class MessageDetailsView extends Main implements BeforeEnterObserver, Has
|
|||||||
}
|
}
|
||||||
|
|
||||||
Image image = new Image(dataUrl, "Nachrichtenbild");
|
Image image = new Image(dataUrl, "Nachrichtenbild");
|
||||||
image.getStyle().set("max-width", "100%").set("border-radius", "12px").set("display", "block")
|
image.addClassName("message-bubble-image");
|
||||||
.set("max-height", "320px").set("height", "auto");
|
|
||||||
image.getElement().setAttribute("loading", "lazy");
|
image.getElement().setAttribute("loading", "lazy");
|
||||||
|
|
||||||
wrapper.add(image);
|
wrapper.add(image);
|
||||||
@@ -687,8 +679,7 @@ public class MessageDetailsView extends Main implements BeforeEnterObserver, Has
|
|||||||
title.getStyle().set("margin", "0");
|
title.getStyle().set("margin", "0");
|
||||||
|
|
||||||
Span subtitle = new Span(conversationTitle);
|
Span subtitle = new Span(conversationTitle);
|
||||||
subtitle.getStyle().set("color", "#666666");
|
subtitle.addClassName("message-subtitle");
|
||||||
subtitle.getStyle().set("font-size", "14px");
|
|
||||||
|
|
||||||
titleLayout.add(title, subtitle);
|
titleLayout.add(title, subtitle);
|
||||||
|
|
||||||
@@ -696,6 +687,7 @@ public class MessageDetailsView extends Main implements BeforeEnterObserver, Has
|
|||||||
layout.setWidthFull();
|
layout.setWidthFull();
|
||||||
layout.setAlignItems(com.vaadin.flow.component.orderedlayout.FlexComponent.Alignment.CENTER);
|
layout.setAlignItems(com.vaadin.flow.component.orderedlayout.FlexComponent.Alignment.CENTER);
|
||||||
layout.setSpacing(true);
|
layout.setSpacing(true);
|
||||||
|
layout.addClassName("message-thread-header");
|
||||||
|
|
||||||
return layout;
|
return layout;
|
||||||
}
|
}
|
||||||
@@ -732,6 +724,7 @@ public class MessageDetailsView extends Main implements BeforeEnterObserver, Has
|
|||||||
layout.setAlignItems(FlexComponent.Alignment.END);
|
layout.setAlignItems(FlexComponent.Alignment.END);
|
||||||
layout.setSpacing(true);
|
layout.setSpacing(true);
|
||||||
layout.expand(messageInput);
|
layout.expand(messageInput);
|
||||||
|
layout.addClassName("message-thread-input");
|
||||||
|
|
||||||
return layout;
|
return layout;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,12 +4,11 @@ import com.vaadin.flow.component.AttachEvent;
|
|||||||
import com.vaadin.flow.component.DetachEvent;
|
import com.vaadin.flow.component.DetachEvent;
|
||||||
import com.vaadin.flow.component.UI;
|
import com.vaadin.flow.component.UI;
|
||||||
import com.vaadin.flow.component.grid.Grid;
|
import com.vaadin.flow.component.grid.Grid;
|
||||||
import com.vaadin.flow.component.html.H2;
|
|
||||||
import com.vaadin.flow.component.html.Main;
|
import com.vaadin.flow.component.html.Main;
|
||||||
|
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.notification.Notification;
|
import com.vaadin.flow.component.notification.Notification;
|
||||||
import com.vaadin.flow.component.notification.NotificationVariant;
|
import com.vaadin.flow.component.notification.NotificationVariant;
|
||||||
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.data.renderer.ComponentRenderer;
|
import com.vaadin.flow.data.renderer.ComponentRenderer;
|
||||||
import com.vaadin.flow.router.HasDynamicTitle;
|
import com.vaadin.flow.router.HasDynamicTitle;
|
||||||
@@ -21,6 +20,7 @@ import de.assecutor.votianlt.model.Message;
|
|||||||
import de.assecutor.votianlt.model.MessageContentType;
|
import de.assecutor.votianlt.model.MessageContentType;
|
||||||
import de.assecutor.votianlt.model.MessageOrigin;
|
import de.assecutor.votianlt.model.MessageOrigin;
|
||||||
import de.assecutor.votianlt.pages.service.AppUserService;
|
import de.assecutor.votianlt.pages.service.AppUserService;
|
||||||
|
import de.assecutor.votianlt.pages.base.ui.component.ViewToolbar;
|
||||||
import de.assecutor.votianlt.service.MessageBroadcaster;
|
import de.assecutor.votianlt.service.MessageBroadcaster;
|
||||||
import de.assecutor.votianlt.service.MessageService;
|
import de.assecutor.votianlt.service.MessageService;
|
||||||
import de.assecutor.votianlt.util.DateTimeFormatUtil;
|
import de.assecutor.votianlt.util.DateTimeFormatUtil;
|
||||||
@@ -60,21 +60,29 @@ public class MessagesView extends Main implements HasDynamicTitle {
|
|||||||
this.messageService = messageService;
|
this.messageService = messageService;
|
||||||
this.appUserService = appUserService;
|
this.appUserService = appUserService;
|
||||||
this.messageBroadcaster = messageBroadcaster;
|
this.messageBroadcaster = messageBroadcaster;
|
||||||
|
addClassNames("data-view", "message-hub-view");
|
||||||
|
|
||||||
// Create main layout
|
// Create main layout
|
||||||
VerticalLayout layout = new VerticalLayout();
|
VerticalLayout layout = new VerticalLayout();
|
||||||
layout.setPadding(true);
|
layout.setPadding(true);
|
||||||
layout.setSpacing(true);
|
layout.setSpacing(true);
|
||||||
layout.setWidthFull();
|
layout.setWidthFull();
|
||||||
|
layout.addClassName("form-shell");
|
||||||
|
|
||||||
// Add title and action buttons
|
// Add title and action buttons
|
||||||
HorizontalLayout headerLayout = createHeaderLayout();
|
ViewToolbar headerLayout = new ViewToolbar(getTranslation("messages.title"));
|
||||||
|
|
||||||
// Create client grid
|
// Create client grid
|
||||||
clientGrid = createClientGrid();
|
clientGrid = createClientGrid();
|
||||||
|
clientGrid.addClassName("data-grid");
|
||||||
|
|
||||||
|
Div gridPanel = new Div(clientGrid);
|
||||||
|
gridPanel.addClassNames("surface-panel", "data-grid-panel");
|
||||||
|
gridPanel.setWidthFull();
|
||||||
|
gridPanel.setHeight("680px");
|
||||||
|
|
||||||
// Add components to layout
|
// Add components to layout
|
||||||
layout.add(headerLayout, clientGrid);
|
layout.add(headerLayout, gridPanel);
|
||||||
|
|
||||||
// Add layout to main view
|
// Add layout to main view
|
||||||
add(layout);
|
add(layout);
|
||||||
@@ -83,18 +91,10 @@ public class MessagesView extends Main implements HasDynamicTitle {
|
|||||||
loadClientSummaries();
|
loadClientSummaries();
|
||||||
}
|
}
|
||||||
|
|
||||||
private HorizontalLayout createHeaderLayout() {
|
|
||||||
H2 title = new H2(getTranslation("messages.title"));
|
|
||||||
HorizontalLayout layout = new HorizontalLayout(title);
|
|
||||||
layout.setWidthFull();
|
|
||||||
layout.setAlignItems(com.vaadin.flow.component.orderedlayout.FlexComponent.Alignment.CENTER);
|
|
||||||
return layout;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Grid<ClientMessageSummary> createClientGrid() {
|
private Grid<ClientMessageSummary> createClientGrid() {
|
||||||
Grid<ClientMessageSummary> grid = new Grid<>(ClientMessageSummary.class, false);
|
Grid<ClientMessageSummary> grid = new Grid<>(ClientMessageSummary.class, false);
|
||||||
grid.setWidthFull();
|
grid.setWidthFull();
|
||||||
grid.setHeight("600px");
|
grid.setHeightFull();
|
||||||
|
|
||||||
// Add columns
|
// Add columns
|
||||||
grid.addColumn(new ComponentRenderer<>(summary -> {
|
grid.addColumn(new ComponentRenderer<>(summary -> {
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ public class MyInvoicesView extends Main implements HasDynamicTitle {
|
|||||||
|
|
||||||
public MyInvoicesView(SystemInvoiceService systemInvoiceService) {
|
public MyInvoicesView(SystemInvoiceService systemInvoiceService) {
|
||||||
this.systemInvoiceService = systemInvoiceService;
|
this.systemInvoiceService = systemInvoiceService;
|
||||||
|
addClassName("data-view");
|
||||||
getStyle().set("max-width", "1100px");
|
getStyle().set("max-width", "1100px");
|
||||||
getStyle().set("margin-left", "auto");
|
getStyle().set("margin-left", "auto");
|
||||||
getStyle().set("margin-right", "auto");
|
getStyle().set("margin-right", "auto");
|
||||||
@@ -74,8 +75,7 @@ public class MyInvoicesView extends Main implements HasDynamicTitle {
|
|||||||
private Component createTopCards() {
|
private Component createTopCards() {
|
||||||
// Container mit responsiven Spalten
|
// Container mit responsiven Spalten
|
||||||
Div container = new Div();
|
Div container = new Div();
|
||||||
container.getStyle().set("display", "grid").set("grid-template-columns", "repeat(auto-fit, minmax(280px, 1fr))")
|
container.addClassName("invoice-top-grid");
|
||||||
.set("gap", "var(--lumo-space-m)");
|
|
||||||
|
|
||||||
// Karte: Offene Rechnungen
|
// Karte: Offene Rechnungen
|
||||||
Paragraph hint = new Paragraph(getTranslation("myinvoices.hint.noopen"));
|
Paragraph hint = new Paragraph(getTranslation("myinvoices.hint.noopen"));
|
||||||
@@ -159,9 +159,7 @@ public class MyInvoicesView extends Main implements HasDynamicTitle {
|
|||||||
H4 emptyTitle = new H4(getTranslation("myinvoices.empty.title"));
|
H4 emptyTitle = new H4(getTranslation("myinvoices.empty.title"));
|
||||||
Paragraph emptyDesc = new Paragraph(getTranslation("myinvoices.empty.desc"));
|
Paragraph emptyDesc = new Paragraph(getTranslation("myinvoices.empty.desc"));
|
||||||
emptyState.add(emptyTitle, emptyDesc);
|
emptyState.add(emptyTitle, emptyDesc);
|
||||||
emptyState.getStyle().set("text-align", "center").set("color", "var(--lumo-secondary-text-color)")
|
emptyState.addClassName("empty-state-card");
|
||||||
.set("padding", "var(--lumo-space-m)").set("border", "1px dashed var(--lumo-contrast-20pct)")
|
|
||||||
.set("border-radius", "var(--lumo-border-radius-m)");
|
|
||||||
updateEmptyStateVisibility(allRows);
|
updateEmptyStateVisibility(allRows);
|
||||||
|
|
||||||
// Suche (einfacher Text-Filter über alle sichtbaren Felder)
|
// Suche (einfacher Text-Filter über alle sichtbaren Felder)
|
||||||
@@ -230,7 +228,7 @@ public class MyInvoicesView extends Main implements HasDynamicTitle {
|
|||||||
H3 h3 = new H3(title);
|
H3 h3 = new H3(title);
|
||||||
h3.getStyle().set("margin", "0");
|
h3.getStyle().set("margin", "0");
|
||||||
Div inner = new Div(content);
|
Div inner = new Div(content);
|
||||||
inner.getStyle().set("padding", "var(--lumo-space-s)");
|
inner.addClassName("invoice-card-inner");
|
||||||
card.add(h3, inner);
|
card.add(h3, inner);
|
||||||
return card;
|
return card;
|
||||||
}
|
}
|
||||||
@@ -246,9 +244,7 @@ public class MyInvoicesView extends Main implements HasDynamicTitle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void styleCard(Div card) {
|
private void styleCard(Div card) {
|
||||||
card.getStyle().set("border", "1px solid var(--lumo-contrast-20pct)")
|
card.addClassName("invoice-summary-card");
|
||||||
.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();
|
card.setWidthFull();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ public class RegisterView extends VerticalLayout implements HasDynamicTitle {
|
|||||||
setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.CENTER);
|
setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.CENTER);
|
||||||
setPadding(true);
|
setPadding(true);
|
||||||
setSpacing(false);
|
setSpacing(false);
|
||||||
|
addClassName("form-page");
|
||||||
|
|
||||||
// Hauptcontainer für das Formular
|
// Hauptcontainer für das Formular
|
||||||
VerticalLayout formContainer = createFormContainer();
|
VerticalLayout formContainer = createFormContainer();
|
||||||
@@ -70,28 +71,18 @@ public class RegisterView extends VerticalLayout implements HasDynamicTitle {
|
|||||||
private VerticalLayout createFormContainer() {
|
private VerticalLayout createFormContainer() {
|
||||||
VerticalLayout container = new VerticalLayout();
|
VerticalLayout container = new VerticalLayout();
|
||||||
container.setWidth("980px");
|
container.setWidth("980px");
|
||||||
|
container.setMaxWidth("100%");
|
||||||
container.setPadding(true);
|
container.setPadding(true);
|
||||||
container.setSpacing(true);
|
container.setSpacing(true);
|
||||||
container.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.STRETCH);
|
container.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.STRETCH);
|
||||||
|
container.addClassNames("form-shell", "form-card");
|
||||||
// Styling für den Container
|
|
||||||
container.getStyle().set("background-color", "var(--lumo-base-color)");
|
|
||||||
container.getStyle().set("border", "1px solid var(--lumo-contrast-20pct)");
|
|
||||||
container.getStyle().set("border-radius", "var(--lumo-border-radius-m)");
|
|
||||||
container.getStyle().set("box-shadow", "var(--lumo-box-shadow-s)");
|
|
||||||
|
|
||||||
// Titel
|
// Titel
|
||||||
H1 title = new H1(getTranslation("register.title"));
|
H1 title = new H1(getTranslation("register.title"));
|
||||||
title.getStyle().set("text-align", "center");
|
title.addClassName("form-title");
|
||||||
title.getStyle().set("color", "var(--lumo-primary-color)");
|
|
||||||
title.getStyle().set("margin-top", "0");
|
|
||||||
|
|
||||||
H2 subtitle = new H2(getTranslation("register.subtitle"));
|
H2 subtitle = new H2(getTranslation("register.subtitle"));
|
||||||
subtitle.getStyle().set("text-align", "center");
|
subtitle.addClassName("form-subtitle");
|
||||||
subtitle.getStyle().set("color", "var(--lumo-secondary-text-color)");
|
|
||||||
subtitle.getStyle().set("font-size", "var(--lumo-font-size-l)");
|
|
||||||
subtitle.getStyle().set("font-weight", "normal");
|
|
||||||
subtitle.getStyle().set("margin-bottom", "var(--lumo-space-l)");
|
|
||||||
|
|
||||||
// Formularfelder
|
// Formularfelder
|
||||||
emailField = new TextField(getTranslation("register.email"));
|
emailField = new TextField(getTranslation("register.email"));
|
||||||
|
|||||||
@@ -2,14 +2,15 @@ package de.assecutor.votianlt.pages.view;
|
|||||||
|
|
||||||
import com.vaadin.flow.component.button.Button;
|
import com.vaadin.flow.component.button.Button;
|
||||||
import com.vaadin.flow.component.grid.Grid;
|
import com.vaadin.flow.component.grid.Grid;
|
||||||
import com.vaadin.flow.component.html.H2;
|
import com.vaadin.flow.component.html.Div;
|
||||||
|
import com.vaadin.flow.component.html.Paragraph;
|
||||||
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.orderedlayout.HorizontalLayout;
|
|
||||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
||||||
import com.vaadin.flow.router.HasDynamicTitle;
|
import com.vaadin.flow.router.HasDynamicTitle;
|
||||||
import com.vaadin.flow.router.Route;
|
import com.vaadin.flow.router.Route;
|
||||||
import de.assecutor.votianlt.model.Customer;
|
import de.assecutor.votianlt.model.Customer;
|
||||||
|
import de.assecutor.votianlt.pages.base.ui.component.ViewToolbar;
|
||||||
import de.assecutor.votianlt.pages.service.CustomerService;
|
import de.assecutor.votianlt.pages.service.CustomerService;
|
||||||
import de.assecutor.votianlt.security.SecurityService;
|
import de.assecutor.votianlt.security.SecurityService;
|
||||||
import jakarta.annotation.security.RolesAllowed;
|
import jakarta.annotation.security.RolesAllowed;
|
||||||
@@ -30,21 +31,14 @@ public class ShowCustomersView extends VerticalLayout implements HasDynamicTitle
|
|||||||
setSizeFull();
|
setSizeFull();
|
||||||
setPadding(true);
|
setPadding(true);
|
||||||
setSpacing(true);
|
setSpacing(true);
|
||||||
|
addClassName("data-view");
|
||||||
|
|
||||||
// Header with title and add button
|
|
||||||
HorizontalLayout header = new HorizontalLayout();
|
|
||||||
header.setWidthFull();
|
|
||||||
header.add(new H2(getTranslation("customers.title")));
|
|
||||||
Button addCustomerButton = new Button(getTranslation("customers.button.add"), new Icon(VaadinIcon.PLUS));
|
Button addCustomerButton = new Button(getTranslation("customers.button.add"), new Icon(VaadinIcon.PLUS));
|
||||||
header.add(addCustomerButton);
|
add(new ViewToolbar(getTranslation("customers.title"), addCustomerButton));
|
||||||
header.setJustifyContentMode(JustifyContentMode.BETWEEN);
|
|
||||||
header.setAlignItems(Alignment.CENTER);
|
|
||||||
add(header);
|
|
||||||
|
|
||||||
// Add hint text
|
// Add hint text
|
||||||
var hintText = new com.vaadin.flow.component.html.Paragraph(getTranslation("customers.hint.click"));
|
Paragraph hintText = new Paragraph(getTranslation("customers.hint.click"));
|
||||||
hintText.getStyle().set("color", "var(--lumo-secondary-text-color)");
|
hintText.addClassName("list-hint");
|
||||||
hintText.getStyle().set("font-size", "var(--lumo-font-size-s)");
|
|
||||||
add(hintText);
|
add(hintText);
|
||||||
|
|
||||||
// Configure grid columns
|
// Configure grid columns
|
||||||
@@ -67,6 +61,7 @@ public class ShowCustomersView extends VerticalLayout implements HasDynamicTitle
|
|||||||
|
|
||||||
grid.setMultiSort(true);
|
grid.setMultiSort(true);
|
||||||
grid.setSizeFull();
|
grid.setSizeFull();
|
||||||
|
grid.addClassName("data-grid");
|
||||||
|
|
||||||
// Make grid rows clickable
|
// Make grid rows clickable
|
||||||
grid.setSelectionMode(Grid.SelectionMode.SINGLE);
|
grid.setSelectionMode(Grid.SelectionMode.SINGLE);
|
||||||
@@ -80,7 +75,11 @@ public class ShowCustomersView extends VerticalLayout implements HasDynamicTitle
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
add(grid);
|
Div gridPanel = new Div(grid);
|
||||||
|
gridPanel.addClassNames("surface-panel", "data-grid-panel");
|
||||||
|
gridPanel.setSizeFull();
|
||||||
|
|
||||||
|
add(gridPanel);
|
||||||
|
|
||||||
// Button action
|
// Button action
|
||||||
addCustomerButton.addClickListener(e -> getUI().ifPresent(ui -> ui.navigate("add-customer")));
|
addCustomerButton.addClickListener(e -> getUI().ifPresent(ui -> ui.navigate("add-customer")));
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import com.vaadin.flow.component.confirmdialog.ConfirmDialog;
|
|||||||
import com.vaadin.flow.component.datepicker.DatePicker;
|
import com.vaadin.flow.component.datepicker.DatePicker;
|
||||||
import com.vaadin.flow.component.grid.Grid;
|
import com.vaadin.flow.component.grid.Grid;
|
||||||
import com.vaadin.flow.component.html.Anchor;
|
import com.vaadin.flow.component.html.Anchor;
|
||||||
import com.vaadin.flow.component.html.H2;
|
import com.vaadin.flow.component.html.Div;
|
||||||
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.notification.Notification;
|
||||||
@@ -21,6 +21,7 @@ import com.vaadin.flow.router.Route;
|
|||||||
import de.assecutor.votianlt.model.Job;
|
import de.assecutor.votianlt.model.Job;
|
||||||
import de.assecutor.votianlt.model.JobStatus;
|
import de.assecutor.votianlt.model.JobStatus;
|
||||||
import de.assecutor.votianlt.messaging.MessagingPublisher;
|
import de.assecutor.votianlt.messaging.MessagingPublisher;
|
||||||
|
import de.assecutor.votianlt.pages.base.ui.component.ViewToolbar;
|
||||||
import de.assecutor.votianlt.util.DateTimeFormatUtil;
|
import de.assecutor.votianlt.util.DateTimeFormatUtil;
|
||||||
import de.assecutor.votianlt.repository.CustomerInvoiceRepository;
|
import de.assecutor.votianlt.repository.CustomerInvoiceRepository;
|
||||||
import de.assecutor.votianlt.repository.JobRepository;
|
import de.assecutor.votianlt.repository.JobRepository;
|
||||||
@@ -62,38 +63,39 @@ public class ShowJobsView extends VerticalLayout implements HasDynamicTitle {
|
|||||||
setSizeFull();
|
setSizeFull();
|
||||||
setPadding(true);
|
setPadding(true);
|
||||||
setSpacing(true);
|
setSpacing(true);
|
||||||
|
addClassName("data-view");
|
||||||
|
|
||||||
startDate.setLabel(getTranslation("jobs.filter.startdate"));
|
startDate.setLabel(getTranslation("jobs.filter.startdate"));
|
||||||
endDate.setLabel(getTranslation("jobs.filter.enddate"));
|
endDate.setLabel(getTranslation("jobs.filter.enddate"));
|
||||||
searchField.setLabel(getTranslation("jobs.filter.search"));
|
searchField.setLabel(getTranslation("jobs.filter.search"));
|
||||||
statusFilter.setLabel(getTranslation("jobs.filter.status"));
|
statusFilter.setLabel(getTranslation("jobs.filter.status"));
|
||||||
|
Button exportButton = new Button(getTranslation("jobs.button.csvexport"));
|
||||||
H2 title = new H2(getTranslation("nav.jobs"));
|
ViewToolbar toolbar = new ViewToolbar(getTranslation("nav.jobs"), exportButton);
|
||||||
add(title);
|
add(toolbar);
|
||||||
|
|
||||||
// Configure status filter
|
// Configure status filter
|
||||||
statusFilter.setItems(getTranslation("jobs.status.all"), getTranslation("jobs.status.open"),
|
statusFilter.setItems(getTranslation("jobs.status.all"), getTranslation("jobs.status.open"),
|
||||||
getTranslation("jobs.status.done"));
|
getTranslation("jobs.status.done"));
|
||||||
statusFilter.setValue(getTranslation("jobs.status.open"));
|
statusFilter.setValue(getTranslation("jobs.status.open"));
|
||||||
statusFilter.setWidth("150px");
|
|
||||||
|
|
||||||
// Configure search field
|
// Configure search field
|
||||||
searchField.setPlaceholder(getTranslation("jobs.filter.search.placeholder"));
|
searchField.setPlaceholder(getTranslation("jobs.filter.search.placeholder"));
|
||||||
searchField.setClearButtonVisible(true);
|
searchField.setClearButtonVisible(true);
|
||||||
searchField.setWidth("200px");
|
|
||||||
|
|
||||||
// Filterleiste mit Export-Button am rechten Rand
|
// Filterleiste mit Export-Button am rechten Rand
|
||||||
Button applyFilter = new Button(getTranslation("jobs.filter.apply"));
|
Button applyFilter = new Button(getTranslation("jobs.filter.apply"));
|
||||||
HorizontalLayout leftFilters = new HorizontalLayout(startDate, endDate, searchField, statusFilter, applyFilter);
|
HorizontalLayout leftFilters = new HorizontalLayout(startDate, endDate, searchField, statusFilter, applyFilter);
|
||||||
leftFilters.setAlignItems(Alignment.END);
|
leftFilters.setAlignItems(Alignment.END);
|
||||||
|
leftFilters.getStyle().set("flex-wrap", "wrap");
|
||||||
|
|
||||||
HorizontalLayout filterBar = new HorizontalLayout();
|
HorizontalLayout filterBar = new HorizontalLayout();
|
||||||
filterBar.setWidthFull();
|
filterBar.setWidthFull();
|
||||||
filterBar.add(leftFilters);
|
filterBar.add(leftFilters);
|
||||||
Button exportButton = new Button(getTranslation("jobs.button.csvexport"));
|
|
||||||
filterBar.add(exportButton);
|
filterBar.add(exportButton);
|
||||||
filterBar.setJustifyContentMode(JustifyContentMode.BETWEEN);
|
filterBar.setJustifyContentMode(JustifyContentMode.BETWEEN);
|
||||||
filterBar.setAlignItems(Alignment.END);
|
filterBar.setAlignItems(Alignment.END);
|
||||||
|
filterBar.getStyle().set("flex-wrap", "wrap");
|
||||||
|
filterBar.addClassNames("surface-panel", "filter-panel");
|
||||||
add(filterBar);
|
add(filterBar);
|
||||||
|
|
||||||
// Init default period: last 30 days
|
// Init default period: last 30 days
|
||||||
@@ -186,6 +188,7 @@ public class ShowJobsView extends VerticalLayout implements HasDynamicTitle {
|
|||||||
|
|
||||||
grid.setMultiSort(true);
|
grid.setMultiSort(true);
|
||||||
grid.setSizeFull();
|
grid.setSizeFull();
|
||||||
|
grid.addClassName("data-grid");
|
||||||
|
|
||||||
// Make grid rows clickable
|
// Make grid rows clickable
|
||||||
grid.setSelectionMode(Grid.SelectionMode.SINGLE);
|
grid.setSelectionMode(Grid.SelectionMode.SINGLE);
|
||||||
@@ -199,7 +202,11 @@ public class ShowJobsView extends VerticalLayout implements HasDynamicTitle {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
add(grid);
|
Div gridPanel = new Div(grid);
|
||||||
|
gridPanel.addClassNames("surface-panel", "data-grid-panel");
|
||||||
|
gridPanel.setSizeFull();
|
||||||
|
|
||||||
|
add(gridPanel);
|
||||||
|
|
||||||
loadData();
|
loadData();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,9 +33,10 @@ public class StartView extends VerticalLayout implements BeforeEnterObserver, Ha
|
|||||||
public StartView(SecurityService securityService, @Value("${app.version:unknown}") String appVersion) {
|
public StartView(SecurityService securityService, @Value("${app.version:unknown}") String appVersion) {
|
||||||
this.securityService = securityService;
|
this.securityService = securityService;
|
||||||
this.appVersion = appVersion;
|
this.appVersion = appVersion;
|
||||||
|
addClassName("landing-view");
|
||||||
setSizeFull();
|
setSizeFull();
|
||||||
setPadding(false);
|
setPadding(false);
|
||||||
setSpacing(false);
|
setSpacing(true);
|
||||||
setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.STRETCH);
|
setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.STRETCH);
|
||||||
|
|
||||||
// Header mit Navigation
|
// Header mit Navigation
|
||||||
@@ -69,14 +70,11 @@ public class StartView extends VerticalLayout implements BeforeEnterObserver, Ha
|
|||||||
header.setSpacing(true);
|
header.setSpacing(true);
|
||||||
header.setDefaultVerticalComponentAlignment(FlexComponent.Alignment.CENTER);
|
header.setDefaultVerticalComponentAlignment(FlexComponent.Alignment.CENTER);
|
||||||
header.setJustifyContentMode(FlexComponent.JustifyContentMode.BETWEEN);
|
header.setJustifyContentMode(FlexComponent.JustifyContentMode.BETWEEN);
|
||||||
header.getStyle().set("background-color", "var(--lumo-contrast-5pct)");
|
header.addClassNames("page-shell", "surface-panel", "landing-header");
|
||||||
header.getStyle().set("border-bottom", "1px solid var(--lumo-contrast-10pct)");
|
|
||||||
|
|
||||||
// Logo
|
// Logo
|
||||||
H1 logo = new H1("votian LT");
|
H1 logo = new H1("votian LT");
|
||||||
logo.getStyle().set("color", "var(--lumo-primary-color)");
|
logo.addClassName("landing-logo");
|
||||||
logo.getStyle().set("margin", "0");
|
|
||||||
logo.getStyle().set("font-weight", "bold");
|
|
||||||
|
|
||||||
// Navigation - abhängig vom Anmeldestatus
|
// Navigation - abhängig vom Anmeldestatus
|
||||||
Component navigation = securityService.isUserLoggedIn() ? createAuthenticatedNavigation()
|
Component navigation = securityService.isUserLoggedIn() ? createAuthenticatedNavigation()
|
||||||
@@ -91,12 +89,15 @@ public class StartView extends VerticalLayout implements BeforeEnterObserver, Ha
|
|||||||
HorizontalLayout navButtons = new HorizontalLayout();
|
HorizontalLayout navButtons = new HorizontalLayout();
|
||||||
navButtons.setSpacing(true);
|
navButtons.setSpacing(true);
|
||||||
navButtons.setDefaultVerticalComponentAlignment(FlexComponent.Alignment.CENTER);
|
navButtons.setDefaultVerticalComponentAlignment(FlexComponent.Alignment.CENTER);
|
||||||
|
navButtons.addClassName("landing-nav");
|
||||||
|
|
||||||
Button loginBtn = new Button(getTranslation("start.button.login"), event -> login());
|
Button loginBtn = new Button(getTranslation("start.button.login"), event -> login());
|
||||||
loginBtn.addThemeVariants(ButtonVariant.LUMO_CONTRAST);
|
loginBtn.addThemeVariants(ButtonVariant.LUMO_CONTRAST);
|
||||||
|
loginBtn.addClassName("landing-nav-button");
|
||||||
|
|
||||||
Button registerBtn = new Button(getTranslation("start.button.register"), event -> register());
|
Button registerBtn = new Button(getTranslation("start.button.register"), event -> register());
|
||||||
registerBtn.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
registerBtn.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
||||||
|
registerBtn.addClassName("landing-nav-button");
|
||||||
|
|
||||||
// Sprachauswahl Button
|
// Sprachauswahl Button
|
||||||
|
|
||||||
@@ -114,6 +115,7 @@ public class StartView extends VerticalLayout implements BeforeEnterObserver, Ha
|
|||||||
|
|
||||||
Button languageBtn = new Button(flag + " " + currentLang);
|
Button languageBtn = new Button(flag + " " + currentLang);
|
||||||
languageBtn.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
|
languageBtn.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
|
||||||
|
languageBtn.addClassName("landing-language-button");
|
||||||
|
|
||||||
ContextMenu languageMenu = new ContextMenu();
|
ContextMenu languageMenu = new ContextMenu();
|
||||||
languageMenu.setOpenOnClick(true);
|
languageMenu.setOpenOnClick(true);
|
||||||
@@ -186,10 +188,12 @@ public class StartView extends VerticalLayout implements BeforeEnterObserver, Ha
|
|||||||
HorizontalLayout navLayout = new HorizontalLayout();
|
HorizontalLayout navLayout = new HorizontalLayout();
|
||||||
navLayout.setSpacing(true);
|
navLayout.setSpacing(true);
|
||||||
navLayout.setDefaultVerticalComponentAlignment(FlexComponent.Alignment.CENTER);
|
navLayout.setDefaultVerticalComponentAlignment(FlexComponent.Alignment.CENTER);
|
||||||
|
navLayout.addClassName("landing-nav");
|
||||||
|
|
||||||
// Auftragserstellung Button
|
// Auftragserstellung Button
|
||||||
Button createOrderBtn = new Button("Auftragserstellung", event -> UI.getCurrent().navigate("add_job"));
|
Button createOrderBtn = new Button("Auftragserstellung", event -> UI.getCurrent().navigate("add_job"));
|
||||||
createOrderBtn.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
createOrderBtn.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
||||||
|
createOrderBtn.addClassName("landing-nav-button");
|
||||||
|
|
||||||
// Verwaltung ComboBox
|
// Verwaltung ComboBox
|
||||||
ComboBox<String> managementCombo = new ComboBox<>();
|
ComboBox<String> managementCombo = new ComboBox<>();
|
||||||
@@ -254,28 +258,23 @@ public class StartView extends VerticalLayout implements BeforeEnterObserver, Ha
|
|||||||
heroSection.setPadding(true);
|
heroSection.setPadding(true);
|
||||||
heroSection.setSpacing(true);
|
heroSection.setSpacing(true);
|
||||||
heroSection.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.CENTER);
|
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", "400px");
|
|
||||||
heroSection.setJustifyContentMode(FlexComponent.JustifyContentMode.CENTER);
|
heroSection.setJustifyContentMode(FlexComponent.JustifyContentMode.CENTER);
|
||||||
|
heroSection.addClassNames("page-shell", "hero-panel", "landing-hero");
|
||||||
|
|
||||||
// Hero Image Placeholder
|
// Hero Image Placeholder
|
||||||
Icon heroIcon = VaadinIcon.TRUCK.create();
|
Icon heroIcon = VaadinIcon.TRUCK.create();
|
||||||
heroIcon.setSize("120px");
|
heroIcon.setSize("120px");
|
||||||
heroIcon.getStyle().set("color", "var(--lumo-primary-color)");
|
heroIcon.addClassName("hero-panel-icon");
|
||||||
|
|
||||||
H1 heroTitle = new H1(getTranslation("start.title"));
|
H1 heroTitle = new H1(getTranslation("start.title"));
|
||||||
heroTitle.getStyle().set("text-align", "center");
|
heroTitle.addClassName("hero-panel-title");
|
||||||
heroTitle.getStyle().set("color", "var(--lumo-primary-text-color)");
|
|
||||||
heroTitle.getStyle().set("margin-bottom", "var(--lumo-space-l)");
|
|
||||||
|
|
||||||
Paragraph heroDescription = new Paragraph(getTranslation("start.hero.description"));
|
Paragraph heroDescription = new Paragraph(getTranslation("start.hero.description"));
|
||||||
heroDescription.getStyle().set("text-align", "center");
|
heroDescription.addClassName("hero-panel-text");
|
||||||
heroDescription.getStyle().set("max-width", "600px");
|
|
||||||
heroDescription.getStyle().set("font-size", "var(--lumo-font-size-l)");
|
|
||||||
|
|
||||||
Button ctaButton = new Button(getTranslation("cta.freetest"), event -> register());
|
Button ctaButton = new Button(getTranslation("cta.freetest"), event -> register());
|
||||||
ctaButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_LARGE);
|
ctaButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_LARGE);
|
||||||
|
ctaButton.addClassName("hero-cta");
|
||||||
|
|
||||||
heroSection.add(heroIcon, heroTitle, heroDescription, ctaButton);
|
heroSection.add(heroIcon, heroTitle, heroDescription, ctaButton);
|
||||||
return heroSection;
|
return heroSection;
|
||||||
@@ -287,24 +286,18 @@ public class StartView extends VerticalLayout implements BeforeEnterObserver, Ha
|
|||||||
systemSection.setPadding(true);
|
systemSection.setPadding(true);
|
||||||
systemSection.setSpacing(true);
|
systemSection.setSpacing(true);
|
||||||
systemSection.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.CENTER);
|
systemSection.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.CENTER);
|
||||||
systemSection.getStyle().set("background-color", "var(--lumo-base-color)");
|
systemSection.addClassNames("page-shell", "surface-panel", "section-panel");
|
||||||
|
|
||||||
// Section Header
|
// Section Header
|
||||||
H2 systemTitle = new H2(getTranslation("start.system.title"));
|
H2 systemTitle = new H2(getTranslation("start.system.title"));
|
||||||
systemTitle.getStyle().set("color", "var(--lumo-primary-color)");
|
systemTitle.addClassName("section-title");
|
||||||
systemTitle.getStyle().set("text-align", "center");
|
|
||||||
|
|
||||||
Paragraph systemIntro = new Paragraph(getTranslation("start.system.intro"));
|
Paragraph systemIntro = new Paragraph(getTranslation("start.system.intro"));
|
||||||
systemIntro.getStyle().set("text-align", "center");
|
systemIntro.addClassName("section-intro");
|
||||||
systemIntro.getStyle().set("max-width", "800px");
|
|
||||||
systemIntro.getStyle().set("margin-bottom", "var(--lumo-space-xl)");
|
|
||||||
|
|
||||||
// Features Grid
|
// Features Grid
|
||||||
HorizontalLayout featuresGrid = new HorizontalLayout();
|
Div featuresGrid = new Div();
|
||||||
featuresGrid.setWidthFull();
|
featuresGrid.addClassName("tile-grid");
|
||||||
featuresGrid.setSpacing(true);
|
|
||||||
featuresGrid.setJustifyContentMode(FlexComponent.JustifyContentMode.CENTER);
|
|
||||||
featuresGrid.setDefaultVerticalComponentAlignment(FlexComponent.Alignment.STRETCH);
|
|
||||||
|
|
||||||
// Feature Cards
|
// Feature Cards
|
||||||
featuresGrid.add(
|
featuresGrid.add(
|
||||||
@@ -324,24 +317,18 @@ public class StartView extends VerticalLayout implements BeforeEnterObserver, Ha
|
|||||||
card.setSpacing(true);
|
card.setSpacing(true);
|
||||||
card.setPadding(true);
|
card.setPadding(true);
|
||||||
card.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.CENTER);
|
card.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.CENTER);
|
||||||
card.getStyle().set("border", "1px solid var(--lumo-contrast-20pct)");
|
card.setWidthFull();
|
||||||
card.getStyle().set("border-radius", "var(--lumo-border-radius-m)");
|
card.addClassName("feature-card");
|
||||||
card.getStyle().set("background-color", "var(--lumo-base-color)");
|
|
||||||
card.getStyle().set("box-shadow", "var(--lumo-box-shadow-xs)");
|
|
||||||
card.setWidth("300px");
|
|
||||||
card.setHeightFull();
|
|
||||||
|
|
||||||
Icon icon = iconType.create();
|
Icon icon = iconType.create();
|
||||||
icon.setSize("48px");
|
icon.setSize("48px");
|
||||||
icon.getStyle().set("color", "var(--lumo-primary-color)");
|
icon.addClassName("feature-card-icon");
|
||||||
|
|
||||||
H3 cardTitle = new H3(title);
|
H3 cardTitle = new H3(title);
|
||||||
cardTitle.getStyle().set("text-align", "center");
|
cardTitle.addClassName("feature-card-title");
|
||||||
cardTitle.getStyle().set("margin", "var(--lumo-space-s) 0");
|
|
||||||
|
|
||||||
Paragraph cardDescription = new Paragraph(description);
|
Paragraph cardDescription = new Paragraph(description);
|
||||||
cardDescription.getStyle().set("text-align", "center");
|
cardDescription.addClassName("feature-card-description");
|
||||||
cardDescription.getStyle().set("font-size", "var(--lumo-font-size-s)");
|
|
||||||
|
|
||||||
card.add(icon, cardTitle, cardDescription);
|
card.add(icon, cardTitle, cardDescription);
|
||||||
return card;
|
return card;
|
||||||
@@ -353,19 +340,17 @@ public class StartView extends VerticalLayout implements BeforeEnterObserver, Ha
|
|||||||
appSection.setPadding(true);
|
appSection.setPadding(true);
|
||||||
appSection.setSpacing(true);
|
appSection.setSpacing(true);
|
||||||
appSection.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.CENTER);
|
appSection.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.CENTER);
|
||||||
appSection.getStyle().set("background-color", "var(--lumo-contrast-5pct)");
|
appSection.addClassNames("page-shell", "surface-panel", "app-overview-panel");
|
||||||
|
|
||||||
H2 appTitle = new H2(getTranslation("start.app.title"));
|
H2 appTitle = new H2(getTranslation("start.app.title"));
|
||||||
appTitle.getStyle().set("color", "var(--lumo-primary-color)");
|
appTitle.addClassName("section-title");
|
||||||
appTitle.getStyle().set("text-align", "center");
|
|
||||||
|
|
||||||
Paragraph appDescription = new Paragraph(getTranslation("start.app.description"));
|
Paragraph appDescription = new Paragraph(getTranslation("start.app.description"));
|
||||||
appDescription.getStyle().set("text-align", "center");
|
appDescription.addClassName("section-intro");
|
||||||
appDescription.getStyle().set("max-width", "800px");
|
|
||||||
|
|
||||||
Icon appIcon = VaadinIcon.MOBILE.create();
|
Icon appIcon = VaadinIcon.MOBILE.create();
|
||||||
appIcon.setSize("80px");
|
appIcon.setSize("80px");
|
||||||
appIcon.getStyle().set("color", "var(--lumo-primary-color)");
|
appIcon.addClassName("feature-card-icon");
|
||||||
|
|
||||||
appSection.add(appTitle, appDescription, appIcon);
|
appSection.add(appTitle, appDescription, appIcon);
|
||||||
return appSection;
|
return appSection;
|
||||||
@@ -377,18 +362,17 @@ public class StartView extends VerticalLayout implements BeforeEnterObserver, Ha
|
|||||||
footer.setPadding(true);
|
footer.setPadding(true);
|
||||||
footer.setSpacing(true);
|
footer.setSpacing(true);
|
||||||
footer.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.CENTER);
|
footer.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.CENTER);
|
||||||
footer.getStyle().set("background-color", "var(--lumo-contrast-10pct)");
|
footer.addClassNames("page-shell", "surface-panel", "footer-panel");
|
||||||
footer.getStyle().set("border-top", "1px solid var(--lumo-contrast-20pct)");
|
|
||||||
|
|
||||||
// Company Info
|
// Company Info
|
||||||
H3 companyTitle = new H3(getTranslation("start.imprint.title"));
|
H3 companyTitle = new H3(getTranslation("start.imprint.title"));
|
||||||
companyTitle.getStyle().set("color", "var(--lumo-primary-color)");
|
companyTitle.addClassName("section-title");
|
||||||
companyTitle.getStyle().set("text-align", "center");
|
|
||||||
|
|
||||||
VerticalLayout companyInfo = new VerticalLayout();
|
VerticalLayout companyInfo = new VerticalLayout();
|
||||||
companyInfo.setSpacing(false);
|
companyInfo.setSpacing(false);
|
||||||
companyInfo.setPadding(false);
|
companyInfo.setPadding(false);
|
||||||
companyInfo.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.CENTER);
|
companyInfo.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.CENTER);
|
||||||
|
companyInfo.addClassName("footer-details");
|
||||||
|
|
||||||
companyInfo.add(new Paragraph(getTranslation("start.imprint.company")),
|
companyInfo.add(new Paragraph(getTranslation("start.imprint.company")),
|
||||||
new Paragraph(getTranslation("start.imprint.address")),
|
new Paragraph(getTranslation("start.imprint.address")),
|
||||||
@@ -397,20 +381,14 @@ public class StartView extends VerticalLayout implements BeforeEnterObserver, Ha
|
|||||||
|
|
||||||
// Call to Action
|
// Call to Action
|
||||||
Paragraph ctaText = new Paragraph(getTranslation("start.cta.text"));
|
Paragraph ctaText = new Paragraph(getTranslation("start.cta.text"));
|
||||||
ctaText.getStyle().set("text-align", "center");
|
ctaText.addClassName("footer-cta");
|
||||||
ctaText.getStyle().set("font-weight", "bold");
|
|
||||||
ctaText.getStyle().set("color", "var(--lumo-primary-color)");
|
|
||||||
ctaText.getStyle().set("margin-top", "var(--lumo-space-l)");
|
|
||||||
|
|
||||||
Paragraph slogan = new Paragraph(getTranslation("start.slogan"));
|
Paragraph slogan = new Paragraph(getTranslation("start.slogan"));
|
||||||
slogan.getStyle().set("text-align", "center");
|
slogan.addClassName("footer-slogan");
|
||||||
slogan.getStyle().set("font-style", "italic");
|
|
||||||
slogan.getStyle().set("color", "var(--lumo-primary-color)");
|
|
||||||
|
|
||||||
// Versionsnummer
|
// Versionsnummer
|
||||||
Span versionSpan = new Span("Version " + appVersion);
|
Span versionSpan = new Span("Version " + appVersion);
|
||||||
versionSpan.getStyle().set("color", "var(--lumo-secondary-text-color)")
|
versionSpan.addClassName("footer-version");
|
||||||
.set("font-size", "var(--lumo-font-size-s)").set("margin-top", "var(--lumo-space-l)");
|
|
||||||
|
|
||||||
footer.add(companyTitle, companyInfo, ctaText, slogan, versionSpan);
|
footer.add(companyTitle, companyInfo, ctaText, slogan, versionSpan);
|
||||||
return footer;
|
return footer;
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ public class StatisticsView extends VerticalLayout implements HasDynamicTitle {
|
|||||||
setSizeFull();
|
setSizeFull();
|
||||||
setPadding(false);
|
setPadding(false);
|
||||||
setSpacing(false);
|
setSpacing(false);
|
||||||
|
addClassName("statistics-chat-view");
|
||||||
|
|
||||||
// Header
|
// Header
|
||||||
HorizontalLayout header = createHeader();
|
HorizontalLayout header = createHeader();
|
||||||
@@ -64,11 +65,13 @@ public class StatisticsView extends VerticalLayout implements HasDynamicTitle {
|
|||||||
chatContainer.setPadding(true);
|
chatContainer.setPadding(true);
|
||||||
chatContainer.setSpacing(true);
|
chatContainer.setSpacing(true);
|
||||||
chatContainer.getStyle().set("padding-bottom", "20px");
|
chatContainer.getStyle().set("padding-bottom", "20px");
|
||||||
|
chatContainer.addClassName("statistics-chat-container");
|
||||||
|
|
||||||
Scroller scroller = new Scroller(chatContainer);
|
Scroller scroller = new Scroller(chatContainer);
|
||||||
scroller.setSizeFull();
|
scroller.setSizeFull();
|
||||||
scroller.setScrollDirection(Scroller.ScrollDirection.VERTICAL);
|
scroller.setScrollDirection(Scroller.ScrollDirection.VERTICAL);
|
||||||
scroller.getStyle().set("background", "var(--lumo-contrast-5pct)");
|
scroller.getStyle().set("background", "var(--lumo-contrast-5pct)");
|
||||||
|
scroller.addClassName("statistics-scroller");
|
||||||
|
|
||||||
add(scroller);
|
add(scroller);
|
||||||
setFlexGrow(1, scroller);
|
setFlexGrow(1, scroller);
|
||||||
@@ -83,8 +86,7 @@ public class StatisticsView extends VerticalLayout implements HasDynamicTitle {
|
|||||||
header.setWidthFull();
|
header.setWidthFull();
|
||||||
header.setPadding(true);
|
header.setPadding(true);
|
||||||
header.setAlignItems(FlexComponent.Alignment.CENTER);
|
header.setAlignItems(FlexComponent.Alignment.CENTER);
|
||||||
header.getStyle().set("background", "var(--lumo-base-color)").set("border-bottom",
|
header.addClassName("statistics-header");
|
||||||
"1px solid var(--lumo-contrast-10pct)");
|
|
||||||
|
|
||||||
Icon aiIcon = VaadinIcon.MAGIC.create();
|
Icon aiIcon = VaadinIcon.MAGIC.create();
|
||||||
aiIcon.getStyle().set("color", "var(--lumo-primary-color)");
|
aiIcon.getStyle().set("color", "var(--lumo-primary-color)");
|
||||||
@@ -106,8 +108,7 @@ public class StatisticsView extends VerticalLayout implements HasDynamicTitle {
|
|||||||
inputArea.setPadding(true);
|
inputArea.setPadding(true);
|
||||||
inputArea.setSpacing(true);
|
inputArea.setSpacing(true);
|
||||||
inputArea.setAlignItems(FlexComponent.Alignment.CENTER);
|
inputArea.setAlignItems(FlexComponent.Alignment.CENTER);
|
||||||
inputArea.getStyle().set("background", "var(--lumo-base-color)").set("border-top",
|
inputArea.addClassName("statistics-input-panel");
|
||||||
"1px solid var(--lumo-contrast-10pct)");
|
|
||||||
|
|
||||||
Button sendButton = new Button(VaadinIcon.PAPERPLANE.create());
|
Button sendButton = new Button(VaadinIcon.PAPERPLANE.create());
|
||||||
sendButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
sendButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
||||||
@@ -124,6 +125,7 @@ public class StatisticsView extends VerticalLayout implements HasDynamicTitle {
|
|||||||
|
|
||||||
HorizontalLayout quickActions = new HorizontalLayout(jobCountBtn, revenueBtn, trendBtn);
|
HorizontalLayout quickActions = new HorizontalLayout(jobCountBtn, revenueBtn, trendBtn);
|
||||||
quickActions.setSpacing(true);
|
quickActions.setSpacing(true);
|
||||||
|
quickActions.addClassName("statistics-quick-actions");
|
||||||
|
|
||||||
VerticalLayout inputWrapper = new VerticalLayout();
|
VerticalLayout inputWrapper = new VerticalLayout();
|
||||||
inputWrapper.setPadding(false);
|
inputWrapper.setPadding(false);
|
||||||
@@ -193,17 +195,10 @@ public class StatisticsView extends VerticalLayout implements HasDynamicTitle {
|
|||||||
|
|
||||||
private void addUserMessage(String message) {
|
private void addUserMessage(String message) {
|
||||||
Div messageDiv = new Div();
|
Div messageDiv = new Div();
|
||||||
messageDiv.addClassName("chat-message");
|
messageDiv.addClassNames("chat-message", "user-message");
|
||||||
messageDiv.addClassName("user-message");
|
|
||||||
messageDiv.getStyle().set("display", "flex").set("justify-content", "flex-end").set("margin-bottom",
|
|
||||||
"var(--lumo-space-m)");
|
|
||||||
|
|
||||||
Div bubble = new Div();
|
Div bubble = new Div();
|
||||||
bubble.getStyle().set("background", "var(--lumo-primary-color)")
|
bubble.addClassNames("chat-bubble", "chat-bubble--user");
|
||||||
.set("color", "var(--lumo-primary-contrast-color)")
|
|
||||||
.set("padding", "var(--lumo-space-s) var(--lumo-space-m)")
|
|
||||||
.set("border-radius", "var(--lumo-border-radius-l)").set("max-width", "70%")
|
|
||||||
.set("word-wrap", "break-word");
|
|
||||||
|
|
||||||
Paragraph text = new Paragraph(message);
|
Paragraph text = new Paragraph(message);
|
||||||
text.getStyle().set("margin", "0");
|
text.getStyle().set("margin", "0");
|
||||||
@@ -219,16 +214,10 @@ public class StatisticsView extends VerticalLayout implements HasDynamicTitle {
|
|||||||
|
|
||||||
private void addAiResponse(AiStatisticsService.StatisticsResponse response) {
|
private void addAiResponse(AiStatisticsService.StatisticsResponse response) {
|
||||||
Div messageDiv = new Div();
|
Div messageDiv = new Div();
|
||||||
messageDiv.addClassName("chat-message");
|
messageDiv.addClassNames("chat-message", "ai-message");
|
||||||
messageDiv.addClassName("ai-message");
|
|
||||||
messageDiv.getStyle().set("display", "flex").set("justify-content", "flex-start").set("margin-bottom",
|
|
||||||
"var(--lumo-space-m)");
|
|
||||||
|
|
||||||
Div bubble = new Div();
|
Div bubble = new Div();
|
||||||
bubble.getStyle().set("background", "var(--lumo-base-color)")
|
bubble.addClassNames("chat-bubble", "chat-bubble--ai");
|
||||||
.set("border", "1px solid var(--lumo-contrast-10pct)").set("padding", "var(--lumo-space-m)")
|
|
||||||
.set("border-radius", "var(--lumo-border-radius-l)").set("max-width", "85%")
|
|
||||||
.set("box-shadow", "var(--lumo-box-shadow-xs)");
|
|
||||||
|
|
||||||
// AI Icon
|
// AI Icon
|
||||||
HorizontalLayout header = new HorizontalLayout();
|
HorizontalLayout header = new HorizontalLayout();
|
||||||
@@ -283,14 +272,10 @@ public class StatisticsView extends VerticalLayout implements HasDynamicTitle {
|
|||||||
|
|
||||||
private void addErrorMessage(String message) {
|
private void addErrorMessage(String message) {
|
||||||
Div messageDiv = new Div();
|
Div messageDiv = new Div();
|
||||||
messageDiv.getStyle().set("display", "flex").set("justify-content", "flex-start").set("margin-bottom",
|
messageDiv.addClassNames("chat-message", "error-message");
|
||||||
"var(--lumo-space-m)");
|
|
||||||
|
|
||||||
Div bubble = new Div();
|
Div bubble = new Div();
|
||||||
bubble.getStyle().set("background", "var(--lumo-error-color-10pct)")
|
bubble.addClassNames("chat-bubble", "chat-bubble--error");
|
||||||
.set("border", "1px solid var(--lumo-error-color)")
|
|
||||||
.set("padding", "var(--lumo-space-s) var(--lumo-space-m)")
|
|
||||||
.set("border-radius", "var(--lumo-border-radius-l)").set("max-width", "70%");
|
|
||||||
|
|
||||||
Icon errorIcon = VaadinIcon.EXCLAMATION_CIRCLE.create();
|
Icon errorIcon = VaadinIcon.EXCLAMATION_CIRCLE.create();
|
||||||
errorIcon.setSize("16px");
|
errorIcon.setSize("16px");
|
||||||
@@ -310,14 +295,10 @@ public class StatisticsView extends VerticalLayout implements HasDynamicTitle {
|
|||||||
|
|
||||||
private Div createLoadingMessage() {
|
private Div createLoadingMessage() {
|
||||||
Div messageDiv = new Div();
|
Div messageDiv = new Div();
|
||||||
messageDiv.getStyle().set("display", "flex").set("justify-content", "flex-start").set("margin-bottom",
|
messageDiv.addClassNames("chat-message", "loading-message");
|
||||||
"var(--lumo-space-m)");
|
|
||||||
|
|
||||||
Div bubble = new Div();
|
Div bubble = new Div();
|
||||||
bubble.getStyle().set("background", "var(--lumo-base-color)")
|
bubble.addClassNames("chat-bubble", "chat-bubble--loading");
|
||||||
.set("border", "1px solid var(--lumo-contrast-10pct)")
|
|
||||||
.set("padding", "var(--lumo-space-s) var(--lumo-space-m)")
|
|
||||||
.set("border-radius", "var(--lumo-border-radius-l)");
|
|
||||||
|
|
||||||
Span dots = new Span(getTranslation("statistics.loading"));
|
Span dots = new Span(getTranslation("statistics.loading"));
|
||||||
dots.getStyle().set("color", "var(--lumo-secondary-text-color)").set("font-style", "italic");
|
dots.getStyle().set("color", "var(--lumo-secondary-text-color)").set("font-style", "italic");
|
||||||
|
|||||||
@@ -49,12 +49,14 @@ public class UserMessagesView extends Main implements HasUrlParameter<String>, H
|
|||||||
public UserMessagesView(AppUserService appUserService, MessageService messageService) {
|
public UserMessagesView(AppUserService appUserService, MessageService messageService) {
|
||||||
this.appUserService = appUserService;
|
this.appUserService = appUserService;
|
||||||
this.messageService = messageService;
|
this.messageService = messageService;
|
||||||
|
addClassName("message-hub-view");
|
||||||
|
|
||||||
// Create main layout
|
// Create main layout
|
||||||
contentLayout = new VerticalLayout();
|
contentLayout = new VerticalLayout();
|
||||||
contentLayout.setPadding(true);
|
contentLayout.setPadding(true);
|
||||||
contentLayout.setSpacing(true);
|
contentLayout.setSpacing(true);
|
||||||
contentLayout.setWidthFull();
|
contentLayout.setWidthFull();
|
||||||
|
contentLayout.addClassName("form-shell");
|
||||||
|
|
||||||
add(contentLayout);
|
add(contentLayout);
|
||||||
}
|
}
|
||||||
@@ -104,6 +106,7 @@ public class UserMessagesView extends Main implements HasUrlParameter<String>, H
|
|||||||
layout.setWidthFull();
|
layout.setWidthFull();
|
||||||
layout.setAlignItems(com.vaadin.flow.component.orderedlayout.FlexComponent.Alignment.CENTER);
|
layout.setAlignItems(com.vaadin.flow.component.orderedlayout.FlexComponent.Alignment.CENTER);
|
||||||
layout.setSpacing(true);
|
layout.setSpacing(true);
|
||||||
|
layout.addClassName("message-thread-header");
|
||||||
|
|
||||||
return layout;
|
return layout;
|
||||||
}
|
}
|
||||||
@@ -112,12 +115,11 @@ public class UserMessagesView extends Main implements HasUrlParameter<String>, H
|
|||||||
VerticalLayout section = new VerticalLayout();
|
VerticalLayout section = new VerticalLayout();
|
||||||
section.setPadding(true);
|
section.setPadding(true);
|
||||||
section.setSpacing(true);
|
section.setSpacing(true);
|
||||||
section.getStyle().set("border", "1px solid #e0e0e0");
|
|
||||||
section.getStyle().set("border-radius", "8px");
|
|
||||||
section.setWidthFull();
|
section.setWidthFull();
|
||||||
section.getStyle().set("margin-right", "20px");
|
section.addClassName("message-section");
|
||||||
|
|
||||||
H3 title = new H3(getTranslation("usermessages.general.title"));
|
H3 title = new H3(getTranslation("usermessages.general.title"));
|
||||||
|
title.addClassName("message-section-title");
|
||||||
section.add(title);
|
section.add(title);
|
||||||
|
|
||||||
List<Message> sortedMessages = new ArrayList<>();
|
List<Message> sortedMessages = new ArrayList<>();
|
||||||
@@ -145,12 +147,11 @@ public class UserMessagesView extends Main implements HasUrlParameter<String>, H
|
|||||||
VerticalLayout section = new VerticalLayout();
|
VerticalLayout section = new VerticalLayout();
|
||||||
section.setPadding(true);
|
section.setPadding(true);
|
||||||
section.setSpacing(true);
|
section.setSpacing(true);
|
||||||
section.getStyle().set("border", "1px solid #e0e0e0");
|
|
||||||
section.getStyle().set("border-radius", "8px");
|
|
||||||
section.setWidthFull();
|
section.setWidthFull();
|
||||||
section.getStyle().set("margin-right", "20px");
|
section.addClassName("message-section");
|
||||||
|
|
||||||
H3 title = new H3(getTranslation("usermessages.job.title"));
|
H3 title = new H3(getTranslation("usermessages.job.title"));
|
||||||
|
title.addClassName("message-section-title");
|
||||||
section.add(title);
|
section.add(title);
|
||||||
|
|
||||||
if (jobMessages == null || jobMessages.isEmpty()) {
|
if (jobMessages == null || jobMessages.isEmpty()) {
|
||||||
@@ -191,40 +192,21 @@ public class UserMessagesView extends Main implements HasUrlParameter<String>, H
|
|||||||
int messageCount, int unreadCount, String conversationId) {
|
int messageCount, int unreadCount, String conversationId) {
|
||||||
Div card = new Div();
|
Div card = new Div();
|
||||||
card.setWidthFull();
|
card.setWidthFull();
|
||||||
card.getStyle().set("padding", "15px");
|
|
||||||
card.getStyle().set("border", "1px solid #e0e0e0");
|
|
||||||
card.getStyle().set("border-radius", "8px");
|
|
||||||
card.getStyle().set("cursor", "pointer");
|
card.getStyle().set("cursor", "pointer");
|
||||||
card.getStyle().set("background-color", "#ffffff");
|
|
||||||
card.getStyle().set("margin-bottom", "10px");
|
|
||||||
card.getStyle().set("max-width", "97.5%");
|
|
||||||
card.addClassName("message-card");
|
card.addClassName("message-card");
|
||||||
|
|
||||||
// Hover effect
|
|
||||||
card.getElement().addEventListener("mouseenter", e -> {
|
|
||||||
card.getStyle().set("background-color", "#f5f5f5");
|
|
||||||
});
|
|
||||||
card.getElement().addEventListener("mouseleave", e -> {
|
|
||||||
card.getStyle().set("background-color", "#ffffff");
|
|
||||||
});
|
|
||||||
|
|
||||||
// Title row with unread indicator
|
// Title row with unread indicator
|
||||||
HorizontalLayout titleRow = new HorizontalLayout();
|
HorizontalLayout titleRow = new HorizontalLayout();
|
||||||
titleRow.setWidthFull();
|
titleRow.setWidthFull();
|
||||||
titleRow.setAlignItems(com.vaadin.flow.component.orderedlayout.FlexComponent.Alignment.CENTER);
|
titleRow.setAlignItems(com.vaadin.flow.component.orderedlayout.FlexComponent.Alignment.CENTER);
|
||||||
|
titleRow.setSpacing(true);
|
||||||
|
|
||||||
Span titleSpan = new Span(conversationTitle);
|
Span titleSpan = new Span(conversationTitle);
|
||||||
titleSpan.getStyle().set("font-weight", "bold");
|
titleSpan.addClassName("message-section-title");
|
||||||
titleSpan.getStyle().set("font-size", "16px");
|
|
||||||
|
|
||||||
if (unreadCount > 0) {
|
if (unreadCount > 0) {
|
||||||
Span unreadBadge = new Span(String.valueOf(unreadCount));
|
Span unreadBadge = new Span(String.valueOf(unreadCount));
|
||||||
unreadBadge.getStyle().set("background-color", "var(--lumo-primary-color)");
|
unreadBadge.addClassName("message-badge");
|
||||||
unreadBadge.getStyle().set("color", "white");
|
|
||||||
unreadBadge.getStyle().set("border-radius", "50%");
|
|
||||||
unreadBadge.getStyle().set("padding", "2px 8px");
|
|
||||||
unreadBadge.getStyle().set("font-size", "12px");
|
|
||||||
unreadBadge.getStyle().set("font-weight", "bold");
|
|
||||||
titleRow.add(titleSpan, unreadBadge);
|
titleRow.add(titleSpan, unreadBadge);
|
||||||
} else {
|
} else {
|
||||||
titleRow.add(titleSpan);
|
titleRow.add(titleSpan);
|
||||||
@@ -235,20 +217,16 @@ public class UserMessagesView extends Main implements HasUrlParameter<String>, H
|
|||||||
// Preview text
|
// Preview text
|
||||||
Span preview = new Span(Optional.ofNullable(lastMessagePreview).filter(s -> !s.isBlank())
|
Span preview = new Span(Optional.ofNullable(lastMessagePreview).filter(s -> !s.isBlank())
|
||||||
.orElse(getTranslation("usermessages.preview.empty")));
|
.orElse(getTranslation("usermessages.preview.empty")));
|
||||||
preview.getStyle().set("color", "#666666");
|
preview.addClassName("message-preview");
|
||||||
preview.getStyle().set("font-size", "14px");
|
|
||||||
|
|
||||||
// Metadata row
|
// Metadata row
|
||||||
HorizontalLayout metaRow = new HorizontalLayout();
|
HorizontalLayout metaRow = new HorizontalLayout();
|
||||||
metaRow.setWidthFull();
|
metaRow.setWidthFull();
|
||||||
|
metaRow.addClassName("message-meta");
|
||||||
|
|
||||||
Span timeSpan = new Span(lastMessageTime != null ? DateTimeFormatUtil.formatDateTime(lastMessageTime) : "-");
|
Span timeSpan = new Span(lastMessageTime != null ? DateTimeFormatUtil.formatDateTime(lastMessageTime) : "-");
|
||||||
timeSpan.getStyle().set("color", "#999999");
|
|
||||||
timeSpan.getStyle().set("font-size", "12px");
|
|
||||||
|
|
||||||
Span countSpan = new Span(getTranslation("usermessages.message.count", messageCount));
|
Span countSpan = new Span(getTranslation("usermessages.message.count", messageCount));
|
||||||
countSpan.getStyle().set("color", "#999999");
|
|
||||||
countSpan.getStyle().set("font-size", "12px");
|
|
||||||
|
|
||||||
metaRow.add(timeSpan, countSpan);
|
metaRow.add(timeSpan, countSpan);
|
||||||
metaRow.expand(timeSpan);
|
metaRow.expand(timeSpan);
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ public class VerwaltungView extends Main implements HasDynamicTitle {
|
|||||||
setSizeFull();
|
setSizeFull();
|
||||||
addClassNames(LumoUtility.BoxSizing.BORDER, LumoUtility.Display.FLEX, LumoUtility.FlexDirection.COLUMN,
|
addClassNames(LumoUtility.BoxSizing.BORDER, LumoUtility.Display.FLEX, LumoUtility.FlexDirection.COLUMN,
|
||||||
LumoUtility.Padding.MEDIUM, LumoUtility.Gap.SMALL);
|
LumoUtility.Padding.MEDIUM, LumoUtility.Gap.SMALL);
|
||||||
|
addClassName("data-view");
|
||||||
|
|
||||||
add(new ViewToolbar(getTranslation("verwaltung.title")));
|
add(new ViewToolbar(getTranslation("verwaltung.title")));
|
||||||
|
|
||||||
@@ -37,6 +38,7 @@ public class VerwaltungView extends Main implements HasDynamicTitle {
|
|||||||
content.setDefaultHorizontalComponentAlignment(VerticalLayout.Alignment.CENTER);
|
content.setDefaultHorizontalComponentAlignment(VerticalLayout.Alignment.CENTER);
|
||||||
content.setJustifyContentMode(VerticalLayout.JustifyContentMode.CENTER);
|
content.setJustifyContentMode(VerticalLayout.JustifyContentMode.CENTER);
|
||||||
content.setSizeFull();
|
content.setSizeFull();
|
||||||
|
content.addClassNames("surface-panel", "section-panel");
|
||||||
|
|
||||||
add(content);
|
add(content);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user