diff --git a/apps/ops/src/composables/useHelpers.js b/apps/ops/src/composables/useHelpers.js index 0f27225..e135a26 100644 --- a/apps/ops/src/composables/useHelpers.js +++ b/apps/ops/src/composables/useHelpers.js @@ -212,6 +212,26 @@ export const ICON = { clock: _s(''), loader: _s(''), truck: _s(''), + // ── Single-color toolbar set (used in dispatch top bar). All inherit + // currentColor; CSS sizes them via .sb-icon-svg below. Strokes only, + // 2px stroke for crisp rendering at 14-16px display size. + user: _s(''), + users: _s(''), + package: _s(''), + sliders: _s(''), + chevDown: _s(''), + map: _s(''), + clipboard:_s(''), + sparkles: _s(''), + signal: _s(''), + rotateCw: _s(''), + alertTri: _s(''), + moreH: _s(''), + pause: _s(''), + play: _s(''), + externalLink: _s(''), + target: _s(''), + calendar: _s(''), } // Job type icon based on service/subject diff --git a/apps/ops/src/composables/useResourceFilter.js b/apps/ops/src/composables/useResourceFilter.js index 637f114..817e030 100644 --- a/apps/ops/src/composables/useResourceFilter.js +++ b/apps/ops/src/composables/useResourceFilter.js @@ -5,7 +5,10 @@ export function useResourceFilter (store, opts = {}) { const filterStatus = ref(localStorage.getItem('sbv2-filterStatus') || '') const filterGroup = ref(localStorage.getItem('sbv2-filterGroup') || '') const filterTags = ref(JSON.parse(localStorage.getItem('sbv2-filterTags') || '[]')) - const filterResourceType = ref(localStorage.getItem('sbv2-filterResType') || '') // '' | 'human' | 'material' + // Default 'human' = show only techs. Materials are secondary and + // accessed via the resource-type dropdown when needed. Once the user + // explicitly picks something else (incl. ''), it's persisted. + const filterResourceType = ref(localStorage.getItem('sbv2-filterResType') ?? 'human') // '' | 'human' | 'material' const searchQuery = ref('') const techSort = ref(localStorage.getItem('sbv2-techSort') || 'default') const manualOrder = ref(JSON.parse(localStorage.getItem('sbv2-techOrder') || '[]')) diff --git a/apps/ops/src/pages/DispatchPage.vue b/apps/ops/src/pages/DispatchPage.vue index eb5d709..034a7c8 100644 --- a/apps/ops/src/pages/DispatchPage.vue +++ b/apps/ops/src/pages/DispatchPage.vue @@ -401,8 +401,24 @@ const periodEndStr = computed(() => { return localDateStr(d) }) 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 moreMenuOpen = ref(false) // ⋯ dropdown in the top toolbar (right side) +const viewsMenuOpen = ref(false) // "Vue principale ▾" dropdown (left side) +const resTypeMenuOpen = ref(false) // Resource type chip dropdown ([👤 45 ▾]) +const resTypeIcon = computed(() => + filterResourceType.value === 'material' ? ICON.package + : filterResourceType.value === 'human' ? ICON.users + : ICON.users // 'Tous' falls back to the people icon +) +const resTypeLabel = computed(() => + filterResourceType.value === 'material' ? 'Matériel' + : filterResourceType.value === 'human' ? 'Techs' + : 'Toutes les ressources' +) +const resTypeCount = computed(() => + filterResourceType.value === 'material' ? materialCount.value + : filterResourceType.value === 'human' ? humanCount.value + : (humanCount.value + materialCount.value) +) const gpsSettingsOpen = ref(false) const gpsShowInactive = ref(false) const gpsFilteredTechs = computed(() => @@ -968,6 +984,7 @@ function onKeyDown (e) { selectedJob.value = null; multiSelect.value = [] moreMenuOpen.value = false viewsMenuOpen.value = false + resTypeMenuOpen.value = false if (geoFixTech.value) cancelTechGeoFix() } if (e.key === 'z' && (e.metaKey || e.ctrlKey) && !e.shiftKey) { @@ -1181,7 +1198,7 @@ onMounted(async () => { loadOffers() loadPresets() document.addEventListener('keydown', onKeyDown) - document.addEventListener('click', () => { closeCtxMenu(); assistCtx.value = null; techCtx.value = null; moreMenuOpen.value = false; viewsMenuOpen.value = false }) + document.addEventListener('click', () => { closeCtxMenu(); assistCtx.value = null; techCtx.value = null; moreMenuOpen.value = false; viewsMenuOpen.value = false; resTypeMenuOpen.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) @@ -1227,10 +1244,33 @@ onUnmounted(() => { 👥{{ p.name }} -
- - - + +
+ +
+ + +
+ +
+
@@ -1267,7 +1311,8 @@ onUnmounted(() => {
@@ -1276,12 +1321,17 @@ onUnmounted(() => { (badges, surcharge) ou qui sont des CTA principaux (Publier, + WO). Le reste descend dans le menu ⋯ pour libérer la largeur. --> - ⚠️ {{ overloadedTechs.length }} surchargé{{ overloadedTechs.length > 1 ? 's' : '' }} + + {{ overloadedTechs.length }} surchargé{{ overloadedTechs.length > 1 ? 's' : '' }} + - @@ -1292,25 +1342,27 @@ onUnmounted(() => { ressources/GPS, lien ERPNext. Tout ce qui n'est pas dans le hot path du dispatcher au quotidien. -->
- +
- Ouvrir ERPNext + Ouvrir ERPNext
diff --git a/apps/ops/src/pages/dispatch-styles.scss b/apps/ops/src/pages/dispatch-styles.scss index bc77b9b..da8891e 100644 --- a/apps/ops/src/pages/dispatch-styles.scss +++ b/apps/ops/src/pages/dispatch-styles.scss @@ -105,7 +105,16 @@ .sb-menu-dropdown-left { right:auto; left:0; min-width:180px; } /* Current-view button: shows the active tab name + a chevron. */ .sb-tab-current { display:inline-flex; align-items:center; gap:6px; } -.sb-tab-chev { font-size:0.6rem; opacity:0.65; } +.sb-tab-chev { font-size:0.6rem; opacity:0.65; display:inline-flex; align-items:center; } +.sb-tab-chev svg { width:10px; height:10px; } +/* Single-color Lucide-style icon container — sizes the inline SVG and + keeps it baseline-aligned with neighboring text. Uses currentColor + on stroke so the icon inherits whatever color the parent applies. */ +.sb-icon-svg { display:inline-flex; align-items:center; line-height:0; } +.sb-icon-svg svg { width:14px; height:14px; flex-shrink:0; } +.sb-menu-item .sb-icon-svg svg { width:15px; height:15px; opacity:0.85; } +.sb-icon-btn .sb-icon-svg svg { width:13px; height:13px; } +.sb-menu-btn .sb-icon-svg svg { width:16px; height:16px; } @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%;