Erweiterungen
This commit is contained in:
@@ -6,7 +6,7 @@ import de.assecutor.votianlt.dto.JobWithRelatedDataDTO;
|
||||
import de.assecutor.votianlt.model.AppUser;
|
||||
import de.assecutor.votianlt.model.CargoItem;
|
||||
import de.assecutor.votianlt.model.Job;
|
||||
import de.assecutor.votianlt.model.TaskEntry;
|
||||
import de.assecutor.votianlt.model.task.BaseTask;
|
||||
import de.assecutor.votianlt.pages.service.AppUserService;
|
||||
import de.assecutor.votianlt.repository.AppUserRepository;
|
||||
import de.assecutor.votianlt.repository.CargoItemRepository;
|
||||
@@ -140,7 +140,7 @@ public class MessageController {
|
||||
|
||||
if (request == null || request.getEmail() == null || request.getPassword() == null
|
||||
|| request.getEmail().isBlank() || request.getPassword().isBlank()) {
|
||||
AppLoginResponse response = new AppLoginResponse(false, "E-Mail und Passwort sind erforderlich", null);
|
||||
AppLoginResponse response = new AppLoginResponse(false, "E-Mail und Passwort sind erforderlich", null, null, null);
|
||||
log.info("STOMP Response for '/app/auth/login' sent to '/user/queue/auth': success={}, message='{}'",
|
||||
false, "E-Mail und Passwort sind erforderlich");
|
||||
return response;
|
||||
@@ -148,7 +148,7 @@ public class MessageController {
|
||||
|
||||
AppUser user = appUserRepository.findByEmail(request.getEmail());
|
||||
if (user == null) {
|
||||
AppLoginResponse response = new AppLoginResponse(false, "Benutzer nicht gefunden", null);
|
||||
AppLoginResponse response = new AppLoginResponse(false, "Benutzer nicht gefunden", null, null, null);
|
||||
log.info("STOMP Response for '/app/auth/login' sent to '/user/queue/auth': success={}, message='{}'",
|
||||
false, "Benutzer nicht gefunden");
|
||||
return response;
|
||||
@@ -156,13 +156,13 @@ public class MessageController {
|
||||
|
||||
boolean ok = appUserService.verifyPassword(request.getPassword(), user.getPassword());
|
||||
if (!ok) {
|
||||
AppLoginResponse response = new AppLoginResponse(false, "Ungültige Anmeldedaten", null);
|
||||
AppLoginResponse response = new AppLoginResponse(false, "Ungültige Anmeldedaten", null, null, null);
|
||||
log.info("STOMP Response for '/app/auth/login' sent to '/user/queue/auth': success={}, message='{}'",
|
||||
false, "Ungültige Anmeldedaten");
|
||||
return response;
|
||||
}
|
||||
|
||||
AppLoginResponse response = new AppLoginResponse(true, "Anmeldung erfolgreich", user.getIdAsString());
|
||||
AppLoginResponse response = new AppLoginResponse(true, "Anmeldung erfolgreich", null, null, user.getIdAsString());
|
||||
log.info("STOMP Response for '/app/auth/login' sent to '/user/queue/auth': success={}, message='{}', appUserId='{}'",
|
||||
true, "Anmeldung erfolgreich", response.getAppUserId());
|
||||
return response;
|
||||
@@ -192,11 +192,18 @@ public class MessageController {
|
||||
// Find jobs assigned to this app user
|
||||
List<Job> assignedJobs = jobRepository.findByAppUser(appUserId);
|
||||
|
||||
// For each job, fetch related cargo items and tasks
|
||||
// For each job, fetch related cargo items and tasks (ordered by task order)
|
||||
List<JobWithRelatedDataDTO> jobsWithRelatedData = assignedJobs.stream()
|
||||
.map(job -> {
|
||||
List<CargoItem> cargoItems = cargoItemRepository.findByJobId(job.getId());
|
||||
List<TaskEntry> tasks = taskRepository.findByJobId(job.getId());
|
||||
List<BaseTask> tasks = taskRepository.findByJobIdOrderByTaskOrderAsc(job.getId());
|
||||
|
||||
// Log task details for debugging
|
||||
tasks.forEach(task -> {
|
||||
log.info("Task details for job {}: type={}, order={}",
|
||||
job.getId(), task.getTaskType(), task.getTaskOrder());
|
||||
});
|
||||
|
||||
return new JobWithRelatedDataDTO(job, cargoItems, tasks);
|
||||
})
|
||||
.toList();
|
||||
@@ -233,13 +240,13 @@ public class MessageController {
|
||||
|
||||
try {
|
||||
org.bson.types.ObjectId taskId = new org.bson.types.ObjectId(taskIdStr);
|
||||
java.util.Optional<TaskEntry> opt = taskRepository.findById(taskId);
|
||||
java.util.Optional<BaseTask> opt = taskRepository.findById(taskId);
|
||||
if (opt.isEmpty()) {
|
||||
response.put("success", false);
|
||||
response.put("message", "Task nicht gefunden");
|
||||
return response;
|
||||
}
|
||||
TaskEntry task = opt.get();
|
||||
BaseTask task = opt.get();
|
||||
task.setCompleted(true);
|
||||
task.setCompletedAt(LocalDateTime.now());
|
||||
if (completedBy != null) task.setCompletedBy(completedBy);
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
package de.assecutor.votianlt.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class AppLoginRequest {
|
||||
private String email;
|
||||
private String password;
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,15 @@ package de.assecutor.votianlt.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class AppLoginResponse {
|
||||
private boolean success;
|
||||
private String message;
|
||||
private String appUserId; // MongoDB ObjectId as hex string
|
||||
}
|
||||
private String token;
|
||||
private String userId;
|
||||
private String appUserId;
|
||||
}
|
||||
@@ -2,7 +2,7 @@ package de.assecutor.votianlt.dto;
|
||||
|
||||
import de.assecutor.votianlt.model.CargoItem;
|
||||
import de.assecutor.votianlt.model.Job;
|
||||
import de.assecutor.votianlt.model.TaskEntry;
|
||||
import de.assecutor.votianlt.model.task.BaseTask;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
@@ -19,5 +19,5 @@ import java.util.List;
|
||||
public class JobWithRelatedDataDTO {
|
||||
private Job job;
|
||||
private List<CargoItem> cargoItems;
|
||||
private List<TaskEntry> tasks;
|
||||
private List<BaseTask> tasks;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package de.assecutor.votianlt.model.task;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.data.mongodb.core.mapping.Field;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class BarcodeTask extends BaseTask {
|
||||
|
||||
@Field("min_barcode_count")
|
||||
private Integer minBarcodeCount;
|
||||
|
||||
@Field("max_barcode_count")
|
||||
private Integer maxBarcodeCount;
|
||||
|
||||
public BarcodeTask(Integer minBarcodeCount, Integer maxBarcodeCount) {
|
||||
this.minBarcodeCount = minBarcodeCount;
|
||||
this.maxBarcodeCount = maxBarcodeCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTaskType() {
|
||||
return "BARCODE";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return "Barcode";
|
||||
}
|
||||
}
|
||||
80
src/main/java/de/assecutor/votianlt/model/task/BaseTask.java
Normal file
80
src/main/java/de/assecutor/votianlt/model/task/BaseTask.java
Normal file
@@ -0,0 +1,80 @@
|
||||
package de.assecutor.votianlt.model.task;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonGetter;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonSubTypes;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.bson.types.ObjectId;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.mongodb.core.mapping.Document;
|
||||
import org.springframework.data.mongodb.core.mapping.Field;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@Document(collection = "tasks")
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "taskType")
|
||||
@JsonSubTypes({
|
||||
@JsonSubTypes.Type(value = ConfirmationTask.class, name = "CONFIRMATION"),
|
||||
@JsonSubTypes.Type(value = SignatureTask.class, name = "SIGNATURE"),
|
||||
@JsonSubTypes.Type(value = TodoListTask.class, name = "TODOLIST"),
|
||||
@JsonSubTypes.Type(value = PhotoTask.class, name = "PHOTO"),
|
||||
@JsonSubTypes.Type(value = BarcodeTask.class, name = "BARCODE")
|
||||
})
|
||||
public abstract class BaseTask {
|
||||
@Id
|
||||
@JsonIgnore
|
||||
private ObjectId id;
|
||||
|
||||
@Field("job_id")
|
||||
@JsonIgnore
|
||||
private ObjectId jobId;
|
||||
|
||||
@Field("text")
|
||||
private String text;
|
||||
|
||||
@Field("task_order")
|
||||
private Integer taskOrder = 0;
|
||||
|
||||
@Field("completed")
|
||||
private boolean completed = false;
|
||||
|
||||
@Field("completed_at")
|
||||
private LocalDateTime completedAt;
|
||||
|
||||
@Field("completed_by")
|
||||
private String completedBy;
|
||||
|
||||
@Field("completion_note")
|
||||
private String completionNote;
|
||||
|
||||
/**
|
||||
* Returns the ObjectId as string for JSON serialization.
|
||||
*/
|
||||
@JsonGetter("id")
|
||||
public String getIdAsString() {
|
||||
return id != null ? id.toString() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the job ObjectId as string for JSON serialization.
|
||||
*/
|
||||
@JsonGetter("jobId")
|
||||
public String getJobIdAsString() {
|
||||
return jobId != null ? jobId.toString() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the task type as string for JSON serialization.
|
||||
*/
|
||||
@JsonGetter("taskType")
|
||||
public abstract String getTaskType();
|
||||
|
||||
/**
|
||||
* Returns the display name for this task type.
|
||||
*/
|
||||
public abstract String getDisplayName();
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package de.assecutor.votianlt.model.task;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.data.mongodb.core.mapping.Field;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ConfirmationTask extends BaseTask {
|
||||
|
||||
@Field("button_text")
|
||||
private String buttonText;
|
||||
|
||||
public ConfirmationTask(String buttonText) {
|
||||
this.buttonText = buttonText;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTaskType() {
|
||||
return "CONFIRMATION";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return "Bestätigung";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package de.assecutor.votianlt.model.task;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.data.mongodb.core.mapping.Field;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class PhotoTask extends BaseTask {
|
||||
|
||||
@Field("min_photo_count")
|
||||
private Integer minPhotoCount;
|
||||
|
||||
@Field("max_photo_count")
|
||||
private Integer maxPhotoCount;
|
||||
|
||||
public PhotoTask(Integer minPhotoCount, Integer maxPhotoCount) {
|
||||
this.minPhotoCount = minPhotoCount;
|
||||
this.maxPhotoCount = maxPhotoCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTaskType() {
|
||||
return "PHOTO";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return "Foto";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package de.assecutor.votianlt.model.task;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class SignatureTask extends BaseTask {
|
||||
|
||||
|
||||
@Override
|
||||
public String getTaskType() {
|
||||
return "SIGNATURE";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return "Unterschrift";
|
||||
}
|
||||
}
|
||||
19
src/main/java/de/assecutor/votianlt/model/task/TaskType.java
Normal file
19
src/main/java/de/assecutor/votianlt/model/task/TaskType.java
Normal file
@@ -0,0 +1,19 @@
|
||||
package de.assecutor.votianlt.model.task;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package de.assecutor.votianlt.model.task;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.data.mongodb.core.mapping.Field;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class TodoListTask extends BaseTask {
|
||||
|
||||
@Field("todo_items")
|
||||
private List<String> todoItems;
|
||||
|
||||
public TodoListTask(List<String> todoItems) {
|
||||
this.todoItems = todoItems;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTaskType() {
|
||||
return "TODOLIST";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return "To-Do Liste";
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ package de.assecutor.votianlt.pages.service;
|
||||
import de.assecutor.votianlt.model.CargoItem;
|
||||
import de.assecutor.votianlt.model.Job;
|
||||
import de.assecutor.votianlt.model.JobStatus;
|
||||
import de.assecutor.votianlt.model.TaskEntry;
|
||||
import de.assecutor.votianlt.model.task.BaseTask;
|
||||
import de.assecutor.votianlt.repository.JobRepository;
|
||||
import de.assecutor.votianlt.repository.TaskRepository;
|
||||
import de.assecutor.votianlt.security.SecurityService;
|
||||
@@ -33,7 +33,7 @@ public class AddJobService {
|
||||
* @param job der Auftrag
|
||||
* @param transientCargo zugehörige, noch nicht gespeicherte CargoItems aus der View
|
||||
*/
|
||||
public Job addJobWithCargo(Job job, List<CargoItem> transientCargo, List<TaskEntry> transientTasks) {
|
||||
public Job addJobWithCargo(Job job, List<CargoItem> transientCargo, List<BaseTask> transientTasks) {
|
||||
try {
|
||||
// Metadaten setzen
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
@@ -70,15 +70,27 @@ public class AddJobService {
|
||||
}).toList();
|
||||
cargoItemRepository.saveAll(itemsWithJob);
|
||||
modified = true;
|
||||
// Tasks separat speichern und referenzieren
|
||||
if (transientTasks != null && !transientTasks.isEmpty()) {
|
||||
var prepared = transientTasks.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.filter(te -> te.getText() != null && !te.getText().isBlank())
|
||||
.peek(te -> te.setJobId(jobId))
|
||||
.toList();
|
||||
taskRepository.saveAll(prepared);
|
||||
}
|
||||
|
||||
// Tasks separat speichern und referenzieren mit korrekter Nummerierung
|
||||
if (transientTasks != null && !transientTasks.isEmpty()) {
|
||||
var filteredTasks = transientTasks.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.filter(task -> task.getTaskType() != null) // Filter nach TaskType statt Text
|
||||
.toList();
|
||||
|
||||
// Setze JobId und stelle sicher, dass taskOrder korrekt ist
|
||||
for (int i = 0; i < filteredTasks.size(); i++) {
|
||||
BaseTask task = filteredTasks.get(i);
|
||||
task.setJobId(jobId);
|
||||
// Verwende die bereits gesetzte taskOrder oder setze sie auf den Index
|
||||
if (task.getTaskOrder() == null || task.getTaskOrder() != i) {
|
||||
task.setTaskOrder(i); // Stelle sicher, dass die Reihenfolge stimmt
|
||||
}
|
||||
}
|
||||
|
||||
taskRepository.saveAll(filteredTasks);
|
||||
log.info("Saved {} tasks for job {} with ordering", filteredTasks.size(), jobId);
|
||||
}
|
||||
|
||||
if (modified) {
|
||||
|
||||
@@ -28,7 +28,13 @@ import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility;
|
||||
import de.assecutor.votianlt.model.Job;
|
||||
import de.assecutor.votianlt.model.TaskEntry;
|
||||
import de.assecutor.votianlt.model.task.BaseTask;
|
||||
import de.assecutor.votianlt.model.task.TaskType;
|
||||
import de.assecutor.votianlt.model.task.ConfirmationTask;
|
||||
import de.assecutor.votianlt.model.task.SignatureTask;
|
||||
import de.assecutor.votianlt.model.task.TodoListTask;
|
||||
import de.assecutor.votianlt.model.task.PhotoTask;
|
||||
import de.assecutor.votianlt.model.task.BarcodeTask;
|
||||
import de.assecutor.votianlt.pages.service.AddJobService;
|
||||
import de.assecutor.votianlt.pages.service.CustomerService;
|
||||
import de.assecutor.votianlt.pages.base.ui.component.ViewToolbar;
|
||||
@@ -110,7 +116,7 @@ public class AddJobView extends Main {
|
||||
private final List<CargoItem> cargoItemsState = new ArrayList<>();
|
||||
// Stage sections for drag and drop
|
||||
// Backing list for tasks to mirror UI rows
|
||||
private final List<TaskEntry> tasksState = new ArrayList<>();
|
||||
private final List<BaseTask> tasksState = new ArrayList<>();
|
||||
// Dynamic lists and additional controls
|
||||
// Cargo section UI refs for error highlighting
|
||||
private VerticalLayout cargoAreaContainer;
|
||||
@@ -1464,9 +1470,9 @@ public class AddJobView extends Main {
|
||||
|
||||
|
||||
// Task type selection
|
||||
ComboBox<TaskEntry.TaskType> taskTypeCombo = new ComboBox<>("Aufgabentyp");
|
||||
taskTypeCombo.setItems(TaskEntry.TaskType.values());
|
||||
taskTypeCombo.setItemLabelGenerator(TaskEntry.TaskType::getDisplayName);
|
||||
ComboBox<TaskType> taskTypeCombo = new ComboBox<>("Aufgabentyp");
|
||||
taskTypeCombo.setItems(TaskType.values());
|
||||
taskTypeCombo.setItemLabelGenerator(TaskType::getDisplayName);
|
||||
taskTypeCombo.setPlaceholder("Aufgabentyp wählen...");
|
||||
taskTypeCombo.setWidthFull();
|
||||
|
||||
@@ -1487,55 +1493,87 @@ public class AddJobView extends Main {
|
||||
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);
|
||||
if (idx >= 0 && idx < tasksState.size()) {
|
||||
tasksState.remove(idx);
|
||||
// Reorder remaining tasks to maintain correct sequence
|
||||
reorderTasksAfterDeletion();
|
||||
}
|
||||
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);
|
||||
// Create TaskEntry and add to state with correct order
|
||||
BaseTask task = new ConfirmationTask("");
|
||||
task.setTaskOrder(tasksState.size()); // Set order based on current position
|
||||
tasksState.add(task);
|
||||
|
||||
taskTypeCombo.addValueChangeListener(ev -> {
|
||||
TaskEntry.TaskType selectedType = ev.getValue();
|
||||
TaskType selectedType = ev.getValue();
|
||||
if (selectedType != null) {
|
||||
taskEntry.setTaskType(selectedType);
|
||||
updateTaskConfiguration(configContainer, taskEntry);
|
||||
// Create new task instance based on type
|
||||
BaseTask newTask = createTaskByType(selectedType);
|
||||
newTask.setText(task.getText());
|
||||
newTask.setCompleted(task.isCompleted());
|
||||
newTask.setCompletedAt(task.getCompletedAt());
|
||||
newTask.setCompletedBy(task.getCompletedBy());
|
||||
newTask.setCompletionNote(task.getCompletionNote());
|
||||
|
||||
// Replace in state and preserve order
|
||||
int index = tasksState.indexOf(task);
|
||||
if (index >= 0) {
|
||||
newTask.setTaskOrder(index); // Preserve the order
|
||||
tasksState.set(index, newTask);
|
||||
}
|
||||
|
||||
updateTaskConfiguration(configContainer, newTask);
|
||||
}
|
||||
});
|
||||
|
||||
// Set initial configuration
|
||||
taskTypeCombo.setValue(TaskEntry.TaskType.CONFIRMATION);
|
||||
updateTaskConfiguration(configContainer, taskEntry);
|
||||
taskTypeCombo.setValue(TaskType.CONFIRMATION);
|
||||
updateTaskConfiguration(configContainer, task);
|
||||
|
||||
tasksList.add(taskContainer);
|
||||
}
|
||||
|
||||
private void updateTaskConfiguration(VerticalLayout configContainer, TaskEntry taskEntry) {
|
||||
private BaseTask createTaskByType(TaskType taskType) {
|
||||
return switch (taskType) {
|
||||
case CONFIRMATION -> new ConfirmationTask("");
|
||||
case SIGNATURE -> new SignatureTask();
|
||||
case TODOLIST -> new TodoListTask(new ArrayList<>());
|
||||
case PHOTO -> new PhotoTask(1, 10);
|
||||
case BARCODE -> new BarcodeTask(1, 10);
|
||||
};
|
||||
}
|
||||
|
||||
private void reorderTasksAfterDeletion() {
|
||||
// Reorder all tasks in tasksState to maintain correct sequence
|
||||
for (int i = 0; i < tasksState.size(); i++) {
|
||||
BaseTask task = tasksState.get(i);
|
||||
if (task != null) {
|
||||
task.setTaskOrder(i); // Reset order to match current position
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateTaskConfiguration(VerticalLayout configContainer, BaseTask task) {
|
||||
configContainer.removeAll();
|
||||
|
||||
TaskEntry.TaskType taskType = taskEntry.getTaskType();
|
||||
TaskType taskType = TaskType.valueOf(task.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() : "");
|
||||
ConfirmationTask confirmationTask = (ConfirmationTask) task;
|
||||
buttonTextField.setValue(confirmationTask.getButtonText() != null ?
|
||||
confirmationTask.getButtonText() : "");
|
||||
buttonTextField.addValueChangeListener(ev -> {
|
||||
taskEntry.getConfiguration().setButtonText(ev.getValue());
|
||||
confirmationTask.setButtonText(ev.getValue());
|
||||
});
|
||||
configContainer.add(buttonTextField);
|
||||
break;
|
||||
@@ -1575,14 +1613,14 @@ public class AddJobView extends Main {
|
||||
removeTodo.addThemeVariants(ButtonVariant.LUMO_ERROR, ButtonVariant.LUMO_TERTIARY);
|
||||
removeTodo.addClickListener(e -> {
|
||||
todoList.remove(todoRow);
|
||||
updateTodoItems(todoList, taskEntry);
|
||||
updateTodoItems(todoList, task);
|
||||
});
|
||||
|
||||
todoRow.add(todoField, removeTodo);
|
||||
todoRow.setFlexGrow(1, todoField);
|
||||
todoList.add(todoRow);
|
||||
|
||||
todoField.addValueChangeListener(ev -> updateTodoItems(todoList, taskEntry));
|
||||
todoField.addValueChangeListener(ev -> updateTodoItems(todoList, task));
|
||||
};
|
||||
|
||||
Button addTodoBtn = new Button("To-Do Punkt hinzufügen", new Icon(VaadinIcon.PLUS));
|
||||
@@ -1601,26 +1639,27 @@ public class AddJobView extends Main {
|
||||
photoLayout.setWidthFull();
|
||||
photoLayout.setSpacing(true);
|
||||
|
||||
PhotoTask photoTask = (PhotoTask) task;
|
||||
IntegerField minPhotos = new IntegerField("Min. Anzahl Fotos");
|
||||
minPhotos.setPlaceholder("1");
|
||||
minPhotos.setMin(1);
|
||||
minPhotos.setValue(taskEntry.getConfiguration().getMinPhotoCount() != null ?
|
||||
taskEntry.getConfiguration().getMinPhotoCount() : 1);
|
||||
minPhotos.setValue(photoTask.getMinPhotoCount() != null ?
|
||||
photoTask.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);
|
||||
maxPhotos.setValue(photoTask.getMaxPhotoCount() != null ?
|
||||
photoTask.getMaxPhotoCount() : 10);
|
||||
|
||||
photoLayout.add(minPhotos, maxPhotos);
|
||||
|
||||
minPhotos.addValueChangeListener(ev -> {
|
||||
taskEntry.getConfiguration().setMinPhotoCount(ev.getValue());
|
||||
photoTask.setMinPhotoCount(ev.getValue());
|
||||
});
|
||||
|
||||
maxPhotos.addValueChangeListener(ev -> {
|
||||
taskEntry.getConfiguration().setMaxPhotoCount(ev.getValue());
|
||||
photoTask.setMaxPhotoCount(ev.getValue());
|
||||
});
|
||||
|
||||
configContainer.add(photoLayout);
|
||||
@@ -1631,26 +1670,27 @@ public class AddJobView extends Main {
|
||||
barcodeLayout.setWidthFull();
|
||||
barcodeLayout.setSpacing(true);
|
||||
|
||||
BarcodeTask barcodeTask = (BarcodeTask) task;
|
||||
IntegerField minBarcodes = new IntegerField("Min. Anzahl Barcodes");
|
||||
minBarcodes.setPlaceholder("1");
|
||||
minBarcodes.setMin(1);
|
||||
minBarcodes.setValue(taskEntry.getConfiguration().getMinBarcodeCount() != null ?
|
||||
taskEntry.getConfiguration().getMinBarcodeCount() : 1);
|
||||
minBarcodes.setValue(barcodeTask.getMinBarcodeCount() != null ?
|
||||
barcodeTask.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);
|
||||
maxBarcodes.setValue(barcodeTask.getMaxBarcodeCount() != null ?
|
||||
barcodeTask.getMaxBarcodeCount() : 10);
|
||||
|
||||
barcodeLayout.add(minBarcodes, maxBarcodes);
|
||||
|
||||
minBarcodes.addValueChangeListener(ev -> {
|
||||
taskEntry.getConfiguration().setMinBarcodeCount(ev.getValue());
|
||||
barcodeTask.setMinBarcodeCount(ev.getValue());
|
||||
});
|
||||
|
||||
maxBarcodes.addValueChangeListener(ev -> {
|
||||
taskEntry.getConfiguration().setMaxBarcodeCount(ev.getValue());
|
||||
barcodeTask.setMaxBarcodeCount(ev.getValue());
|
||||
});
|
||||
|
||||
configContainer.add(barcodeLayout);
|
||||
@@ -1658,7 +1698,7 @@ public class AddJobView extends Main {
|
||||
}
|
||||
}
|
||||
|
||||
private void updateTodoItems(VerticalLayout todoList, TaskEntry taskEntry) {
|
||||
private void updateTodoItems(VerticalLayout todoList, BaseTask task) {
|
||||
List<String> todoItems = todoList.getChildren()
|
||||
.map(component -> {
|
||||
if (component instanceof HorizontalLayout) {
|
||||
@@ -1672,7 +1712,9 @@ public class AddJobView extends Main {
|
||||
.filter(item -> !item.trim().isEmpty())
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
|
||||
taskEntry.getConfiguration().setTodoItems(todoItems);
|
||||
if (task instanceof TodoListTask) {
|
||||
((TodoListTask) task).setTodoItems(todoItems);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,6 +6,12 @@ import com.vaadin.flow.component.html.Span;
|
||||
import com.vaadin.flow.component.html.Div;
|
||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
||||
import com.vaadin.flow.component.dialog.Dialog;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.button.ButtonVariant;
|
||||
import com.vaadin.flow.component.html.H4;
|
||||
import com.vaadin.flow.component.icon.Icon;
|
||||
import com.vaadin.flow.component.icon.VaadinIcon;
|
||||
import com.vaadin.flow.router.BeforeEvent;
|
||||
import com.vaadin.flow.router.HasUrlParameter;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
@@ -13,7 +19,11 @@ import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility;
|
||||
import de.assecutor.votianlt.model.CargoItem;
|
||||
import de.assecutor.votianlt.model.Job;
|
||||
import de.assecutor.votianlt.model.TaskEntry;
|
||||
import de.assecutor.votianlt.model.task.BaseTask;
|
||||
import de.assecutor.votianlt.model.task.TodoListTask;
|
||||
import de.assecutor.votianlt.model.task.PhotoTask;
|
||||
import de.assecutor.votianlt.model.task.SignatureTask;
|
||||
import de.assecutor.votianlt.model.task.ConfirmationTask;
|
||||
import de.assecutor.votianlt.model.AppUser;
|
||||
import de.assecutor.votianlt.pages.base.ui.component.ViewToolbar;
|
||||
import de.assecutor.votianlt.repository.CargoItemRepository;
|
||||
@@ -23,6 +33,7 @@ import de.assecutor.votianlt.pages.service.AppUserService;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import org.bson.types.ObjectId;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
@@ -38,6 +49,7 @@ public class JobSummaryView extends Main implements HasUrlParameter<String> {
|
||||
private final AppUserService appUserService;
|
||||
|
||||
private final VerticalLayout content;
|
||||
private final List<Div> taskCards = new ArrayList<>();
|
||||
|
||||
public JobSummaryView(JobRepository jobRepository,
|
||||
CargoItemRepository cargoItemRepository,
|
||||
@@ -70,12 +82,12 @@ public class JobSummaryView extends Main implements HasUrlParameter<String> {
|
||||
Job job = jobRepository.findById(jobId).orElse(null);
|
||||
if (job == null) return;
|
||||
List<CargoItem> cargo = cargoItemRepository.findByJobId(jobId);
|
||||
List<TaskEntry> tasks = taskRepository.findByJobId(jobId);
|
||||
List<BaseTask> tasks = taskRepository.findByJobIdOrderByTaskOrderAsc(jobId);
|
||||
|
||||
render(job, cargo, tasks);
|
||||
}
|
||||
|
||||
private void render(Job job, List<CargoItem> cargoItems, List<TaskEntry> tasks) {
|
||||
private void render(Job job, List<CargoItem> cargoItems, List<BaseTask> tasks) {
|
||||
content.removeAll();
|
||||
|
||||
// Kopfzeile: Abholung/Lieferung
|
||||
@@ -113,21 +125,28 @@ public class JobSummaryView extends Main implements HasUrlParameter<String> {
|
||||
// Aufgaben
|
||||
VerticalLayout tasksBox = borderedBox();
|
||||
tasksBox.add(new H3("Zu quittierende Aufgaben"));
|
||||
if (tasks == null || tasks.stream().filter(Objects::nonNull).map(TaskEntry::getText).filter(t -> t != null && !t.isBlank()).findAny().isEmpty()) {
|
||||
|
||||
// Ensure consistent spacing and width for task cards
|
||||
tasksBox.setSpacing(false);
|
||||
tasksBox.getStyle().set("gap", "var(--lumo-space-xs)");
|
||||
|
||||
// Clear previous task cards
|
||||
taskCards.clear();
|
||||
|
||||
if (tasks == null || tasks.isEmpty()) {
|
||||
tasksBox.add(new Span("Keine Aufgaben"));
|
||||
} else {
|
||||
tasks.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.forEach(task -> {
|
||||
String t = task.getText();
|
||||
if (t == null || t.isBlank()) return;
|
||||
Span s = new Span("• " + t);
|
||||
if (task.isCompleted()) {
|
||||
// Use Lumo success color for completed tasks
|
||||
s.getStyle().set("color", "var(--lumo-success-text-color)");
|
||||
}
|
||||
tasksBox.add(s);
|
||||
});
|
||||
for (BaseTask task : tasks) {
|
||||
if (task != null) {
|
||||
// Use getDisplayName() instead of getText() for task display
|
||||
String displayName = task.getDisplayName();
|
||||
if (displayName != null && !displayName.isBlank()) {
|
||||
Div taskCard = createTaskCard(task, displayName);
|
||||
taskCards.add(taskCard); // Keep reference for hover reset
|
||||
tasksBox.add(taskCard);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
content.add(tasksBox);
|
||||
|
||||
@@ -324,6 +343,253 @@ public class JobSummaryView extends Main implements HasUrlParameter<String> {
|
||||
if (s == null) return "";
|
||||
return s.replace("\\", "\\\\").replace("'", "\\'").replace("\n", " ").replace("\r", " ");
|
||||
}
|
||||
|
||||
private void showTaskDetailsDialog(BaseTask task) {
|
||||
Dialog dialog = new Dialog();
|
||||
dialog.setWidth("500px");
|
||||
dialog.setResizable(true);
|
||||
dialog.setDraggable(true);
|
||||
|
||||
// Reset all task card hover states when dialog closes
|
||||
dialog.addDialogCloseActionListener(e -> resetAllTaskCardHoverStates());
|
||||
|
||||
VerticalLayout dialogContent = new VerticalLayout();
|
||||
dialogContent.setPadding(true);
|
||||
dialogContent.setSpacing(true);
|
||||
|
||||
// Header
|
||||
H4 header = new H4("Aufgaben-Details");
|
||||
dialogContent.add(header);
|
||||
|
||||
// Task type and status
|
||||
Span typeSpan = new Span("Typ: " + task.getDisplayName());
|
||||
typeSpan.getStyle().set("font-weight", "bold");
|
||||
dialogContent.add(typeSpan);
|
||||
|
||||
Span statusSpan = new Span("Status: " + (task.isCompleted() ? "Abgeschlossen" : "Offen"));
|
||||
if (task.isCompleted()) {
|
||||
statusSpan.getStyle().set("color", "var(--lumo-success-text-color)");
|
||||
} else {
|
||||
statusSpan.getStyle().set("color", "var(--lumo-error-text-color)");
|
||||
}
|
||||
dialogContent.add(statusSpan);
|
||||
|
||||
// Task-specific details
|
||||
addTaskSpecificDetails(dialogContent, task);
|
||||
|
||||
// Completion details if completed
|
||||
if (task.isCompleted()) {
|
||||
dialogContent.add(new Span("")); // Spacer
|
||||
if (task.getCompletedAt() != null) {
|
||||
dialogContent.add(new Span("Abgeschlossen am: " + formatDateTime(task.getCompletedAt())));
|
||||
}
|
||||
if (task.getCompletedBy() != null && !task.getCompletedBy().isBlank()) {
|
||||
dialogContent.add(new Span("Abgeschlossen von: " + task.getCompletedBy()));
|
||||
}
|
||||
if (task.getCompletionNote() != null && !task.getCompletionNote().isBlank()) {
|
||||
dialogContent.add(new Span("Notiz: " + task.getCompletionNote()));
|
||||
}
|
||||
}
|
||||
|
||||
// Close button
|
||||
Button closeButton = new Button("Schließen", e -> {
|
||||
dialog.close();
|
||||
resetAllTaskCardHoverStates();
|
||||
});
|
||||
closeButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
||||
|
||||
HorizontalLayout buttonLayout = new HorizontalLayout(closeButton);
|
||||
buttonLayout.setJustifyContentMode(HorizontalLayout.JustifyContentMode.END);
|
||||
dialogContent.add(buttonLayout);
|
||||
|
||||
dialog.add(dialogContent);
|
||||
dialog.open();
|
||||
}
|
||||
|
||||
private void addTaskSpecificDetails(VerticalLayout content, BaseTask task) {
|
||||
if (task instanceof TodoListTask todoTask) {
|
||||
content.add(new Span("To-Do Items:"));
|
||||
if (todoTask.getTodoItems() != null && !todoTask.getTodoItems().isEmpty()) {
|
||||
for (String item : todoTask.getTodoItems()) {
|
||||
if (item != null && !item.isBlank()) {
|
||||
Span itemSpan = new Span(" • " + item);
|
||||
itemSpan.getStyle().set("margin-left", "20px");
|
||||
content.add(itemSpan);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
content.add(new Span(" Keine Items definiert"));
|
||||
}
|
||||
} else if (task instanceof PhotoTask photoTask) {
|
||||
if (photoTask.getMinPhotoCount() != null || photoTask.getMaxPhotoCount() != null) {
|
||||
String photoInfo = "Fotos: ";
|
||||
if (photoTask.getMinPhotoCount() != null && photoTask.getMaxPhotoCount() != null) {
|
||||
photoInfo += photoTask.getMinPhotoCount() + " - " + photoTask.getMaxPhotoCount() + " Fotos erforderlich";
|
||||
} else if (photoTask.getMinPhotoCount() != null) {
|
||||
photoInfo += "Mindestens " + photoTask.getMinPhotoCount() + " Fotos erforderlich";
|
||||
} else if (photoTask.getMaxPhotoCount() != null) {
|
||||
photoInfo += "Maximal " + photoTask.getMaxPhotoCount() + " Fotos erlaubt";
|
||||
}
|
||||
content.add(new Span(photoInfo));
|
||||
}
|
||||
} else if (task instanceof ConfirmationTask confirmationTask) {
|
||||
if (confirmationTask.getButtonText() != null && !confirmationTask.getButtonText().isBlank()) {
|
||||
content.add(new Span("Button-Text: " + confirmationTask.getButtonText()));
|
||||
}
|
||||
} else if (task instanceof SignatureTask) {
|
||||
content.add(new Span("Unterschrift erforderlich"));
|
||||
}
|
||||
}
|
||||
|
||||
private String formatDateTime(java.time.LocalDateTime dateTime) {
|
||||
try {
|
||||
java.time.format.DateTimeFormatter fmt = java.time.format.DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm").withLocale(Locale.GERMANY);
|
||||
return dateTime.format(fmt);
|
||||
} catch (Exception e) {
|
||||
return dateTime.toString();
|
||||
}
|
||||
}
|
||||
|
||||
private Div createTaskCard(BaseTask task, String displayName) {
|
||||
Div taskCard = new Div();
|
||||
|
||||
// Card styling with fixed width
|
||||
taskCard.getStyle()
|
||||
.set("border", "1px solid var(--lumo-contrast-20pct)")
|
||||
.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
|
||||
Icon taskIcon = getTaskIcon(task);
|
||||
taskIcon.getStyle().set("color", task.isCompleted() ? "var(--lumo-success-color)" : "var(--lumo-primary-color)");
|
||||
|
||||
// Task content
|
||||
VerticalLayout taskContent = new VerticalLayout();
|
||||
taskContent.setPadding(false);
|
||||
taskContent.setSpacing(false);
|
||||
taskContent.getStyle().set("flex-grow", "1");
|
||||
|
||||
// Task name with order number (display as 1-based instead of 0-based)
|
||||
String taskNameWithOrder = (task.getTaskOrder() != null ? (task.getTaskOrder() + 1) + ". " : "") + displayName;
|
||||
Span taskName = new Span(taskNameWithOrder);
|
||||
taskName.getStyle()
|
||||
.set("font-weight", "500")
|
||||
.set("font-size", "var(--lumo-font-size-m)")
|
||||
.set("color", task.isCompleted() ? "var(--lumo-success-text-color)" : "var(--lumo-body-text-color)");
|
||||
|
||||
// Task status/description
|
||||
Span taskDescription = new Span(getTaskDescription(task));
|
||||
taskDescription.getStyle()
|
||||
.set("font-size", "var(--lumo-font-size-s)")
|
||||
.set("color", "var(--lumo-secondary-text-color)")
|
||||
.set("margin-top", "var(--lumo-space-xs)");
|
||||
|
||||
taskContent.add(taskName, taskDescription);
|
||||
|
||||
// Status indicator
|
||||
Div statusIndicator = new Div();
|
||||
statusIndicator.getStyle()
|
||||
.set("width", "8px")
|
||||
.set("height", "8px")
|
||||
.set("border-radius", "50%")
|
||||
.set("background-color", task.isCompleted() ? "var(--lumo-success-color)" : "var(--lumo-error-color)");
|
||||
|
||||
taskCard.add(taskIcon, taskContent, statusIndicator);
|
||||
|
||||
// Click handler with hover state reset
|
||||
taskCard.addClickListener(event -> {
|
||||
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;
|
||||
}
|
||||
|
||||
private Icon getTaskIcon(BaseTask task) {
|
||||
if (task instanceof TodoListTask) {
|
||||
return new Icon(VaadinIcon.LIST);
|
||||
} else if (task instanceof PhotoTask) {
|
||||
return new Icon(VaadinIcon.CAMERA);
|
||||
} else if (task instanceof SignatureTask) {
|
||||
return new Icon(VaadinIcon.EDIT);
|
||||
} else if (task instanceof ConfirmationTask) {
|
||||
return new Icon(VaadinIcon.CHECK_CIRCLE);
|
||||
} else {
|
||||
return new Icon(VaadinIcon.TASKS);
|
||||
}
|
||||
}
|
||||
|
||||
private String getTaskDescription(BaseTask task) {
|
||||
if (task.isCompleted()) {
|
||||
return "Abgeschlossen" + (task.getCompletedAt() != null ? " am " + formatLocalDate(task.getCompletedAt().toLocalDate()) : "");
|
||||
}
|
||||
|
||||
if (task instanceof TodoListTask todoTask) {
|
||||
int itemCount = todoTask.getTodoItems() != null ? todoTask.getTodoItems().size() : 0;
|
||||
return itemCount + " Aufgabe" + (itemCount != 1 ? "n" : "") + " zu erledigen";
|
||||
} else if (task instanceof PhotoTask photoTask) {
|
||||
if (photoTask.getMinPhotoCount() != null && photoTask.getMaxPhotoCount() != null) {
|
||||
return photoTask.getMinPhotoCount() + "-" + photoTask.getMaxPhotoCount() + " Fotos erforderlich";
|
||||
} else if (photoTask.getMinPhotoCount() != null) {
|
||||
return "Mind. " + photoTask.getMinPhotoCount() + " Foto" + (photoTask.getMinPhotoCount() != 1 ? "s" : "");
|
||||
} else if (photoTask.getMaxPhotoCount() != null) {
|
||||
return "Max. " + photoTask.getMaxPhotoCount() + " Foto" + (photoTask.getMaxPhotoCount() != 1 ? "s" : "");
|
||||
} else {
|
||||
return "Foto erforderlich";
|
||||
}
|
||||
} else if (task instanceof SignatureTask) {
|
||||
return "Unterschrift erforderlich";
|
||||
} else if (task instanceof ConfirmationTask confirmationTask) {
|
||||
if (confirmationTask.getButtonText() != null && !confirmationTask.getButtonText().isBlank()) {
|
||||
return "Bestätigung: " + confirmationTask.getButtonText();
|
||||
} else {
|
||||
return "Bestätigung erforderlich";
|
||||
}
|
||||
}
|
||||
|
||||
return "Aufgabe offen";
|
||||
}
|
||||
|
||||
private void resetAllTaskCardHoverStates() {
|
||||
// Reset hover state for all task cards
|
||||
for (Div taskCard : taskCards) {
|
||||
if (taskCard != null) {
|
||||
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)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
package de.assecutor.votianlt.repository;
|
||||
|
||||
import de.assecutor.votianlt.model.TaskEntry;
|
||||
import de.assecutor.votianlt.model.task.BaseTask;
|
||||
import org.bson.types.ObjectId;
|
||||
import org.springframework.data.mongodb.repository.MongoRepository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface TaskRepository extends MongoRepository<TaskEntry, ObjectId> {
|
||||
List<TaskEntry> findByJobId(ObjectId jobId);
|
||||
public interface TaskRepository extends MongoRepository<BaseTask, ObjectId> {
|
||||
List<BaseTask> findByJobIdOrderByTaskOrderAsc(ObjectId jobId);
|
||||
|
||||
// Deprecated - use findByJobIdOrderByTaskOrderAsc instead
|
||||
@Deprecated
|
||||
default List<BaseTask> findByJobId(ObjectId jobId) {
|
||||
return findByJobIdOrderByTaskOrderAsc(jobId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import de.assecutor.votianlt.dto.JobWithRelatedDataDTO;
|
||||
import de.assecutor.votianlt.model.CargoItem;
|
||||
import de.assecutor.votianlt.model.Job;
|
||||
import de.assecutor.votianlt.model.TaskEntry;
|
||||
import org.bson.types.ObjectId;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Simple test to verify JSON serialization of ObjectIds as strings
|
||||
*/
|
||||
public class JsonSerializationTest {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
System.out.println("[DEBUG_LOG] Testing Job ID serialization...");
|
||||
|
||||
// Create test data
|
||||
Job job = new Job();
|
||||
job.setId(new ObjectId());
|
||||
job.setJobNumber("TEST-001");
|
||||
|
||||
CargoItem cargo = new CargoItem();
|
||||
cargo.setId(new ObjectId());
|
||||
cargo.setJobId(job.getId());
|
||||
cargo.setDescription("Test cargo");
|
||||
|
||||
TaskEntry task = new TaskEntry();
|
||||
task.setId(new ObjectId());
|
||||
task.setJobId(job.getId());
|
||||
task.setText("Test task");
|
||||
|
||||
JobWithRelatedDataDTO dto = new JobWithRelatedDataDTO(job, List.of(cargo), List.of(task));
|
||||
|
||||
// Serialize to JSON
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
String json = mapper.writeValueAsString(dto);
|
||||
|
||||
System.out.println("[DEBUG_LOG] Serialized JSON: " + json);
|
||||
|
||||
// Check if job ID is serialized as string
|
||||
if (json.contains("\"id\":\"" + job.getId().toString() + "\"")) {
|
||||
System.out.println("[DEBUG_LOG] ✓ Job ID correctly serialized as string");
|
||||
} else if (json.contains("\"id\":{")) {
|
||||
System.out.println("[DEBUG_LOG] ✗ Job ID serialized as ObjectId object");
|
||||
} else {
|
||||
System.out.println("[DEBUG_LOG] ? Job ID serialization format unclear");
|
||||
}
|
||||
|
||||
// Test individual Job serialization
|
||||
String jobJson = mapper.writeValueAsString(job);
|
||||
System.out.println("[DEBUG_LOG] Individual Job JSON: " + jobJson);
|
||||
|
||||
if (jobJson.contains("\"id\":\"" + job.getId().toString() + "\"")) {
|
||||
System.out.println("[DEBUG_LOG] ✓ Individual Job ID correctly serialized as string");
|
||||
} else {
|
||||
System.out.println("[DEBUG_LOG] ✗ Individual Job ID not serialized as string");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user