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(() => {
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); }