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")
private Integer taskOrder = 0;
@Field("description")
private String description;
@Field("completed")
private boolean completed = false;

View File

@@ -391,9 +391,10 @@ public class AddJobView extends Main {
.setWeekdaysShort(java.util.Arrays.asList(
"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.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
submitButton.setEnabled(false);
}
// Testdaten entfernt
@@ -951,6 +952,16 @@ public class AddJobView extends Main {
private void triggerValidation() {
// Create a temporary job object to trigger validation
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) {
@@ -992,6 +1003,9 @@ public class AddJobView extends Main {
}
private void updateTabLabel(com.vaadin.flow.component.tabs.Tab tab, String baseLabel, boolean hasErrors) {
if (tab == null) {
return;
}
if (hasErrors) {
tab.setLabel(baseLabel + " ⚠️");
} else {
@@ -1043,7 +1057,28 @@ public class AddJobView extends Main {
}
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;
}
@@ -1275,7 +1310,8 @@ public class AddJobView extends Main {
cargoItemsState.remove(idx);
}
cargoList.remove(row);
updateTabLabels(); // Update tab validation when cargo item is removed
triggerValidation();
updateTabLabels();
});
row.add(desc, qty, weight, len, wid, hei, remove);
@@ -1317,32 +1353,38 @@ public class AddJobView extends Main {
desc.addValueChangeListener(ev -> {
item.setDescription(ev.getValue());
validateField.accept(desc);
updateTabLabels(); // Update tab validation when cargo description changes
triggerValidation();
updateTabLabels();
});
qty.addValueChangeListener(ev -> {
item.setQuantity(ev.getValue());
validateField.accept(qty);
updateTabLabels(); // Update tab validation when cargo quantity changes
triggerValidation();
updateTabLabels();
});
weight.addValueChangeListener(ev -> {
item.setWeightKg(ev.getValue());
validateField.accept(weight);
updateTabLabels(); // Update tab validation when cargo weight changes
triggerValidation();
updateTabLabels();
});
len.addValueChangeListener(ev -> {
item.setLengthMm(ev.getValue());
validateField.accept(len);
updateTabLabels(); // Update tab validation when cargo length changes
triggerValidation();
updateTabLabels();
});
wid.addValueChangeListener(ev -> {
item.setWidthMm(ev.getValue());
validateField.accept(wid);
updateTabLabels(); // Update tab validation when cargo width changes
triggerValidation();
updateTabLabels();
});
hei.addValueChangeListener(ev -> {
item.setHeightMm(ev.getValue());
validateField.accept(hei);
updateTabLabels(); // Update tab validation when cargo height changes
triggerValidation();
updateTabLabels();
});
// Initial validation
@@ -1679,12 +1721,16 @@ public class AddJobView extends Main {
}
updateTaskConfiguration(configContainer, newTask);
triggerValidation();
updateTabLabels();
}
});
// Set initial configuration
taskTypeCombo.setValue(TaskType.CONFIRMATION);
updateTaskConfiguration(configContainer, currentTask[0]);
triggerValidation();
updateTabLabels();
tasksList.add(taskContainer);
}
@@ -1717,6 +1763,33 @@ public class AddJobView extends Main {
switch (taskType) {
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");
buttonTextField.setPlaceholder("z.B. 'Bestätigen', 'Abgeschlossen'");
buttonTextField.setWidthFull();
@@ -1725,10 +1798,12 @@ public class AddJobView extends Main {
buttonTextField.addValueChangeListener(ev -> {
// Find the current ConfirmationTask in tasksState and update it
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;
case SIGNATURE:
@@ -1753,6 +1828,18 @@ public class AddJobView extends Main {
todoList.setPadding(false);
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) -> {
HorizontalLayout todoRow = new HorizontalLayout();
todoRow.setWidthFull();
@@ -1761,6 +1848,9 @@ public class AddJobView extends Main {
TextField todoField = new TextField();
todoField.setPlaceholder("To-Do Punkt");
todoField.setWidth("100%");
todoField.setRequiredIndicatorVisible(true);
// Initial red styling for empty field
updateTodoFieldStyling.accept(todoField);
Button removeTodo = new Button(new Icon(VaadinIcon.CLOSE_SMALL));
removeTodo.addThemeVariants(ButtonVariant.LUMO_ERROR, ButtonVariant.LUMO_TERTIARY);
@@ -1773,7 +1863,10 @@ public class AddJobView extends Main {
todoRow.setFlexGrow(1, todoField);
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));
@@ -1793,7 +1886,10 @@ public class AddJobView extends Main {
TextField todoField = new TextField();
todoField.setPlaceholder("To-Do Punkt");
todoField.setWidth("100%");
todoField.setRequiredIndicatorVisible(true);
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));
removeTodo.addThemeVariants(ButtonVariant.LUMO_ERROR, ButtonVariant.LUMO_TERTIARY);
@@ -1806,7 +1902,10 @@ public class AddJobView extends Main {
todoRow.setFlexGrow(1, todoField);
todoList.add(todoRow);
todoField.addValueChangeListener(ev -> updateTodoItems(todoList, task));
todoField.addValueChangeListener(ev -> {
updateTodoFieldStyling.accept(todoField);
updateTodoItems(todoList, task);
});
}
} else {
// Add initial empty todo item if no existing items
@@ -1894,6 +1993,10 @@ public class AddJobView extends Main {
if (task instanceof TodoListTask) {
((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);
triggerValidation();
updateTabLabels();
}
});
@@ -2201,6 +2306,8 @@ public class AddJobView extends Main {
if (taskType != null) {
taskTypeCombo.setValue(taskType);
updateTaskConfiguration(configContainer, task);
triggerValidation();
updateTabLabels();
}
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.pages.base.ui.view.MainLayout;
import jakarta.annotation.security.RolesAllowed;
import com.vaadin.flow.component.UI;
@Route(value = "dashboard", layout = MainLayout.class)
@PageTitle("VotianLT - Dashboard")
@@ -96,12 +95,13 @@ public class AuthenticatedStartView extends VerticalLayout {
systemIntro.getStyle().set("max-width", "800px");
systemIntro.getStyle().set("margin-bottom", "var(--lumo-space-xl)");
// Features Grid
HorizontalLayout featuresGrid = new HorizontalLayout();
featuresGrid.setSpacing(true);
featuresGrid.setJustifyContentMode(FlexComponent.JustifyContentMode.CENTER);
featuresGrid.setDefaultVerticalComponentAlignment(FlexComponent.Alignment.START);
// Features Grid - using Div with CSS flexbox for proper centering
Div featuresGrid = new Div();
featuresGrid.getStyle().set("display", "flex");
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%");
// Feature Cards
@@ -126,6 +126,10 @@ public class AuthenticatedStartView extends VerticalLayout {
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.getStyle().set("flex-grow", "0");
card.getStyle().set("flex-shrink", "0");
Icon icon = iconType.create();
icon.setSize("48px");
@@ -177,6 +181,7 @@ public class AuthenticatedStartView extends VerticalLayout {
footer.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.CENTER);
footer.getStyle().set("background-color", "var(--lumo-contrast-10pct)");
footer.getStyle().set("border-top", "1px solid var(--lumo-contrast-20pct)");
footer.getStyle().set("margin-top", "auto");
HorizontalLayout footerContent = new HorizontalLayout();
footerContent.setDefaultVerticalComponentAlignment(FlexComponent.Alignment.CENTER);

View File

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