Erweiterungen

This commit is contained in:
2026-01-07 09:56:34 +01:00
parent 9b838863d9
commit 20cd516317
4 changed files with 135 additions and 19 deletions

View File

@@ -35,6 +35,9 @@ public abstract class BaseTask {
@Field("task_order") @Field("task_order")
private Integer taskOrder = 0; private Integer taskOrder = 0;
@Field("description")
private String description;
@Field("completed") @Field("completed")
private boolean completed = false; private boolean completed = false;

View File

@@ -391,9 +391,10 @@ public class AddJobView extends Main {
.setWeekdaysShort(java.util.Arrays.asList( .setWeekdaysShort(java.util.Arrays.asList(
"So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"))); "So", "Mo", "Di", "Mi", "Do", "Fr", "Sa")));
// Submit button // Submit button - initially disabled until all required fields are valid
submitButton = new Button("Auftrag anlegen", event -> submit()); submitButton = new Button("Auftrag anlegen", event -> submit());
submitButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY); submitButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
submitButton.setEnabled(false);
} }
// Testdaten entfernt // Testdaten entfernt
@@ -951,6 +952,16 @@ public class AddJobView extends Main {
private void triggerValidation() { private void triggerValidation() {
// Create a temporary job object to trigger validation // Create a temporary job object to trigger validation
binder.validate(); binder.validate();
// Update submit button state based on all validation checks
if (submitButton != null) {
boolean hasErrors = hasAddressValidationErrors()
|| hasAppointmentValidationErrors()
|| hasCargoValidationErrors()
|| hasPriceValidationErrors()
|| hasTasksValidationErrors();
submitButton.setEnabled(!hasErrors);
}
} }
private void updateFieldStyling(TextField field) { private void updateFieldStyling(TextField field) {
@@ -992,6 +1003,9 @@ public class AddJobView extends Main {
} }
private void updateTabLabel(com.vaadin.flow.component.tabs.Tab tab, String baseLabel, boolean hasErrors) { private void updateTabLabel(com.vaadin.flow.component.tabs.Tab tab, String baseLabel, boolean hasErrors) {
if (tab == null) {
return;
}
if (hasErrors) { if (hasErrors) {
tab.setLabel(baseLabel + " ⚠️"); tab.setLabel(baseLabel + " ⚠️");
} else { } else {
@@ -1043,7 +1057,28 @@ public class AddJobView extends Main {
} }
private boolean hasTasksValidationErrors() { private boolean hasTasksValidationErrors() {
// Tasks are optional, so no validation errors for tasks for (BaseTask task : tasksState) {
// Check if any ConfirmationTask has an empty description (required field)
if (task instanceof ConfirmationTask) {
String description = task.getDescription();
if (description == null || description.trim().isEmpty()) {
return true;
}
}
// Check if any TodoListTask has at least one non-empty todo item
if (task instanceof TodoListTask todoListTask) {
List<String> todoItems = todoListTask.getTodoItems();
if (todoItems == null || todoItems.isEmpty()) {
return true;
}
// Check if at least one todo item is non-empty
boolean hasValidTodoItem = todoItems.stream()
.anyMatch(item -> item != null && !item.trim().isEmpty());
if (!hasValidTodoItem) {
return true;
}
}
}
return false; return false;
} }
@@ -1275,7 +1310,8 @@ public class AddJobView extends Main {
cargoItemsState.remove(idx); cargoItemsState.remove(idx);
} }
cargoList.remove(row); cargoList.remove(row);
updateTabLabels(); // Update tab validation when cargo item is removed triggerValidation();
updateTabLabels();
}); });
row.add(desc, qty, weight, len, wid, hei, remove); row.add(desc, qty, weight, len, wid, hei, remove);
@@ -1317,32 +1353,38 @@ public class AddJobView extends Main {
desc.addValueChangeListener(ev -> { desc.addValueChangeListener(ev -> {
item.setDescription(ev.getValue()); item.setDescription(ev.getValue());
validateField.accept(desc); validateField.accept(desc);
updateTabLabels(); // Update tab validation when cargo description changes triggerValidation();
updateTabLabels();
}); });
qty.addValueChangeListener(ev -> { qty.addValueChangeListener(ev -> {
item.setQuantity(ev.getValue()); item.setQuantity(ev.getValue());
validateField.accept(qty); validateField.accept(qty);
updateTabLabels(); // Update tab validation when cargo quantity changes triggerValidation();
updateTabLabels();
}); });
weight.addValueChangeListener(ev -> { weight.addValueChangeListener(ev -> {
item.setWeightKg(ev.getValue()); item.setWeightKg(ev.getValue());
validateField.accept(weight); validateField.accept(weight);
updateTabLabels(); // Update tab validation when cargo weight changes triggerValidation();
updateTabLabels();
}); });
len.addValueChangeListener(ev -> { len.addValueChangeListener(ev -> {
item.setLengthMm(ev.getValue()); item.setLengthMm(ev.getValue());
validateField.accept(len); validateField.accept(len);
updateTabLabels(); // Update tab validation when cargo length changes triggerValidation();
updateTabLabels();
}); });
wid.addValueChangeListener(ev -> { wid.addValueChangeListener(ev -> {
item.setWidthMm(ev.getValue()); item.setWidthMm(ev.getValue());
validateField.accept(wid); validateField.accept(wid);
updateTabLabels(); // Update tab validation when cargo width changes triggerValidation();
updateTabLabels();
}); });
hei.addValueChangeListener(ev -> { hei.addValueChangeListener(ev -> {
item.setHeightMm(ev.getValue()); item.setHeightMm(ev.getValue());
validateField.accept(hei); validateField.accept(hei);
updateTabLabels(); // Update tab validation when cargo height changes triggerValidation();
updateTabLabels();
}); });
// Initial validation // Initial validation
@@ -1679,12 +1721,16 @@ public class AddJobView extends Main {
} }
updateTaskConfiguration(configContainer, newTask); updateTaskConfiguration(configContainer, newTask);
triggerValidation();
updateTabLabels();
} }
}); });
// Set initial configuration // Set initial configuration
taskTypeCombo.setValue(TaskType.CONFIRMATION); taskTypeCombo.setValue(TaskType.CONFIRMATION);
updateTaskConfiguration(configContainer, currentTask[0]); updateTaskConfiguration(configContainer, currentTask[0]);
triggerValidation();
updateTabLabels();
tasksList.add(taskContainer); tasksList.add(taskContainer);
} }
@@ -1717,6 +1763,33 @@ public class AddJobView extends Main {
switch (taskType) { switch (taskType) {
case CONFIRMATION: case CONFIRMATION:
// Description field (required)
TextField descriptionField = new TextField("Beschreibung");
descriptionField.setPlaceholder("Beschreibung der Aufgabe...");
descriptionField.setWidthFull();
descriptionField.setRequiredIndicatorVisible(true);
descriptionField.setValue(task.getDescription() != null ? task.getDescription() : "");
descriptionField.addValueChangeListener(ev -> {
task.setDescription(ev.getValue());
// Update field styling based on value
boolean isEmpty = ev.getValue() == null || ev.getValue().trim().isEmpty();
if (isEmpty) {
descriptionField.getStyle().set("--vaadin-input-field-background", "rgba(255, 0, 0, 0.1)");
descriptionField.getStyle().set("--vaadin-input-field-border-color", "rgba(255, 0, 0, 0.3)");
} else {
descriptionField.getStyle().remove("--vaadin-input-field-background");
descriptionField.getStyle().remove("--vaadin-input-field-border-color");
}
triggerValidation();
updateTabLabels();
});
// Initial styling for empty field
if (task.getDescription() == null || task.getDescription().trim().isEmpty()) {
descriptionField.getStyle().set("--vaadin-input-field-background", "rgba(255, 0, 0, 0.1)");
descriptionField.getStyle().set("--vaadin-input-field-border-color", "rgba(255, 0, 0, 0.3)");
}
// Button text field
TextField buttonTextField = new TextField("Button-Text"); TextField buttonTextField = new TextField("Button-Text");
buttonTextField.setPlaceholder("z.B. 'Bestätigen', 'Abgeschlossen'"); buttonTextField.setPlaceholder("z.B. 'Bestätigen', 'Abgeschlossen'");
buttonTextField.setWidthFull(); buttonTextField.setWidthFull();
@@ -1725,10 +1798,12 @@ public class AddJobView extends Main {
buttonTextField.addValueChangeListener(ev -> { buttonTextField.addValueChangeListener(ev -> {
// Find the current ConfirmationTask in tasksState and update it // Find the current ConfirmationTask in tasksState and update it
for (BaseTask stateTask : tasksState) { for (BaseTask stateTask : tasksState) {
((ConfirmationTask) stateTask).setButtonText(ev.getValue()); if (stateTask instanceof ConfirmationTask) {
((ConfirmationTask) stateTask).setButtonText(ev.getValue());
}
} }
}); });
configContainer.add(buttonTextField); configContainer.add(descriptionField, buttonTextField);
break; break;
case SIGNATURE: case SIGNATURE:
@@ -1753,6 +1828,18 @@ public class AddJobView extends Main {
todoList.setPadding(false); todoList.setPadding(false);
todoList.setSpacing(true); todoList.setSpacing(true);
// Helper to update todo field styling based on value
java.util.function.Consumer<TextField> updateTodoFieldStyling = (field) -> {
boolean isEmpty = field.getValue() == null || field.getValue().trim().isEmpty();
if (isEmpty) {
field.getStyle().set("--vaadin-input-field-background", "rgba(255, 0, 0, 0.1)");
field.getStyle().set("--vaadin-input-field-border-color", "rgba(255, 0, 0, 0.3)");
} else {
field.getStyle().remove("--vaadin-input-field-background");
field.getStyle().remove("--vaadin-input-field-border-color");
}
};
java.util.function.Consumer<Void> addTodoItem = (v) -> { java.util.function.Consumer<Void> addTodoItem = (v) -> {
HorizontalLayout todoRow = new HorizontalLayout(); HorizontalLayout todoRow = new HorizontalLayout();
todoRow.setWidthFull(); todoRow.setWidthFull();
@@ -1761,6 +1848,9 @@ public class AddJobView extends Main {
TextField todoField = new TextField(); TextField todoField = new TextField();
todoField.setPlaceholder("To-Do Punkt"); todoField.setPlaceholder("To-Do Punkt");
todoField.setWidth("100%"); todoField.setWidth("100%");
todoField.setRequiredIndicatorVisible(true);
// Initial red styling for empty field
updateTodoFieldStyling.accept(todoField);
Button removeTodo = new Button(new Icon(VaadinIcon.CLOSE_SMALL)); Button removeTodo = new Button(new Icon(VaadinIcon.CLOSE_SMALL));
removeTodo.addThemeVariants(ButtonVariant.LUMO_ERROR, ButtonVariant.LUMO_TERTIARY); removeTodo.addThemeVariants(ButtonVariant.LUMO_ERROR, ButtonVariant.LUMO_TERTIARY);
@@ -1773,7 +1863,10 @@ public class AddJobView extends Main {
todoRow.setFlexGrow(1, todoField); todoRow.setFlexGrow(1, todoField);
todoList.add(todoRow); todoList.add(todoRow);
todoField.addValueChangeListener(ev -> updateTodoItems(todoList, task)); todoField.addValueChangeListener(ev -> {
updateTodoFieldStyling.accept(todoField);
updateTodoItems(todoList, task);
});
}; };
Button addTodoBtn = new Button("To-Do Punkt hinzufügen", new Icon(VaadinIcon.PLUS)); Button addTodoBtn = new Button("To-Do Punkt hinzufügen", new Icon(VaadinIcon.PLUS));
@@ -1793,7 +1886,10 @@ public class AddJobView extends Main {
TextField todoField = new TextField(); TextField todoField = new TextField();
todoField.setPlaceholder("To-Do Punkt"); todoField.setPlaceholder("To-Do Punkt");
todoField.setWidth("100%"); todoField.setWidth("100%");
todoField.setRequiredIndicatorVisible(true);
todoField.setValue(todoText != null ? todoText : ""); // Set the saved text todoField.setValue(todoText != null ? todoText : ""); // Set the saved text
// Apply styling based on value
updateTodoFieldStyling.accept(todoField);
Button removeTodo = new Button(new Icon(VaadinIcon.CLOSE_SMALL)); Button removeTodo = new Button(new Icon(VaadinIcon.CLOSE_SMALL));
removeTodo.addThemeVariants(ButtonVariant.LUMO_ERROR, ButtonVariant.LUMO_TERTIARY); removeTodo.addThemeVariants(ButtonVariant.LUMO_ERROR, ButtonVariant.LUMO_TERTIARY);
@@ -1806,7 +1902,10 @@ public class AddJobView extends Main {
todoRow.setFlexGrow(1, todoField); todoRow.setFlexGrow(1, todoField);
todoList.add(todoRow); todoList.add(todoRow);
todoField.addValueChangeListener(ev -> updateTodoItems(todoList, task)); todoField.addValueChangeListener(ev -> {
updateTodoFieldStyling.accept(todoField);
updateTodoItems(todoList, task);
});
} }
} else { } else {
// Add initial empty todo item if no existing items // Add initial empty todo item if no existing items
@@ -1894,6 +1993,10 @@ public class AddJobView extends Main {
if (task instanceof TodoListTask) { if (task instanceof TodoListTask) {
((TodoListTask) task).setTodoItems(todoItems); ((TodoListTask) task).setTodoItems(todoItems);
} }
// Trigger validation to update submit button state
triggerValidation();
updateTabLabels();
} }
/** /**
@@ -2193,6 +2296,8 @@ public class AddJobView extends Main {
} }
updateTaskConfiguration(configContainer, newTask); updateTaskConfiguration(configContainer, newTask);
triggerValidation();
updateTabLabels();
} }
}); });
@@ -2201,6 +2306,8 @@ public class AddJobView extends Main {
if (taskType != null) { if (taskType != null) {
taskTypeCombo.setValue(taskType); taskTypeCombo.setValue(taskType);
updateTaskConfiguration(configContainer, task); updateTaskConfiguration(configContainer, task);
triggerValidation();
updateTabLabels();
} }
tasksList.add(taskContainer); tasksList.add(taskContainer);

View File

@@ -12,7 +12,6 @@ import com.vaadin.flow.router.Route;
import de.assecutor.votianlt.security.SecurityService; import de.assecutor.votianlt.security.SecurityService;
import de.assecutor.votianlt.pages.base.ui.view.MainLayout; import de.assecutor.votianlt.pages.base.ui.view.MainLayout;
import jakarta.annotation.security.RolesAllowed; import jakarta.annotation.security.RolesAllowed;
import com.vaadin.flow.component.UI;
@Route(value = "dashboard", layout = MainLayout.class) @Route(value = "dashboard", layout = MainLayout.class)
@PageTitle("VotianLT - Dashboard") @PageTitle("VotianLT - Dashboard")
@@ -96,12 +95,13 @@ public class AuthenticatedStartView extends VerticalLayout {
systemIntro.getStyle().set("max-width", "800px"); systemIntro.getStyle().set("max-width", "800px");
systemIntro.getStyle().set("margin-bottom", "var(--lumo-space-xl)"); systemIntro.getStyle().set("margin-bottom", "var(--lumo-space-xl)");
// Features Grid // Features Grid - using Div with CSS flexbox for proper centering
HorizontalLayout featuresGrid = new HorizontalLayout(); Div featuresGrid = new Div();
featuresGrid.setSpacing(true); featuresGrid.getStyle().set("display", "flex");
featuresGrid.setJustifyContentMode(FlexComponent.JustifyContentMode.CENTER);
featuresGrid.setDefaultVerticalComponentAlignment(FlexComponent.Alignment.START);
featuresGrid.getStyle().set("flex-wrap", "wrap"); featuresGrid.getStyle().set("flex-wrap", "wrap");
featuresGrid.getStyle().set("justify-content", "center");
featuresGrid.getStyle().set("align-items", "flex-start");
featuresGrid.getStyle().set("gap", "var(--lumo-space-l)");
featuresGrid.getStyle().set("width", "100%"); featuresGrid.getStyle().set("width", "100%");
// Feature Cards // Feature Cards
@@ -126,6 +126,10 @@ public class AuthenticatedStartView extends VerticalLayout {
card.getStyle().set("border-radius", "var(--lumo-border-radius-m)"); card.getStyle().set("border-radius", "var(--lumo-border-radius-m)");
card.getStyle().set("box-shadow", "var(--lumo-box-shadow-s)"); card.getStyle().set("box-shadow", "var(--lumo-box-shadow-s)");
card.setWidth("300px"); card.setWidth("300px");
card.setMinWidth("300px");
card.setMaxWidth("300px");
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");
@@ -177,6 +181,7 @@ public class AuthenticatedStartView extends VerticalLayout {
footer.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.CENTER); footer.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.CENTER);
footer.getStyle().set("background-color", "var(--lumo-contrast-10pct)"); footer.getStyle().set("background-color", "var(--lumo-contrast-10pct)");
footer.getStyle().set("border-top", "1px solid var(--lumo-contrast-20pct)"); 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);

View File

@@ -221,6 +221,7 @@ public class StartView extends VerticalLayout implements BeforeEnterObserver {
HorizontalLayout featuresGrid = new HorizontalLayout(); HorizontalLayout featuresGrid = new HorizontalLayout();
featuresGrid.setWidthFull(); featuresGrid.setWidthFull();
featuresGrid.setSpacing(true); featuresGrid.setSpacing(true);
featuresGrid.setJustifyContentMode(FlexComponent.JustifyContentMode.CENTER);
featuresGrid.setDefaultVerticalComponentAlignment(FlexComponent.Alignment.START); featuresGrid.setDefaultVerticalComponentAlignment(FlexComponent.Alignment.START);
// Feature Cards // Feature Cards