From 16343b61e199fde8f7b9c0eb42e95e788d06b9e2 Mon Sep 17 00:00:00 2001 From: louispaulb Date: Tue, 5 May 2026 14:17:33 -0400 Subject: [PATCH] =?UTF-8?q?fix(ops/dispatch):=20top=20bar=20polish=20?= =?UTF-8?q?=E2=80=94=20visible=20=E2=8B=AF=20menu,=20collapsed=20AI,=20fly?= =?UTF-8?q?-to=20tech,=20views=20dropdown?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Four fixes around the dispatch header following dispatcher feedback: 1. **⋯ overflow menu was invisible**: .sb-header had `overflow:hidden`, which clipped the absolutely-positioned dropdown right at the header's bottom edge. Switched the header to `overflow:visible` (children all have flex-shrink:0 + a flex:1 center, so the layout doesn't actually overflow horizontally). Bumped z-index to 5000 for safety on top of map/calendar layers. 2. **NLP/Assistant IA bar hidden by default**: was eagerly rendering on every page load, with the long French placeholder polluting the header below the toolbar. The user just wanted the icon. Now `nlpVisible` defaults to false, persisted in localStorage so power users who flip it on keep it open across sessions. Toggle still lives in the ⋯ menu. 3. **Click a tech in the resource list now flies the map to them**: selectTechOnBoard previously only opened the map panel. Now it also `map.flyTo({ center })` using `tech.gpsCoords ?? tech.coords` — live Traccar position wins when the tech is online; falls back to the saved home base. Animated, deferred a tick so map.resize() happens first, otherwise flyTo can land on garbage coords during the panel's open transition. 4. **Board view tabs collapsed into a "Vue principale ▾" dropdown**: was [Vue principale][Par région][+] inline. Now a single button showing the active view; click reveals the others + the future "+ Nouvelle vue" entry. Same dropdown component as the ⋯ menu (shared CSS, click-outside + ESC close). --- apps/ops/src/composables/useMap.js | 13 +++++++++++ apps/ops/src/pages/DispatchPage.vue | 31 +++++++++++++++++++++---- apps/ops/src/pages/dispatch-styles.scss | 19 +++++++++++---- 3 files changed, 54 insertions(+), 9 deletions(-) diff --git a/apps/ops/src/composables/useMap.js b/apps/ops/src/composables/useMap.js index 417d2b6..1bb8269 100644 --- a/apps/ops/src/composables/useMap.js +++ b/apps/ops/src/composables/useMap.js @@ -411,6 +411,19 @@ export function useMap (deps) { localStorage.setItem('sbv2-mapW', String(mapPanelW.value)) mapVisible.value = true } + // Live GPS wins over the ERPNext-saved home base, so the rep + // sees where the tech actually IS right now if Traccar reports + // them online. Fall back to home coords when offline. We + // flyTo (animated pan+zoom) so the dispatcher gets a clear + // visual cue, instead of a hard jump. + const pos = tech.gpsCoords || tech.coords + if (pos && map && Number.isFinite(pos[0]) && Number.isFinite(pos[1])) { + // Defer one tick so the map panel has time to be visible + // and `map.resize()` has run before the camera animation. + nextTick(() => { + try { map.flyTo({ center: pos, zoom: Math.max(map.getZoom(), 12), speed: 1.2, essential: true }) } catch (_e) {} + }) + } } if (map) { drawMapMarkers(); drawSelectedRoute() } } diff --git a/apps/ops/src/pages/DispatchPage.vue b/apps/ops/src/pages/DispatchPage.vue index 51dba03..eb5d709 100644 --- a/apps/ops/src/pages/DispatchPage.vue +++ b/apps/ops/src/pages/DispatchPage.vue @@ -372,7 +372,12 @@ const bookingOverlay = ref(null) const woModalOpen = ref(false) const woModalCtx = ref({}) const publishModalOpen = ref(false) -const nlpVisible = ref(true) // NLP bar always visible +// NLP bar is hidden by default; toggled from the ⋯ menu (Assistant IA). +// Showing it eagerly bloats the header on narrow laptops and the +// example placeholder text added visual noise. Persist the user's +// preference in localStorage so power users keep it open if they want. +const nlpVisible = ref(localStorage.getItem('sbv2-nlp-visible') === '1') +watch(nlpVisible, v => localStorage.setItem('sbv2-nlp-visible', v ? '1' : '0')) const draftCount = computed(() => store.jobs.filter(j => !j.published && j.status !== 'completed' && j.status !== 'cancelled').length) function onNlpAction (result) { @@ -397,6 +402,7 @@ const periodEndStr = computed(() => { }) const onPublished = jobNames => store.publishJobsLocal(jobNames) const moreMenuOpen = ref(false) // ⋯ dropdown in the top toolbar (right side) +const viewsMenuOpen = ref(false) // "Vue principale ▾" dropdown (left side) const gpsSettingsOpen = ref(false) const gpsShowInactive = ref(false) const gpsFilteredTechs = computed(() => @@ -961,6 +967,7 @@ function onKeyDown (e) { filterPanelOpen.value = false; projectsPanelOpen.value = false selectedJob.value = null; multiSelect.value = [] moreMenuOpen.value = false + viewsMenuOpen.value = false if (geoFixTech.value) cancelTechGeoFix() } if (e.key === 'z' && (e.metaKey || e.ctrlKey) && !e.shiftKey) { @@ -1174,7 +1181,7 @@ onMounted(async () => { loadOffers() loadPresets() document.addEventListener('keydown', onKeyDown) - document.addEventListener('click', () => { closeCtxMenu(); assistCtx.value = null; techCtx.value = null; moreMenuOpen.value = false }) + document.addEventListener('click', () => { closeCtxMenu(); assistCtx.value = null; techCtx.value = null; moreMenuOpen.value = false; viewsMenuOpen.value = false }) if (!document.getElementById('mapbox-css')) { const l = document.createElement('link'); l.id='mapbox-css'; l.rel='stylesheet' l.href='https://api.mapbox.com/mapbox-gl-js/v2.15.0/mapbox-gl.css'; document.head.appendChild(l) @@ -1225,9 +1232,23 @@ onUnmounted(() => { -
- - + +
+ +
+ +
+ +