From 8f57bb5ca4e2e19b48413c09ccb99834cf65e9d7 Mon Sep 17 00:00:00 2001 From: Sven Carstensen Date: Fri, 20 Feb 2026 16:23:16 +0100 Subject: [PATCH] Erweiterungen --- src/main/frontend/themes/default/styles.css | 4 ++ .../controller/MessageController.java | 6 +++ .../votianlt/messaging/MessagingConfig.java | 15 ++++++-- .../messaging/MessagingPublisher.java | 23 +++++++++--- .../votianlt/messaging/WebSocketService.java | 19 ++++++++-- .../pages/base/ui/view/MainLayout.java | 37 ++++++++++++++++--- .../service/ClientConnectionService.java | 5 ++- 7 files changed, 90 insertions(+), 19 deletions(-) diff --git a/src/main/frontend/themes/default/styles.css b/src/main/frontend/themes/default/styles.css index e69de29..0d00eef 100644 --- a/src/main/frontend/themes/default/styles.css +++ b/src/main/frontend/themes/default/styles.css @@ -0,0 +1,4 @@ +/* Breite des linken Menüs (Drawer) */ +vaadin-app-layout { + --vaadin-app-layout-drawer-width: 286px; +} \ No newline at end of file diff --git a/src/main/java/de/assecutor/votianlt/controller/MessageController.java b/src/main/java/de/assecutor/votianlt/controller/MessageController.java index 731f6e6..b27638a 100644 --- a/src/main/java/de/assecutor/votianlt/controller/MessageController.java +++ b/src/main/java/de/assecutor/votianlt/controller/MessageController.java @@ -114,10 +114,14 @@ public class MessageController { */ public void handleGetAssignedJobs(String appUserId) { if (appUserId == null || appUserId.isBlank()) { + log.warn("[JOBS] appUserId is null or blank, cannot retrieve jobs"); return; } + log.info("[JOBS] Retrieving assigned jobs for appUserId: {}", appUserId); + List assignedJobs = jobRepository.findByAppUser(appUserId); + log.info("[JOBS] Found {} jobs for appUserId: {}", assignedJobs.size(), appUserId); List jobsWithRelatedData = assignedJobs.stream().map(job -> { List cargoItems = cargoItemRepository.findByJobId(job.getId()); @@ -125,7 +129,9 @@ public class MessageController { return new JobWithRelatedDataDTO(job, cargoItems, tasks); }).toList(); + log.info("[JOBS] Publishing {} jobs to client {} on topic /client/jobs", jobsWithRelatedData.size(), appUserId); messagingPublisher.publishAsJson(appUserId, "jobs", jobsWithRelatedData); + log.info("[JOBS] Jobs published successfully for client {}", appUserId); } /** diff --git a/src/main/java/de/assecutor/votianlt/messaging/MessagingConfig.java b/src/main/java/de/assecutor/votianlt/messaging/MessagingConfig.java index 98f9bf6..451bb83 100644 --- a/src/main/java/de/assecutor/votianlt/messaging/MessagingConfig.java +++ b/src/main/java/de/assecutor/votianlt/messaging/MessagingConfig.java @@ -109,26 +109,35 @@ public class MessagingConfig { if (response.isSuccess()) { String appUserId = response.getAppUserId(); + log.info("[Messaging] Login successful for appUserId: {}", appUserId); + webSocketService.registerAuthenticatedSession(wsSessionId, appUserId); // Send success response to the now-authenticated session // locationTrackingEnabled: true = client should send position updates - Map authResponse = Map.of("success", true, "message", response.getMessage(), - "locationTrackingEnabled", true); + // appUserId: wird an den Client gesendet für Referenz + Map authResponse = Map.of( + "success", true, + "message", response.getMessage(), + "locationTrackingEnabled", true, + "appUserId", appUserId + ); byte[] responseBytes = objectMapper.writeValueAsBytes(authResponse); + log.info("[Messaging] Sending auth response to appUserId: {}", appUserId); webSocketService.sendToClient(appUserId, "auth", responseBytes); // Register client - pending messages and jobs will be sent after // client confirms buffer_flushed clientConnectionService.registerClient(appUserId); } else { + log.warn("[Messaging] Login failed: {}", response.getMessage()); // Send failure response to the pending session Map authResponse = Map.of("success", false, "message", response.getMessage()); byte[] responseBytes = objectMapper.writeValueAsBytes(authResponse); webSocketService.sendToSessionById(wsSessionId, "/client/auth", responseBytes); } } catch (Exception e) { - log.error("[Messaging] Login handling error: {}", e.getMessage()); + log.error("[Messaging] Login handling error: {}", e.getMessage(), e); } } diff --git a/src/main/java/de/assecutor/votianlt/messaging/MessagingPublisher.java b/src/main/java/de/assecutor/votianlt/messaging/MessagingPublisher.java index d9119d0..b2501f0 100644 --- a/src/main/java/de/assecutor/votianlt/messaging/MessagingPublisher.java +++ b/src/main/java/de/assecutor/votianlt/messaging/MessagingPublisher.java @@ -28,16 +28,29 @@ class MessagingPublisherImpl implements MessagingPublisher { @Override public void publishAsJson(String clientId, String messageType, Object payload) { try { + // Prüfen ob Client verbunden ist + boolean isConnected = webSocketService.isClientConnected(clientId); + log.debug("[Messaging] Publishing to {}/{} - connected: {}", clientId, messageType, isConnected); + + if (!isConnected) { + log.warn("[Messaging] Client {} is not connected, cannot send {}", clientId, messageType); + return; + } + String json = objectMapper.writeValueAsString(payload); byte[] data = json.getBytes(StandardCharsets.UTF_8); - webSocketService.sendToClient(clientId, messageType, data).exceptionally(ex -> { - log.error("[Messaging] Failed to deliver to {}/{}: {}", clientId, messageType, ex.getMessage()); - return null; - }); + webSocketService.sendToClient(clientId, messageType, data) + .thenRun(() -> { + log.debug("[Messaging] Successfully sent {}/{} to client {}", messageType, clientId); + }) + .exceptionally(ex -> { + log.error("[Messaging] Failed to deliver to {}/{}: {}", clientId, messageType, ex.getMessage(), ex); + return null; + }); } catch (Exception e) { - log.error("[Messaging] Failed to publish to {}/{}: {}", clientId, messageType, e.getMessage()); + log.error("[Messaging] Failed to publish to {}/{}: {}", clientId, messageType, e.getMessage(), e); } } } diff --git a/src/main/java/de/assecutor/votianlt/messaging/WebSocketService.java b/src/main/java/de/assecutor/votianlt/messaging/WebSocketService.java index 2ddd714..e325b81 100644 --- a/src/main/java/de/assecutor/votianlt/messaging/WebSocketService.java +++ b/src/main/java/de/assecutor/votianlt/messaging/WebSocketService.java @@ -129,9 +129,19 @@ public class WebSocketService extends TextWebSocketHandler { public CompletableFuture sendToClient(String clientId, String messageType, byte[] payload) { WebSocketSession session = clientSessions.get(clientId); - if (session == null || !session.isOpen()) { + if (session == null) { + log.warn("[WebSocket] No session found for client {}", clientId); return CompletableFuture - .failedFuture(new IOException("No active WebSocket session for client: " + clientId)); + .failedFuture(new IOException("No WebSocket session for client: " + clientId)); + } + + if (!session.isOpen()) { + log.warn("[WebSocket] Session for client {} is closed", clientId); + // Session aus der Map entfernen + clientSessions.remove(clientId); + sessionToClient.remove(session.getId()); + return CompletableFuture + .failedFuture(new IOException("WebSocket session closed for client: " + clientId)); } try { @@ -143,12 +153,13 @@ public class WebSocketService extends TextWebSocketHandler { wireMessage.set("payload", objectMapper.readTree(payloadJson)); String wireJson = objectMapper.writeValueAsString(wireMessage); - log.info("[WebSocket OUT] {} -> {}", topic, wireJson); + log.info("[WebSocket OUT] {} to client {} (session open: {})", topic, clientId, session.isOpen()); sendToSession(session, wireJson); + log.debug("[WebSocket] Message sent successfully to client {}", clientId); return CompletableFuture.completedFuture(null); } catch (Exception e) { - log.error("[WebSocket] Failed to send to client {}: {}", clientId, e.getMessage()); + log.error("[WebSocket] Failed to send to client {}: {}", clientId, e.getMessage(), e); return CompletableFuture.failedFuture(new IOException("Failed to send WebSocket message", e)); } } diff --git a/src/main/java/de/assecutor/votianlt/pages/base/ui/view/MainLayout.java b/src/main/java/de/assecutor/votianlt/pages/base/ui/view/MainLayout.java index b390258..6071ac6 100644 --- a/src/main/java/de/assecutor/votianlt/pages/base/ui/view/MainLayout.java +++ b/src/main/java/de/assecutor/votianlt/pages/base/ui/view/MainLayout.java @@ -30,7 +30,6 @@ import de.assecutor.votianlt.pages.service.UserInvoiceDataService; import de.assecutor.votianlt.pages.view.EditProfileView; import de.assecutor.votianlt.model.Language; import de.assecutor.votianlt.security.SecurityService; -import de.assecutor.votianlt.service.LanguageService; import de.assecutor.votianlt.service.MessageBadgeUpdateService; import de.assecutor.votianlt.service.MessageService; import lombok.extern.slf4j.Slf4j; @@ -51,7 +50,6 @@ public final class MainLayout extends AppLayout { private final MessageService messageService; private final MessageBadgeUpdateService messageBadgeUpdateService; private final AppUserService appUserService; - private final LanguageService languageService; private Div headerRef; private Scroller navRef; private Component userMenuRef; @@ -61,22 +59,29 @@ public final class MainLayout extends AppLayout { public MainLayout(SecurityService securityService, UserInvoiceDataService userInvoiceDataService, MessageService messageService, MessageBadgeUpdateService messageBadgeUpdateService, - AppUserService appUserService, LanguageService languageService) { + AppUserService appUserService) { this.securityService = securityService; this.userInvoiceDataService = userInvoiceDataService; this.messageService = messageService; this.messageBadgeUpdateService = messageBadgeUpdateService; this.appUserService = appUserService; - this.languageService = languageService; setPrimarySection(Section.DRAWER); + // Drawer Styles für volle Höhe + getStyle().set("--vaadin-app-layout-drawer-width", "286px"); + // Always build the drawer; keep references and toggle visibility on attach and // after navigation headerRef = createHeader(); - navRef = new Scroller(createSideNav()); + + // Scroller für Navigation mit maximaler Höhe + Component sideNav = createSideNav(); + navRef = new Scroller(sideNav); + userMenuRef = createUserMenu(); addToDrawer(headerRef, navRef, userMenuRef); + updateDrawerVisibility(); // Re-check on attach (new UI/session) and on every navigation cycle @@ -128,6 +133,8 @@ public final class MainLayout extends AppLayout { treeData.addItem(null, benutzerItem); // Add children to "Verwaltung" + treeData.addItem(verwaltungItem, + new MenuTreeItem(getTranslation("nav.jobs"), "jobs", VaadinIcon.CLIPBOARD_TEXT)); treeData.addItem(verwaltungItem, new MenuTreeItem(getTranslation("nav.customers"), "customers", VaadinIcon.USERS)); treeData.addItem(verwaltungItem, @@ -153,6 +160,7 @@ public final class MainLayout extends AppLayout { tree = new TreeGrid<>(); tree.setDataProvider(new TreeDataProvider<>(treeData)); tree.addClassNames(Margin.Horizontal.MEDIUM); + tree.setHeight("435px"); // Custom item renderer to show icon and label with badge tree.addComponentHierarchyColumn(item -> { @@ -350,6 +358,25 @@ public final class MainLayout extends AppLayout { super.onAttach(attachEvent); UI ui = attachEvent.getUI(); + // Drawer-Layout anpassen nach kurzer Verzögerung + ui.access(() -> { + getElement().executeJs( + "setTimeout(() => {" + + " const drawer = this.shadowRoot?.querySelector('[part=drawer]');" + + " if (drawer) {" + + " drawer.style.display = 'flex';" + + " drawer.style.flexDirection = 'column';" + + " drawer.style.height = '100vh';" + + " const scroller = drawer.querySelector('vaadin-scroller');" + + " if (scroller) {" + + " scroller.style.flex = '1 1 auto';" + + " scroller.style.minHeight = '0';" + + " }" + + " }" + + "}, 100);" + ); + }); + // Apply user's preferred language immediately after login applyUserLanguagePreference(); diff --git a/src/main/java/de/assecutor/votianlt/service/ClientConnectionService.java b/src/main/java/de/assecutor/votianlt/service/ClientConnectionService.java index 1fcb895..26068f3 100644 --- a/src/main/java/de/assecutor/votianlt/service/ClientConnectionService.java +++ b/src/main/java/de/assecutor/votianlt/service/ClientConnectionService.java @@ -102,10 +102,11 @@ public class ClientConnectionService { */ private void sendAssignedJobs(String appUserId) { try { + log.info("[CLIENT] Sending assigned jobs to appUserId: {}", appUserId); messageController.handleGetAssignedJobs(appUserId); - log.debug("[CLIENT] Sent assigned jobs to {}", appUserId); + log.info("[CLIENT] Assigned jobs sent successfully to {}", appUserId); } catch (Exception e) { - log.error("[CLIENT] Error sending assigned jobs to {}: {}", appUserId, e.getMessage()); + log.error("[CLIENT] Error sending assigned jobs to {}: {}", appUserId, e.getMessage(), e); } }