feat: refresh job summary after task updates
- broadcast job updates after mobile task completions are persisted\n- rerender job_summary live for the affected job via UI access\n- show pickup and delivery tile detail lines like on add_job\n- highlight delivery tiles in light green when all station tasks are completed
This commit is contained in:
@@ -22,6 +22,7 @@ import de.assecutor.votianlt.model.Barcode;
|
||||
import de.assecutor.votianlt.model.Signature;
|
||||
import de.assecutor.votianlt.model.Comment;
|
||||
import de.assecutor.votianlt.service.JobHistoryService;
|
||||
import de.assecutor.votianlt.service.JobUpdateBroadcaster;
|
||||
import de.assecutor.votianlt.service.EmailService;
|
||||
import de.assecutor.votianlt.service.MessageService;
|
||||
import de.assecutor.votianlt.model.JobStatus;
|
||||
@@ -59,6 +60,7 @@ public class MessageController {
|
||||
private final SignatureRepository signatureRepository;
|
||||
private final CommentRepository commentRepository;
|
||||
private final JobHistoryService jobHistoryService;
|
||||
private final JobUpdateBroadcaster jobUpdateBroadcaster;
|
||||
private final EmailService emailService;
|
||||
private final MessageService messageService;
|
||||
|
||||
@@ -66,7 +68,8 @@ public class MessageController {
|
||||
AppUserService appUserService, JobRepository jobRepository, CargoItemRepository cargoItemRepository,
|
||||
TaskRepository taskRepository, PhotoRepository photoRepository, BarcodeRepository barcodeRepository,
|
||||
SignatureRepository signatureRepository, CommentRepository commentRepository,
|
||||
JobHistoryService jobHistoryService, EmailService emailService, MessageService messageService) {
|
||||
JobHistoryService jobHistoryService, JobUpdateBroadcaster jobUpdateBroadcaster, EmailService emailService,
|
||||
MessageService messageService) {
|
||||
this.messagingPublisher = messagingPublisher;
|
||||
this.appUserRepository = appUserRepository;
|
||||
this.appUserService = appUserService;
|
||||
@@ -78,6 +81,7 @@ public class MessageController {
|
||||
this.signatureRepository = signatureRepository;
|
||||
this.commentRepository = commentRepository;
|
||||
this.jobHistoryService = jobHistoryService;
|
||||
this.jobUpdateBroadcaster = jobUpdateBroadcaster;
|
||||
this.emailService = emailService;
|
||||
this.messageService = messageService;
|
||||
}
|
||||
@@ -345,10 +349,10 @@ public class MessageController {
|
||||
task.setCompleted(true);
|
||||
task.setCompletedAt(LocalDateTime.now());
|
||||
taskRepository.save(task);
|
||||
ObjectId jobId = new ObjectId(task.getJobIdAsString());
|
||||
|
||||
// Log detailed task completion in job history
|
||||
try {
|
||||
ObjectId jobId = new ObjectId(task.getJobIdAsString());
|
||||
String taskType = task.getTaskType() != null ? task.getTaskType().toString() : "Unknown";
|
||||
String taskDisplayName = task.getDisplayName() != null ? task.getDisplayName() : taskType;
|
||||
String completedBy = task.getCompletedBy() != null ? task.getCompletedBy() : "Unknown";
|
||||
@@ -360,7 +364,6 @@ public class MessageController {
|
||||
|
||||
// Send email notification for task completion
|
||||
try {
|
||||
ObjectId jobId = new ObjectId(task.getJobIdAsString());
|
||||
String taskType = task.getTaskType() != null ? task.getTaskType().toString() : "Unknown";
|
||||
String completedBy = task.getCompletedBy() != null ? task.getCompletedBy() : "Unknown";
|
||||
emailService.sendTaskCompletionNotification(jobId, taskType, taskIdStr, completedBy);
|
||||
@@ -368,6 +371,8 @@ public class MessageController {
|
||||
} catch (Exception e) {
|
||||
// Ignore email notification errors
|
||||
}
|
||||
|
||||
jobUpdateBroadcaster.broadcast(jobId);
|
||||
} catch (Exception ex) {
|
||||
log.error("[TASK] Completion error: {}", ex.getMessage());
|
||||
}
|
||||
|
||||
@@ -14,10 +14,14 @@ import com.vaadin.flow.component.notification.Notification;
|
||||
import com.vaadin.flow.component.notification.NotificationVariant;
|
||||
import com.vaadin.flow.component.icon.Icon;
|
||||
import com.vaadin.flow.component.icon.VaadinIcon;
|
||||
import com.vaadin.flow.component.AttachEvent;
|
||||
import com.vaadin.flow.component.DetachEvent;
|
||||
import com.vaadin.flow.component.UI;
|
||||
import com.vaadin.flow.router.BeforeEvent;
|
||||
import com.vaadin.flow.router.HasUrlParameter;
|
||||
import com.vaadin.flow.router.HasDynamicTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
import com.vaadin.flow.shared.Registration;
|
||||
import com.vaadin.flow.theme.lumo.LumoUtility;
|
||||
import de.assecutor.votianlt.model.CargoItem;
|
||||
import de.assecutor.votianlt.model.DeliveryStation;
|
||||
@@ -51,6 +55,7 @@ import de.assecutor.votianlt.model.Comment;
|
||||
import de.assecutor.votianlt.model.JobStatus;
|
||||
import de.assecutor.votianlt.pages.service.AppUserService;
|
||||
import de.assecutor.votianlt.service.JobHistoryService;
|
||||
import de.assecutor.votianlt.service.JobUpdateBroadcaster;
|
||||
import de.assecutor.votianlt.service.LocationService;
|
||||
import de.assecutor.votianlt.service.MessageService;
|
||||
import de.assecutor.votianlt.util.DateTimeFormatUtil;
|
||||
@@ -80,6 +85,7 @@ public class JobSummaryView extends Main implements HasUrlParameter<String>, Has
|
||||
private final CommentRepository commentRepository;
|
||||
private final AppUserService appUserService;
|
||||
private final JobHistoryService jobHistoryService;
|
||||
private final JobUpdateBroadcaster jobUpdateBroadcaster;
|
||||
private final LocationService locationService;
|
||||
private final ServiceRepository serviceRepository;
|
||||
|
||||
@@ -88,11 +94,14 @@ public class JobSummaryView extends Main implements HasUrlParameter<String>, Has
|
||||
|
||||
private final VerticalLayout content;
|
||||
private final List<Div> taskCards = new ArrayList<>();
|
||||
private Registration jobUpdateRegistration;
|
||||
private ObjectId currentJobId;
|
||||
|
||||
public JobSummaryView(JobRepository jobRepository, CargoItemRepository cargoItemRepository,
|
||||
TaskRepository taskRepository, SignatureRepository signatureRepository, BarcodeRepository barcodeRepository,
|
||||
PhotoRepository photoRepository, CommentRepository commentRepository, AppUserService appUserService,
|
||||
MessageService messageService, JobHistoryService jobHistoryService, LocationService locationService,
|
||||
MessageService messageService, JobHistoryService jobHistoryService,
|
||||
JobUpdateBroadcaster jobUpdateBroadcaster, LocationService locationService,
|
||||
ServiceRepository serviceRepository) {
|
||||
this.jobRepository = jobRepository;
|
||||
this.cargoItemRepository = cargoItemRepository;
|
||||
@@ -103,6 +112,7 @@ public class JobSummaryView extends Main implements HasUrlParameter<String>, Has
|
||||
this.commentRepository = commentRepository;
|
||||
this.appUserService = appUserService;
|
||||
this.jobHistoryService = jobHistoryService;
|
||||
this.jobUpdateBroadcaster = jobUpdateBroadcaster;
|
||||
this.locationService = locationService;
|
||||
this.serviceRepository = serviceRepository;
|
||||
|
||||
@@ -118,30 +128,62 @@ public class JobSummaryView extends Main implements HasUrlParameter<String>, Has
|
||||
|
||||
@Override
|
||||
public void setParameter(BeforeEvent event, String parameter) {
|
||||
content.removeAll();
|
||||
removeAll(); // Remove existing toolbar
|
||||
|
||||
if (parameter == null || parameter.isBlank()) {
|
||||
content.removeAll();
|
||||
removeAll();
|
||||
add(new ViewToolbar("Zusammenfassung"));
|
||||
content.add(new Span("Fehler: Keine Job-ID angegeben"));
|
||||
add(content);
|
||||
return;
|
||||
}
|
||||
|
||||
ObjectId jobId;
|
||||
try {
|
||||
jobId = new ObjectId(parameter);
|
||||
currentJobId = new ObjectId(parameter);
|
||||
} catch (Exception e) {
|
||||
content.removeAll();
|
||||
removeAll();
|
||||
add(new ViewToolbar("Zusammenfassung"));
|
||||
content.add(new Span("Fehler: Ungültige Job-ID Format: " + parameter));
|
||||
add(content);
|
||||
return;
|
||||
}
|
||||
|
||||
Job job = jobRepository.findById(jobId).orElse(null);
|
||||
refreshCurrentJobSummary();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttach(AttachEvent attachEvent) {
|
||||
super.onAttach(attachEvent);
|
||||
UI ui = attachEvent.getUI();
|
||||
jobUpdateRegistration = jobUpdateBroadcaster.register(jobId -> {
|
||||
if (currentJobId == null || jobId == null || !currentJobId.equals(jobId)) {
|
||||
return;
|
||||
}
|
||||
ui.access(this::refreshCurrentJobSummary);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetach(DetachEvent detachEvent) {
|
||||
if (jobUpdateRegistration != null) {
|
||||
jobUpdateRegistration.remove();
|
||||
jobUpdateRegistration = null;
|
||||
}
|
||||
super.onDetach(detachEvent);
|
||||
}
|
||||
|
||||
private void refreshCurrentJobSummary() {
|
||||
if (currentJobId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
content.removeAll();
|
||||
removeAll();
|
||||
|
||||
Job job = jobRepository.findById(currentJobId).orElse(null);
|
||||
if (job == null) {
|
||||
add(new ViewToolbar("Zusammenfassung"));
|
||||
content.add(new Span("Fehler: Job mit ID " + parameter + " nicht gefunden"));
|
||||
content.add(new Span("Fehler: Job mit ID " + currentJobId.toHexString() + " nicht gefunden"));
|
||||
add(content);
|
||||
return;
|
||||
}
|
||||
@@ -177,8 +219,8 @@ public class JobSummaryView extends Main implements HasUrlParameter<String>, Has
|
||||
// Add toolbar with both buttons in top right (Send Message button on the left)
|
||||
add(new ViewToolbar("Zusammenfassung", sendMessageButton, jobHistoryButton));
|
||||
|
||||
List<CargoItem> cargo = cargoItemRepository.findByJobId(jobId);
|
||||
List<BaseTask> tasks = taskRepository.findByJobIdOrderByTaskOrderAsc(jobId);
|
||||
List<CargoItem> cargo = cargoItemRepository.findByJobId(currentJobId);
|
||||
List<BaseTask> tasks = taskRepository.findByJobIdOrderByTaskOrderAsc(currentJobId);
|
||||
|
||||
render(job, cargo, tasks);
|
||||
add(content);
|
||||
@@ -187,7 +229,7 @@ public class JobSummaryView extends Main implements HasUrlParameter<String>, Has
|
||||
private void render(Job job, List<CargoItem> cargoItems, List<BaseTask> tasks) {
|
||||
content.removeAll();
|
||||
|
||||
content.add(createStationTilesSection(job, tasks));
|
||||
content.add(createStationTilesSection(job, cargoItems, tasks));
|
||||
|
||||
// Fracht und weitere Infos
|
||||
HorizontalLayout midRow = new HorizontalLayout();
|
||||
@@ -305,14 +347,14 @@ public class JobSummaryView extends Main implements HasUrlParameter<String>, Has
|
||||
return box;
|
||||
}
|
||||
|
||||
private Div createStationTilesSection(Job job, List<BaseTask> tasks) {
|
||||
private Div createStationTilesSection(Job job, List<CargoItem> cargoItems, List<BaseTask> tasks) {
|
||||
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.add(createPickupSummaryTile(job));
|
||||
stationGrid.add(createPickupSummaryTile(job, cargoItems));
|
||||
|
||||
List<DeliveryStation> stations = job.getDeliveryStations();
|
||||
if (stations != null && !stations.isEmpty()) {
|
||||
@@ -326,10 +368,10 @@ public class JobSummaryView extends Main implements HasUrlParameter<String>, Has
|
||||
return stationGrid;
|
||||
}
|
||||
|
||||
private StationTile createPickupSummaryTile(Job job) {
|
||||
private StationTile createPickupSummaryTile(Job job, List<CargoItem> cargoItems) {
|
||||
String title = getTranslation("jobsummary.section.pickup") + " "
|
||||
+ formatDateWithTime(job.getPickupDate(), job.getPickupTime());
|
||||
List<String> additionalLines = new ArrayList<>();
|
||||
List<String> additionalLines = buildPickupSummaryDetails(job, cargoItems);
|
||||
if (job.getPickupPhone() != null && !job.getPickupPhone().isBlank()) {
|
||||
additionalLines.add(getTranslation("jobsummary.station.phone") + ": " + job.getPickupPhone());
|
||||
}
|
||||
@@ -345,7 +387,8 @@ public class JobSummaryView extends Main implements HasUrlParameter<String>, Has
|
||||
String title = getTranslation("jobsummary.section.delivery") + " "
|
||||
+ (stationCount > 1 ? (index + 1) + " " : "")
|
||||
+ formatDateWithTime(station.getDeliveryDate(), station.getDeliveryTime());
|
||||
List<String> additionalLines = new ArrayList<>();
|
||||
List<BaseTask> stationTasks = getTasksForStation(station, tasks, false);
|
||||
List<String> additionalLines = buildDeliverySummaryDetails(stationTasks);
|
||||
if (station.getPhone() != null && !station.getPhone().isBlank()) {
|
||||
additionalLines.add(getTranslation("jobsummary.station.phone") + ": " + station.getPhone());
|
||||
}
|
||||
@@ -353,7 +396,7 @@ public class JobSummaryView extends Main implements HasUrlParameter<String>, Has
|
||||
StationTile tile = createSummaryTile(StationTile.StationType.DELIVERY, index + 1, title, station.getCompany(),
|
||||
buildDisplayName(station.getSalutation(), station.getFirstName(), station.getLastName()),
|
||||
station.getStreet(), station.getHouseNumber(), station.getZip(), station.getCity(), additionalLines);
|
||||
List<BaseTask> stationTasks = getTasksForStation(station, tasks, false);
|
||||
tile.setAddressValidated(areAllTasksCompleted(stationTasks));
|
||||
tile.setInteractive(true);
|
||||
tile.setClickListener(clickedTile -> showStationTasksDialog(title, stationTasks));
|
||||
return tile;
|
||||
@@ -362,7 +405,8 @@ public class JobSummaryView extends Main implements HasUrlParameter<String>, Has
|
||||
private StationTile createLegacyDeliverySummaryTile(Job job, List<BaseTask> tasks) {
|
||||
String title = getTranslation("jobsummary.section.delivery") + " "
|
||||
+ formatDateWithTime(job.getDeliveryDate(), job.getDeliveryTime());
|
||||
List<String> additionalLines = new ArrayList<>();
|
||||
List<BaseTask> stationTasks = getTasksForStation(null, tasks, true);
|
||||
List<String> additionalLines = buildDeliverySummaryDetails(stationTasks);
|
||||
if (job.getDeliveryPhone() != null && !job.getDeliveryPhone().isBlank()) {
|
||||
additionalLines.add(getTranslation("jobsummary.station.phone") + ": " + job.getDeliveryPhone());
|
||||
}
|
||||
@@ -371,7 +415,7 @@ public class JobSummaryView extends Main implements HasUrlParameter<String>, Has
|
||||
buildDisplayName(job.getDeliverySalutation(), job.getDeliveryFirstName(), job.getDeliveryLastName()),
|
||||
job.getDeliveryStreet(), job.getDeliveryHouseNumber(), job.getDeliveryZip(), job.getDeliveryCity(),
|
||||
additionalLines);
|
||||
List<BaseTask> stationTasks = getTasksForStation(null, tasks, true);
|
||||
tile.setAddressValidated(areAllTasksCompleted(stationTasks));
|
||||
tile.setInteractive(true);
|
||||
tile.setClickListener(clickedTile -> showStationTasksDialog(title, stationTasks));
|
||||
return tile;
|
||||
@@ -386,6 +430,161 @@ public class JobSummaryView extends Main implements HasUrlParameter<String>, Has
|
||||
return tile;
|
||||
}
|
||||
|
||||
private List<String> buildPickupSummaryDetails(Job job, List<CargoItem> cargoItems) {
|
||||
List<String> additionalLines = new ArrayList<>();
|
||||
additionalLines.add(getTranslation("addjob.tab.cargo") + ": " + summarizeCargoItems(cargoItems));
|
||||
|
||||
String pickupAppointment = formatPickupAppointment(job.getPickupDate(), job.getPickupTime());
|
||||
if (pickupAppointment != null) {
|
||||
additionalLines.add(getTranslation("addjob.appointment.pickup") + ": " + pickupAppointment);
|
||||
}
|
||||
|
||||
additionalLines.add(buildDigitalProcessingPreview(job.isDigitalProcessing(), job.getAppUser()));
|
||||
return additionalLines;
|
||||
}
|
||||
|
||||
private String summarizeCargoItems(List<CargoItem> cargoItems) {
|
||||
if (cargoItems == null || cargoItems.isEmpty()) {
|
||||
return getTranslation("jobsummary.cargo.none");
|
||||
}
|
||||
|
||||
List<String> summaries = new ArrayList<>();
|
||||
for (CargoItem cargoItem : cargoItems) {
|
||||
if (cargoItem == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String description = cargoItem.getDescription() != null ? cargoItem.getDescription().trim() : "";
|
||||
Integer quantity = cargoItem.getQuantity();
|
||||
if (description.isEmpty() && quantity == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
StringBuilder summary = new StringBuilder();
|
||||
if (quantity != null) {
|
||||
summary.append(quantity).append("x ");
|
||||
}
|
||||
summary.append(description.isEmpty() ? getTranslation("addjob.tab.cargo") : description);
|
||||
summaries.add(summary.toString().trim());
|
||||
}
|
||||
|
||||
if (summaries.isEmpty()) {
|
||||
return getTranslation("jobsummary.cargo.none");
|
||||
}
|
||||
if (summaries.size() <= 2) {
|
||||
return String.join(", ", summaries);
|
||||
}
|
||||
return String.join(", ", summaries.subList(0, 2)) + " +" + (summaries.size() - 2);
|
||||
}
|
||||
|
||||
private String formatPickupAppointment(java.time.LocalDate appointmentDate, java.time.LocalTime appointmentTime) {
|
||||
if (appointmentDate == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String formattedDate = formatLocalDate(appointmentDate);
|
||||
if (appointmentTime == null) {
|
||||
return formattedDate;
|
||||
}
|
||||
return formattedDate + " " + formatLocalTime(appointmentTime);
|
||||
}
|
||||
|
||||
private String buildDigitalProcessingPreview(boolean digitalProcessingEnabled, String appUserId) {
|
||||
StringBuilder preview = new StringBuilder();
|
||||
preview.append(getTranslation("profile.settings.digitalprocess")).append(": ")
|
||||
.append(getTranslation(digitalProcessingEnabled ? "common.yes" : "common.no"));
|
||||
|
||||
if (digitalProcessingEnabled && appUserId != null && !appUserId.isBlank()) {
|
||||
preview.append(" (").append(resolveAppUserName(appUserId)).append(")");
|
||||
}
|
||||
return preview.toString();
|
||||
}
|
||||
|
||||
private List<String> buildDeliverySummaryDetails(List<BaseTask> tasks) {
|
||||
if (tasks == null || tasks.isEmpty()) {
|
||||
return List.of(getTranslation("addjob.tab.tasks") + ": " + getTranslation("jobsummary.tasks.none"));
|
||||
}
|
||||
|
||||
List<String> summaries = new ArrayList<>();
|
||||
for (BaseTask task : tasks) {
|
||||
if (task != null) {
|
||||
summaries.add(summarizeDeliveryTask(task));
|
||||
}
|
||||
}
|
||||
|
||||
if (summaries.isEmpty()) {
|
||||
return List.of(getTranslation("addjob.tab.tasks") + ": " + getTranslation("jobsummary.tasks.none"));
|
||||
}
|
||||
return summaries;
|
||||
}
|
||||
|
||||
private String summarizeDeliveryTask(BaseTask task) {
|
||||
if (task instanceof ConfirmationTask confirmationTask) {
|
||||
String buttonText = trimToNull(confirmationTask.getButtonText());
|
||||
if (buttonText != null) {
|
||||
return confirmationTask.getDisplayName() + " \"" + buttonText + "\"";
|
||||
}
|
||||
|
||||
String description = trimToNull(confirmationTask.getDescription());
|
||||
return description != null ? confirmationTask.getDisplayName() + " \"" + description + "\""
|
||||
: confirmationTask.getDisplayName();
|
||||
}
|
||||
|
||||
if (task instanceof TodoListTask todoListTask) {
|
||||
long itemCount = todoListTask.getTodoItems() == null ? 0
|
||||
: todoListTask.getTodoItems().stream().filter(item -> item != null && !item.trim().isEmpty())
|
||||
.count();
|
||||
return itemCount > 0 ? task.getDisplayName() + " (" + itemCount + ")" : task.getDisplayName();
|
||||
}
|
||||
|
||||
if (task instanceof PhotoTask photoTask) {
|
||||
String range = formatMinMaxRange(photoTask.getMinPhotoCount(), photoTask.getMaxPhotoCount());
|
||||
return range.isBlank() ? task.getDisplayName() : task.getDisplayName() + " " + range;
|
||||
}
|
||||
|
||||
if (task instanceof BarcodeTask barcodeTask) {
|
||||
String range = formatMinMaxRange(barcodeTask.getMinBarcodeCount(), barcodeTask.getMaxBarcodeCount());
|
||||
return range.isBlank() ? task.getDisplayName() : task.getDisplayName() + " " + range;
|
||||
}
|
||||
|
||||
if (task instanceof CommentTask commentTask) {
|
||||
String commentText = trimToNull(commentTask.getCommentText());
|
||||
if (commentText != null) {
|
||||
return task.getDisplayName() + " \"" + commentText + "\"";
|
||||
}
|
||||
return commentTask.isRequired() ? task.getDisplayName() + " (" + getTranslation("common.required") + ")"
|
||||
: task.getDisplayName();
|
||||
}
|
||||
|
||||
if (task instanceof SignatureTask) {
|
||||
return task.getDisplayName();
|
||||
}
|
||||
|
||||
String description = trimToNull(task.getDescription());
|
||||
return description != null ? task.getDisplayName() + " \"" + description + "\"" : task.getDisplayName();
|
||||
}
|
||||
|
||||
private String formatMinMaxRange(Integer minValue, Integer maxValue) {
|
||||
if (minValue != null && maxValue != null) {
|
||||
return minValue.equals(maxValue) ? String.valueOf(minValue) : minValue + "-" + maxValue;
|
||||
}
|
||||
if (minValue != null) {
|
||||
return String.valueOf(minValue);
|
||||
}
|
||||
if (maxValue != null) {
|
||||
return String.valueOf(maxValue);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private String trimToNull(String value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
String trimmed = value.trim();
|
||||
return trimmed.isEmpty() ? null : trimmed;
|
||||
}
|
||||
|
||||
private String buildDisplayName(String salutation, String firstName, String lastName) {
|
||||
List<String> parts = new ArrayList<>();
|
||||
if (salutation != null && !salutation.isBlank()) {
|
||||
@@ -432,6 +631,10 @@ public class JobSummaryView extends Main implements HasUrlParameter<String>, Has
|
||||
return stationTasks;
|
||||
}
|
||||
|
||||
private boolean areAllTasksCompleted(List<BaseTask> tasks) {
|
||||
return tasks != null && !tasks.isEmpty() && tasks.stream().allMatch(task -> task != null && task.isCompleted());
|
||||
}
|
||||
|
||||
private void showStationTasksDialog(String stationTitle, List<BaseTask> tasks) {
|
||||
Dialog dialog = new Dialog();
|
||||
dialog.setWidth("720px");
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package de.assecutor.votianlt.service;
|
||||
|
||||
import com.vaadin.flow.shared.Registration;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.bson.types.ObjectId;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
public class JobUpdateBroadcaster {
|
||||
|
||||
private final Executor executor = Executors.newSingleThreadExecutor();
|
||||
private final LinkedHashSet<Consumer<ObjectId>> listeners = new LinkedHashSet<>();
|
||||
|
||||
public synchronized Registration register(Consumer<ObjectId> listener) {
|
||||
listeners.add(listener);
|
||||
return () -> {
|
||||
synchronized (JobUpdateBroadcaster.this) {
|
||||
listeners.remove(listener);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public synchronized void broadcast(ObjectId jobId) {
|
||||
if (jobId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (Consumer<ObjectId> listener : listeners) {
|
||||
executor.execute(() -> {
|
||||
try {
|
||||
listener.accept(jobId);
|
||||
} catch (Exception e) {
|
||||
log.error("Error broadcasting job update for {}", jobId.toHexString(), e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user