From 96a84c3e48cd5b83c0aa63b31fe80ff62b3f7874 Mon Sep 17 00:00:00 2001 From: louispaulb Date: Tue, 5 May 2026 14:07:04 -0400 Subject: [PATCH] =?UTF-8?q?refactor(ops/dispatch):=20consolidate=20top=20t?= =?UTF-8?q?oolbar=20with=20overflow=20=E2=8B=AF=20menu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The header right-side was getting noisy β€” 8 buttons + 2 indicators all competing for screen width, with two visually-similar πŸ“‘ icons (offer pool vs GPS settings) that confused dispatchers. On narrower laptops the bar wrapped or icons overflowed. New layout: [⚠ overload] [πŸ“‹ unassigned + count] [πŸ—Ί Carte] [Publier + count] [+ WO] [β‹―] Everything else dropped into the β‹― dropdown: β€’ ↻ Actualiser β€’ ✨ Assistant IA β€’ πŸ“‘ Offres aux techs (with green count badge) β€’ πŸ‘₯ Ressources & GPS ← was πŸ“‘ in the bar; this is also where the tech-management UI (rename, deactivate, home location, Traccar device link) lives β€’ β†— Ouvrir ERPNext (with the inline status dot) The β‹― menu closes on Escape, on click outside, and after picking an item. Same close-handler chain that already serves the job/tech context menus. The kept-up-front buttons all have either a status badge (counts, overload alert) or are the primary CTAs (Publier, + WO) β€” so the dispatcher's eye stays on workflow signal, not on chrome. --- apps/ops/src/pages/DispatchPage.vue | 47 +++++++++++++++++++------ apps/ops/src/pages/dispatch-styles.scss | 22 ++++++++++++ 2 files changed, 58 insertions(+), 11 deletions(-) diff --git a/apps/ops/src/pages/DispatchPage.vue b/apps/ops/src/pages/DispatchPage.vue index 1f3c4f9..51dba03 100644 --- a/apps/ops/src/pages/DispatchPage.vue +++ b/apps/ops/src/pages/DispatchPage.vue @@ -396,6 +396,7 @@ const periodEndStr = computed(() => { return localDateStr(d) }) const onPublished = jobNames => store.publishJobsLocal(jobNames) +const moreMenuOpen = ref(false) // β‹― dropdown in the top toolbar (right side) const gpsSettingsOpen = ref(false) const gpsShowInactive = ref(false) const gpsFilteredTechs = computed(() => @@ -959,6 +960,7 @@ function onKeyDown (e) { dispatchCriteriaModal.value = false; bookingOverlay.value = null filterPanelOpen.value = false; projectsPanelOpen.value = false selectedJob.value = null; multiSelect.value = [] + moreMenuOpen.value = false if (geoFixTech.value) cancelTechGeoFix() } if (e.key === 'z' && (e.metaKey || e.ctrlKey) && !e.shiftKey) { @@ -1172,7 +1174,7 @@ onMounted(async () => { loadOffers() loadPresets() document.addEventListener('keydown', onKeyDown) - document.addEventListener('click', () => { closeCtxMenu(); assistCtx.value = null; techCtx.value = null }) + document.addEventListener('click', () => { closeCtxMenu(); assistCtx.value = null; techCtx.value = null; moreMenuOpen.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) @@ -1248,27 +1250,50 @@ onUnmounted(() => {
- + ⚠️ {{ overloadedTechs.length }} surchargé{{ overloadedTechs.length > 1 ? 's' : '' }} - - - - - - ERP -
+ + +
+ +
+ + + +
+ +
+ + β†— Ouvrir ERPNext + + +
+
diff --git a/apps/ops/src/pages/dispatch-styles.scss b/apps/ops/src/pages/dispatch-styles.scss index 9fa4994..86d4668 100644 --- a/apps/ops/src/pages/dispatch-styles.scss +++ b/apps/ops/src/pages/dispatch-styles.scss @@ -85,6 +85,28 @@ .sb-logout-btn:hover { opacity:1; color:var(--sb-red); } .sb-erp-dot { width:7px; height:7px; border-radius:50%; background:var(--sb-red); transition:background 0.3s; } .sb-erp-dot.ok { background:var(--sb-green); } + +/* β‹― overflow menu in the top toolbar β€” secondary/admin actions live here. */ +.sb-menu-wrap { position:relative; display:inline-block; } +.sb-menu-btn { font-size:0.95rem; line-height:0.85rem; padding:0.05rem 0.5rem 0.18rem; } +.sb-menu-dropdown { + position:absolute; top:calc(100% + 4px); right:0; z-index:200; + background:var(--sb-card); border:1px solid var(--sb-border); + border-radius:6px; box-shadow:0 6px 22px rgba(0,0,0,0.35); + min-width:220px; padding:4px 0; animation:sb-menu-fade 0.12s ease-out; +} +@keyframes sb-menu-fade { from { opacity:0; transform:translateY(-4px); } to { opacity:1; transform:translateY(0); } } +.sb-menu-item { + display:flex; align-items:center; gap:10px; width:100%; + background:none; border:none; color:var(--sb-text); + font-size:0.78rem; font-weight:500; padding:0.45rem 0.85rem; + cursor:pointer; text-align:left; text-decoration:none; + white-space:nowrap; +} +.sb-menu-item:hover, .sb-menu-item.active { background:var(--sb-sidebar); color:var(--sb-text); } +.sb-menu-item.active { color:var(--sb-accent); font-weight:700; } +.sb-menu-icon { display:inline-block; width:18px; text-align:center; font-size:0.95rem; } +.sb-menu-sep { height:1px; background:var(--sb-border); margin:3px 0; } .sb-wo-btn { background:var(--sb-acc); border:none; border-radius:6px; color:var(--sb-text); font-size:0.7rem; font-weight:800; padding:0.22rem 0.65rem; cursor:pointer; white-space:nowrap; } .sb-wo-btn:hover { filter:brightness(1.15); }