Erweiterungen
This commit is contained in:
@@ -11,6 +11,8 @@ import org.springframework.data.mongodb.core.mapping.Document;
|
||||
import org.springframework.data.mongodb.core.mapping.Field;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@@ -28,6 +30,13 @@ public class TaskEntry {
|
||||
@Field("text")
|
||||
private String text;
|
||||
|
||||
@Field("task_type")
|
||||
private TaskType taskType = TaskType.CONFIRMATION;
|
||||
|
||||
// Task-specific configuration data
|
||||
@Field("configuration")
|
||||
private TaskConfiguration configuration;
|
||||
|
||||
// Completion tracking
|
||||
@Field("completed")
|
||||
private boolean completed = false;
|
||||
@@ -58,5 +67,51 @@ public class TaskEntry {
|
||||
public String getJobIdAsString() {
|
||||
return jobId != null ? jobId.toString() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enum for different task types
|
||||
*/
|
||||
public enum TaskType {
|
||||
CONFIRMATION("Bestätigung"),
|
||||
SIGNATURE("Unterschrift"),
|
||||
TODOLIST("To-Do Liste"),
|
||||
PHOTO("Foto"),
|
||||
BARCODE("Barcode");
|
||||
|
||||
private final String displayName;
|
||||
|
||||
TaskType(String displayName) {
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration data for different task types
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class TaskConfiguration {
|
||||
// For CONFIRMATION: button text
|
||||
private String buttonText;
|
||||
|
||||
// For TODOLIST: list of todo items
|
||||
private List<String> todoItems;
|
||||
|
||||
// For PHOTO: min and max photo count
|
||||
private Integer minPhotoCount;
|
||||
private Integer maxPhotoCount;
|
||||
|
||||
// For BARCODE: min and max barcode count
|
||||
private Integer minBarcodeCount;
|
||||
private Integer maxBarcodeCount;
|
||||
|
||||
// Generic configuration map for future extensions
|
||||
private Map<String, Object> additionalConfig;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -100,6 +100,7 @@ public class AddJobView extends Main {
|
||||
private com.vaadin.flow.component.tabs.Tab addressesTab;
|
||||
private com.vaadin.flow.component.tabs.Tab appointmentsTab;
|
||||
private com.vaadin.flow.component.tabs.Tab cargoTab;
|
||||
private com.vaadin.flow.component.tabs.Tab tasksTab;
|
||||
private com.vaadin.flow.component.tabs.Tab priceTab;
|
||||
|
||||
// Submit button
|
||||
@@ -327,10 +328,13 @@ public class AddJobView extends Main {
|
||||
// Tab 2: Appointments & Processing
|
||||
appointmentsTab = tabSheet.add("Termine & Verarbeitung", createAppointmentsAndProcessingTab());
|
||||
|
||||
// Tab 3: Cargo & Tasks
|
||||
cargoTab = tabSheet.add("Ladung & Aufgaben", createCargoAndTasksTab());
|
||||
// Tab 3: Cargo
|
||||
cargoTab = tabSheet.add("Ladung", createCargoTab());
|
||||
|
||||
// Tab 4: Price & Submit
|
||||
// Tab 4: Tasks
|
||||
tasksTab = tabSheet.add("Aufgaben", createTasksTab());
|
||||
|
||||
// Tab 5: Price & Submit
|
||||
priceTab = tabSheet.add("Preis & Abschluss", createPriceAndSubmitTab());
|
||||
|
||||
add(tabSheet);
|
||||
@@ -436,7 +440,7 @@ public class AddJobView extends Main {
|
||||
return tabContent;
|
||||
}
|
||||
|
||||
private Component createCargoAndTasksTab() {
|
||||
private Component createCargoTab() {
|
||||
VerticalLayout tabContent = new VerticalLayout();
|
||||
tabContent.setSizeFull();
|
||||
tabContent.setPadding(true);
|
||||
@@ -445,6 +449,15 @@ public class AddJobView extends Main {
|
||||
// Add cargo section
|
||||
tabContent.add(createCargoSection());
|
||||
|
||||
return tabContent;
|
||||
}
|
||||
|
||||
private Component createTasksTab() {
|
||||
VerticalLayout tabContent = new VerticalLayout();
|
||||
tabContent.setSizeFull();
|
||||
tabContent.setPadding(true);
|
||||
tabContent.setSpacing(true);
|
||||
|
||||
// Add tasks and notes section
|
||||
tabContent.add(createTasksAndNotesSection());
|
||||
|
||||
@@ -915,7 +928,8 @@ public class AddJobView extends Main {
|
||||
// Check validation state for each tab and update labels with exclamation marks
|
||||
updateTabLabel(addressesTab, "Auftraggeber & Adressen", hasAddressValidationErrors());
|
||||
updateTabLabel(appointmentsTab, "Termine & Verarbeitung", hasAppointmentValidationErrors());
|
||||
updateTabLabel(cargoTab, "Ladung & Aufgaben", hasCargoValidationErrors());
|
||||
updateTabLabel(cargoTab, "Ladung", hasCargoValidationErrors());
|
||||
updateTabLabel(tasksTab, "Aufgaben", hasTasksValidationErrors());
|
||||
updateTabLabel(priceTab, "Preis & Abschluss", hasPriceValidationErrors());
|
||||
}
|
||||
|
||||
@@ -969,6 +983,11 @@ public class AddJobView extends Main {
|
||||
return !allCargoItemsValid; // Return true if ANY cargo item is incomplete (show warning)
|
||||
}
|
||||
|
||||
private boolean hasTasksValidationErrors() {
|
||||
// Tasks are optional, so no validation errors for tasks
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean hasPriceValidationErrors() {
|
||||
return isFieldEmpty(price);
|
||||
}
|
||||
@@ -1066,9 +1085,9 @@ public class AddJobView extends Main {
|
||||
|
||||
} catch (Exception e) {
|
||||
// Other errors
|
||||
// Reset cargo error
|
||||
if (cargoError != null) cargoError.setVisible(false);
|
||||
if (cargoAreaContainer != null) cargoAreaContainer.getStyle().set("border", "1px solid var(--lumo-contrast-20pct)");
|
||||
// Reset cargo error
|
||||
if (cargoError != null) cargoError.setVisible(false);
|
||||
if (cargoAreaContainer != null) cargoAreaContainer.getStyle().set("border", "1px solid var(--lumo-contrast-20pct)");
|
||||
Notification errorNotification = Notification.show(
|
||||
"Fehler beim Erstellen des Auftrags: " + e.getMessage());
|
||||
errorNotification.setDuration(5000);
|
||||
@@ -1265,31 +1284,7 @@ public class AddJobView extends Main {
|
||||
tasksList.setSpacing(true);
|
||||
|
||||
java.util.function.Consumer<Void> addTask = (v) -> {
|
||||
HorizontalLayout row = new HorizontalLayout();
|
||||
row.setWidthFull();
|
||||
row.setAlignItems(FlexComponent.Alignment.END);
|
||||
|
||||
TextField taskField = new TextField();
|
||||
taskField.setPlaceholder("Aufgabe");
|
||||
taskField.setWidth("100%");
|
||||
|
||||
Button remove = new Button(new Icon(VaadinIcon.CLOSE_SMALL));
|
||||
remove.addThemeVariants(ButtonVariant.LUMO_ERROR, ButtonVariant.LUMO_TERTIARY);
|
||||
remove.addClickListener(e -> {
|
||||
int idx = tasksList.getChildren().toList().indexOf(row);
|
||||
if (idx >= 0 && idx < tasksState.size()) tasksState.remove(idx);
|
||||
tasksList.remove(row);
|
||||
});
|
||||
|
||||
row.add(taskField, remove);
|
||||
row.setFlexGrow(1, taskField);
|
||||
tasksList.add(row);
|
||||
|
||||
// Keep backing tasks list in sync
|
||||
TaskEntry te = new TaskEntry();
|
||||
te.setText(taskField.getValue());
|
||||
tasksState.add(te);
|
||||
taskField.addValueChangeListener(ev -> te.setText(ev.getValue()));
|
||||
createTaskRow();
|
||||
};
|
||||
|
||||
// 1 Beispielzeile
|
||||
@@ -1458,4 +1453,226 @@ public class AddJobView extends Main {
|
||||
}
|
||||
}
|
||||
|
||||
private void createTaskRow() {
|
||||
VerticalLayout taskContainer = new VerticalLayout();
|
||||
taskContainer.setPadding(true);
|
||||
taskContainer.setSpacing(true);
|
||||
taskContainer.getStyle().set("border", "1px solid var(--lumo-contrast-20pct)");
|
||||
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
|
||||
ComboBox<TaskEntry.TaskType> taskTypeCombo = new ComboBox<>("Aufgabentyp");
|
||||
taskTypeCombo.setItems(TaskEntry.TaskType.values());
|
||||
taskTypeCombo.setItemLabelGenerator(TaskEntry.TaskType::getDisplayName);
|
||||
taskTypeCombo.setPlaceholder("Aufgabentyp wählen...");
|
||||
taskTypeCombo.setWidthFull();
|
||||
|
||||
// Configuration container for dynamic fields
|
||||
VerticalLayout configContainer = new VerticalLayout();
|
||||
configContainer.setPadding(false);
|
||||
configContainer.setSpacing(true);
|
||||
|
||||
// Red X button positioned in top-right corner
|
||||
Button deleteXButton = new Button(new Icon(VaadinIcon.CLOSE_SMALL));
|
||||
deleteXButton.addThemeVariants(ButtonVariant.LUMO_ERROR, ButtonVariant.LUMO_TERTIARY);
|
||||
deleteXButton.getStyle().set("position", "absolute");
|
||||
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 -> {
|
||||
int idx = tasksList.getChildren().toList().indexOf(taskContainer);
|
||||
if (idx >= 0 && idx < tasksState.size()) tasksState.remove(idx);
|
||||
tasksList.remove(taskContainer);
|
||||
});
|
||||
|
||||
taskContainer.add(taskTypeCombo, configContainer);
|
||||
taskContainer.add(deleteXButton);
|
||||
|
||||
// Create TaskEntry and add to state
|
||||
TaskEntry taskEntry = new TaskEntry();
|
||||
taskEntry.setText("");
|
||||
taskEntry.setTaskType(TaskEntry.TaskType.CONFIRMATION);
|
||||
taskEntry.setConfiguration(new TaskEntry.TaskConfiguration());
|
||||
tasksState.add(taskEntry);
|
||||
|
||||
taskTypeCombo.addValueChangeListener(ev -> {
|
||||
TaskEntry.TaskType selectedType = ev.getValue();
|
||||
if (selectedType != null) {
|
||||
taskEntry.setTaskType(selectedType);
|
||||
updateTaskConfiguration(configContainer, taskEntry);
|
||||
}
|
||||
});
|
||||
|
||||
// Set initial configuration
|
||||
taskTypeCombo.setValue(TaskEntry.TaskType.CONFIRMATION);
|
||||
updateTaskConfiguration(configContainer, taskEntry);
|
||||
|
||||
tasksList.add(taskContainer);
|
||||
}
|
||||
|
||||
private void updateTaskConfiguration(VerticalLayout configContainer, TaskEntry taskEntry) {
|
||||
configContainer.removeAll();
|
||||
|
||||
TaskEntry.TaskType taskType = taskEntry.getTaskType();
|
||||
if (taskType == null) return;
|
||||
|
||||
// Ensure configuration is initialized
|
||||
if (taskEntry.getConfiguration() == null) {
|
||||
taskEntry.setConfiguration(new TaskEntry.TaskConfiguration());
|
||||
}
|
||||
|
||||
switch (taskType) {
|
||||
case CONFIRMATION:
|
||||
TextField buttonTextField = new TextField("Button-Text");
|
||||
buttonTextField.setPlaceholder("z.B. 'Bestätigen', 'Abgeschlossen'");
|
||||
buttonTextField.setWidthFull();
|
||||
buttonTextField.setValue(taskEntry.getConfiguration().getButtonText() != null ?
|
||||
taskEntry.getConfiguration().getButtonText() : "");
|
||||
buttonTextField.addValueChangeListener(ev -> {
|
||||
taskEntry.getConfiguration().setButtonText(ev.getValue());
|
||||
});
|
||||
configContainer.add(buttonTextField);
|
||||
break;
|
||||
|
||||
case SIGNATURE:
|
||||
// No additional configuration needed
|
||||
Span info = new Span("Keine zusätzliche Konfiguration erforderlich");
|
||||
info.getStyle().set("color", "var(--lumo-secondary-text-color)");
|
||||
info.getStyle().set("font-style", "italic");
|
||||
configContainer.add(info);
|
||||
break;
|
||||
|
||||
case TODOLIST:
|
||||
VerticalLayout todoContainer = new VerticalLayout();
|
||||
todoContainer.setPadding(false);
|
||||
todoContainer.setSpacing(true);
|
||||
|
||||
H3 todoTitle = new H3("To-Do Punkte");
|
||||
todoTitle.getStyle().set("margin", "0");
|
||||
todoContainer.add(todoTitle);
|
||||
|
||||
// Dynamic todo list
|
||||
VerticalLayout todoList = new VerticalLayout();
|
||||
todoList.setPadding(false);
|
||||
todoList.setSpacing(true);
|
||||
|
||||
java.util.function.Consumer<Void> addTodoItem = (v) -> {
|
||||
HorizontalLayout todoRow = new HorizontalLayout();
|
||||
todoRow.setWidthFull();
|
||||
todoRow.setAlignItems(FlexComponent.Alignment.END);
|
||||
|
||||
TextField todoField = new TextField();
|
||||
todoField.setPlaceholder("To-Do Punkt");
|
||||
todoField.setWidth("100%");
|
||||
|
||||
Button removeTodo = new Button(new Icon(VaadinIcon.CLOSE_SMALL));
|
||||
removeTodo.addThemeVariants(ButtonVariant.LUMO_ERROR, ButtonVariant.LUMO_TERTIARY);
|
||||
removeTodo.addClickListener(e -> {
|
||||
todoList.remove(todoRow);
|
||||
updateTodoItems(todoList, taskEntry);
|
||||
});
|
||||
|
||||
todoRow.add(todoField, removeTodo);
|
||||
todoRow.setFlexGrow(1, todoField);
|
||||
todoList.add(todoRow);
|
||||
|
||||
todoField.addValueChangeListener(ev -> updateTodoItems(todoList, taskEntry));
|
||||
};
|
||||
|
||||
Button addTodoBtn = new Button("To-Do Punkt hinzufügen", new Icon(VaadinIcon.PLUS));
|
||||
addTodoBtn.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
|
||||
addTodoBtn.addClickListener(e -> addTodoItem.accept(null));
|
||||
|
||||
// Add initial todo item
|
||||
addTodoItem.accept(null);
|
||||
|
||||
todoContainer.add(todoList, addTodoBtn);
|
||||
configContainer.add(todoContainer);
|
||||
break;
|
||||
|
||||
case PHOTO:
|
||||
HorizontalLayout photoLayout = new HorizontalLayout();
|
||||
photoLayout.setWidthFull();
|
||||
photoLayout.setSpacing(true);
|
||||
|
||||
IntegerField minPhotos = new IntegerField("Min. Anzahl Fotos");
|
||||
minPhotos.setPlaceholder("1");
|
||||
minPhotos.setMin(1);
|
||||
minPhotos.setValue(taskEntry.getConfiguration().getMinPhotoCount() != null ?
|
||||
taskEntry.getConfiguration().getMinPhotoCount() : 1);
|
||||
|
||||
IntegerField maxPhotos = new IntegerField("Max. Anzahl Fotos");
|
||||
maxPhotos.setPlaceholder("10");
|
||||
maxPhotos.setMin(1);
|
||||
maxPhotos.setValue(taskEntry.getConfiguration().getMaxPhotoCount() != null ?
|
||||
taskEntry.getConfiguration().getMaxPhotoCount() : 10);
|
||||
|
||||
photoLayout.add(minPhotos, maxPhotos);
|
||||
|
||||
minPhotos.addValueChangeListener(ev -> {
|
||||
taskEntry.getConfiguration().setMinPhotoCount(ev.getValue());
|
||||
});
|
||||
|
||||
maxPhotos.addValueChangeListener(ev -> {
|
||||
taskEntry.getConfiguration().setMaxPhotoCount(ev.getValue());
|
||||
});
|
||||
|
||||
configContainer.add(photoLayout);
|
||||
break;
|
||||
|
||||
case BARCODE:
|
||||
HorizontalLayout barcodeLayout = new HorizontalLayout();
|
||||
barcodeLayout.setWidthFull();
|
||||
barcodeLayout.setSpacing(true);
|
||||
|
||||
IntegerField minBarcodes = new IntegerField("Min. Anzahl Barcodes");
|
||||
minBarcodes.setPlaceholder("1");
|
||||
minBarcodes.setMin(1);
|
||||
minBarcodes.setValue(taskEntry.getConfiguration().getMinBarcodeCount() != null ?
|
||||
taskEntry.getConfiguration().getMinBarcodeCount() : 1);
|
||||
|
||||
IntegerField maxBarcodes = new IntegerField("Max. Anzahl Barcodes");
|
||||
maxBarcodes.setPlaceholder("10");
|
||||
maxBarcodes.setMin(1);
|
||||
maxBarcodes.setValue(taskEntry.getConfiguration().getMaxBarcodeCount() != null ?
|
||||
taskEntry.getConfiguration().getMaxBarcodeCount() : 10);
|
||||
|
||||
barcodeLayout.add(minBarcodes, maxBarcodes);
|
||||
|
||||
minBarcodes.addValueChangeListener(ev -> {
|
||||
taskEntry.getConfiguration().setMinBarcodeCount(ev.getValue());
|
||||
});
|
||||
|
||||
maxBarcodes.addValueChangeListener(ev -> {
|
||||
taskEntry.getConfiguration().setMaxBarcodeCount(ev.getValue());
|
||||
});
|
||||
|
||||
configContainer.add(barcodeLayout);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateTodoItems(VerticalLayout todoList, TaskEntry taskEntry) {
|
||||
List<String> todoItems = todoList.getChildren()
|
||||
.map(component -> {
|
||||
if (component instanceof HorizontalLayout) {
|
||||
HorizontalLayout row = (HorizontalLayout) component;
|
||||
TextField field = (TextField) row.getChildren().findFirst().orElse(null);
|
||||
return field != null ? field.getValue() : null;
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
.filter(item -> !item.trim().isEmpty())
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
|
||||
taskEntry.getConfiguration().setTodoItems(todoItems);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user