refactor(ops/dispatch): single-color Lucide icons + tech-first resource filter
Two issues conflated in the same PR because they touch the same pixels: 1. **Resource filter no longer treats techs and materials as equals.** Was a 3-button inline toggle [Tous][👤 45][🔧 6] with all three visually similar — and the wrench glyph clashed with the wrench used for the filter-settings button. Now: • Default = 'human' (techs only). Materials are secondary resources; they don't deserve front-of-bar real estate. • Single chip [👥 45 ▾] in the toolbar. Click → dropdown: · Techs (45) ← active by default · Matériel (6) (only shown if materialCount > 0) · Tous (51) (only shown if materialCount > 0) • Defaults to localStorage 'sbv2-filterResType' if previously persisted, otherwise 'human' instead of '' (was ''). 2. **Mixed-style icons (emoji + Lucide SVG) replaced with consistent single-color Lucide-style strokes.** Each is a stroke-only inline <svg> with stroke="currentColor", so they inherit the surrounding text color (no green/red/yellow tinting). Added to the existing ICON set in useHelpers.js: user, users, package, sliders, chevDown, map, clipboard, sparkles, signal, rotateCw, alertTri, moreH, pause, play, externalLink, target, calendar Replaced in the dispatch top toolbar: ⚠️ → ICON.alertTri (overload alert) 📋 → ICON.clipboard (unscheduled jobs) 🗺 → ICON.map (map toggle) 🗓 → ICON.calendar (planning toggle) 👥 → ICON.users (team-jobs button + Ressources menu) 🔧 → ICON.sliders (filter-settings — was wrench, which collided with the materials filter) 👤/🔧 → ICON.users / .package (resource type dropdown) ↻ → ICON.rotateCw (refresh in ⋯ menu) ✨ → ICON.sparkles (AI in ⋯ menu) 📡 → ICON.signal (offers in ⋯ menu) ↗ → ICON.externalLink (ERPNext link in ⋯ menu) ⋯ → ICON.moreH (the ⋯ button itself) .sb-icon-svg gives them consistent sizing (14px in buttons, 15px in dropdown items, 16px in the ⋯ trigger) so they're crisp at all the spots they appear. Emojis still in place elsewhere (job-tile chips, status badges, etc.) will be migrated incrementally — out of scope for this pass which only targeted the user's visible header.
This commit is contained in:
parent
16343b61e1
commit
66b358d568
|
|
@ -212,6 +212,26 @@ export const ICON = {
|
||||||
clock: _s('<circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/>'),
|
clock: _s('<circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/>'),
|
||||||
loader: _s('<path d="M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83"/>'),
|
loader: _s('<path d="M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83"/>'),
|
||||||
truck: _s('<path d="M5 17h2l3-6h4l3 6h2M7 17a2 2 0 1 1-4 0M21 17a2 2 0 1 1-4 0"/>'),
|
truck: _s('<path d="M5 17h2l3-6h4l3 6h2M7 17a2 2 0 1 1-4 0M21 17a2 2 0 1 1-4 0"/>'),
|
||||||
|
// ── 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('<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/>'),
|
||||||
|
users: _s('<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/>'),
|
||||||
|
package: _s('<path d="m7.5 4.27 9 5.15"/><path d="M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z"/><path d="m3.3 7 8.7 5 8.7-5"/><path d="M12 22V12"/>'),
|
||||||
|
sliders: _s('<line x1="21" x2="14" y1="4" y2="4"/><line x1="10" x2="3" y1="4" y2="4"/><line x1="21" x2="12" y1="12" y2="12"/><line x1="8" x2="3" y1="12" y2="12"/><line x1="21" x2="16" y1="20" y2="20"/><line x1="12" x2="3" y1="20" y2="20"/><line x1="14" x2="14" y1="2" y2="6"/><line x1="8" x2="8" y1="10" y2="14"/><line x1="16" x2="16" y1="18" y2="22"/>'),
|
||||||
|
chevDown: _s('<path d="m6 9 6 6 6-6"/>'),
|
||||||
|
map: _s('<path d="M14.106 5.553a2 2 0 0 0 1.788 0l3.659-1.83A1 1 0 0 1 21 4.619v12.764a1 1 0 0 1-.553.894l-4.553 2.277a2 2 0 0 1-1.788 0l-4.212-2.106a2 2 0 0 0-1.788 0l-3.659 1.83A1 1 0 0 1 3 19.381V6.618a1 1 0 0 1 .553-.894l4.553-2.277a2 2 0 0 1 1.788 0z"/><path d="M15 5.764v15"/><path d="M9 3.236v15"/>'),
|
||||||
|
clipboard:_s('<rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/>'),
|
||||||
|
sparkles: _s('<path d="M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z"/><path d="M20 3v4"/><path d="M22 5h-4"/><path d="M4 17v2"/><path d="M5 18H3"/>'),
|
||||||
|
signal: _s('<path d="M2 20h.01"/><path d="M7 20v-4"/><path d="M12 20v-8"/><path d="M17 20V8"/><path d="M22 4v16"/>'),
|
||||||
|
rotateCw: _s('<path d="M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8"/><path d="M21 3v5h-5"/>'),
|
||||||
|
alertTri: _s('<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/><path d="M12 9v4"/><path d="M12 17h.01"/>'),
|
||||||
|
moreH: _s('<circle cx="12" cy="12" r="1"/><circle cx="19" cy="12" r="1"/><circle cx="5" cy="12" r="1"/>'),
|
||||||
|
pause: _s('<rect x="14" y="4" width="4" height="16" rx="1"/><rect x="6" y="4" width="4" height="16" rx="1"/>'),
|
||||||
|
play: _s('<polygon points="6 3 20 12 6 21 6 3"/>'),
|
||||||
|
externalLink: _s('<path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>'),
|
||||||
|
target: _s('<circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="6"/><circle cx="12" cy="12" r="2"/>'),
|
||||||
|
calendar: _s('<rect width="18" height="18" x="3" y="4" rx="2" ry="2"/><line x1="16" x2="16" y1="2" y2="6"/><line x1="8" x2="8" y1="2" y2="6"/><line x1="3" x2="21" y1="10" y2="10"/>'),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Job type icon based on service/subject
|
// Job type icon based on service/subject
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,10 @@ export function useResourceFilter (store, opts = {}) {
|
||||||
const filterStatus = ref(localStorage.getItem('sbv2-filterStatus') || '')
|
const filterStatus = ref(localStorage.getItem('sbv2-filterStatus') || '')
|
||||||
const filterGroup = ref(localStorage.getItem('sbv2-filterGroup') || '')
|
const filterGroup = ref(localStorage.getItem('sbv2-filterGroup') || '')
|
||||||
const filterTags = ref(JSON.parse(localStorage.getItem('sbv2-filterTags') || '[]'))
|
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 searchQuery = ref('')
|
||||||
const techSort = ref(localStorage.getItem('sbv2-techSort') || 'default')
|
const techSort = ref(localStorage.getItem('sbv2-techSort') || 'default')
|
||||||
const manualOrder = ref(JSON.parse(localStorage.getItem('sbv2-techOrder') || '[]'))
|
const manualOrder = ref(JSON.parse(localStorage.getItem('sbv2-techOrder') || '[]'))
|
||||||
|
|
|
||||||
|
|
@ -403,6 +403,22 @@ const periodEndStr = computed(() => {
|
||||||
const onPublished = jobNames => store.publishJobsLocal(jobNames)
|
const onPublished = jobNames => store.publishJobsLocal(jobNames)
|
||||||
const moreMenuOpen = ref(false) // ⋯ dropdown in the top toolbar (right side)
|
const moreMenuOpen = ref(false) // ⋯ dropdown in the top toolbar (right side)
|
||||||
const viewsMenuOpen = ref(false) // "Vue principale ▾" dropdown (left 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 gpsSettingsOpen = ref(false)
|
||||||
const gpsShowInactive = ref(false)
|
const gpsShowInactive = ref(false)
|
||||||
const gpsFilteredTechs = computed(() =>
|
const gpsFilteredTechs = computed(() =>
|
||||||
|
|
@ -968,6 +984,7 @@ function onKeyDown (e) {
|
||||||
selectedJob.value = null; multiSelect.value = []
|
selectedJob.value = null; multiSelect.value = []
|
||||||
moreMenuOpen.value = false
|
moreMenuOpen.value = false
|
||||||
viewsMenuOpen.value = false
|
viewsMenuOpen.value = false
|
||||||
|
resTypeMenuOpen.value = false
|
||||||
if (geoFixTech.value) cancelTechGeoFix()
|
if (geoFixTech.value) cancelTechGeoFix()
|
||||||
}
|
}
|
||||||
if (e.key === 'z' && (e.metaKey || e.ctrlKey) && !e.shiftKey) {
|
if (e.key === 'z' && (e.metaKey || e.ctrlKey) && !e.shiftKey) {
|
||||||
|
|
@ -1181,7 +1198,7 @@ onMounted(async () => {
|
||||||
loadOffers()
|
loadOffers()
|
||||||
loadPresets()
|
loadPresets()
|
||||||
document.addEventListener('keydown', onKeyDown)
|
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')) {
|
if (!document.getElementById('mapbox-css')) {
|
||||||
const l = document.createElement('link'); l.id='mapbox-css'; l.rel='stylesheet'
|
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)
|
l.href='https://api.mapbox.com/mapbox-gl-js/v2.15.0/mapbox-gl.css'; document.head.appendChild(l)
|
||||||
|
|
@ -1227,10 +1244,33 @@ onUnmounted(() => {
|
||||||
<span v-if="p.type === 'group'" class="sb-qp-icon">👥</span>{{ p.name }}
|
<span v-if="p.type === 'group'" class="sb-qp-icon">👥</span>{{ p.name }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="materialCount > 0" class="sb-res-type-toggle">
|
<!-- Resource type — single chip with Lucide icon + count + chevron.
|
||||||
<button :class="{ active: !filterResourceType }" @click="filterResourceType=''">Tous <span class="sbf-count">{{ humanCount + materialCount }}</span></button>
|
Default is 'human' (techs are the primary thing); materials
|
||||||
<button :class="{ active: filterResourceType==='human' }" @click="filterResourceType='human'">👤 <span class="sbf-count">{{ humanCount }}</span></button>
|
are accessed via the dropdown only when needed (and aren't
|
||||||
<button :class="{ active: filterResourceType==='material' }" @click="filterResourceType='material'">🔧 <span class="sbf-count">{{ materialCount }}</span></button>
|
even shown if the customer doesn't have any). -->
|
||||||
|
<div class="sb-menu-wrap">
|
||||||
|
<button class="sb-icon-btn sb-tab-current" :class="{ active: resTypeMenuOpen }"
|
||||||
|
@click.stop="resTypeMenuOpen = !resTypeMenuOpen"
|
||||||
|
:title="resTypeLabel">
|
||||||
|
<span class="sb-icon-svg" v-html="resTypeIcon"></span>
|
||||||
|
<span class="sbf-count">{{ resTypeCount }}</span>
|
||||||
|
<span class="sb-tab-chev" v-html="ICON.chevDown"></span>
|
||||||
|
</button>
|
||||||
|
<div v-if="resTypeMenuOpen" class="sb-menu-dropdown sb-menu-dropdown-left" @click.stop>
|
||||||
|
<button class="sb-menu-item" :class="{ active: filterResourceType === 'human' }" @click="filterResourceType='human'; resTypeMenuOpen=false">
|
||||||
|
<span class="sb-icon-svg" v-html="ICON.users"></span> Techs
|
||||||
|
<span class="sbs-count" style="margin-left:auto">{{ humanCount }}</span>
|
||||||
|
</button>
|
||||||
|
<button v-if="materialCount > 0" class="sb-menu-item" :class="{ active: filterResourceType === 'material' }" @click="filterResourceType='material'; resTypeMenuOpen=false">
|
||||||
|
<span class="sb-icon-svg" v-html="ICON.package"></span> Matériel
|
||||||
|
<span class="sbs-count" style="margin-left:auto">{{ materialCount }}</span>
|
||||||
|
</button>
|
||||||
|
<div v-if="materialCount > 0" class="sb-menu-sep"></div>
|
||||||
|
<button v-if="materialCount > 0" class="sb-menu-item" :class="{ active: !filterResourceType }" @click="filterResourceType=''; resTypeMenuOpen=false">
|
||||||
|
Tous
|
||||||
|
<span class="sbs-count" style="margin-left:auto">{{ humanCount + materialCount }}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Board view selector — a single dropdown instead of all tabs
|
<!-- Board view selector — a single dropdown instead of all tabs
|
||||||
inline. Saves header width on narrow laptops; the chevron
|
inline. Saves header width on narrow laptops; the chevron
|
||||||
|
|
@ -1250,12 +1290,16 @@ onUnmounted(() => {
|
||||||
<button class="sb-menu-item" disabled title="Bientôt"><span class="sb-menu-icon">+</span> Nouvelle vue</button>
|
<button class="sb-menu-item" disabled title="Bientôt"><span class="sb-menu-icon">+</span> Nouvelle vue</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button class="sb-icon-btn" :class="{ active: filterPanelOpen }" @click="filterPanelOpen=!filterPanelOpen" title="Filtres & Ressources">
|
<button class="sb-icon-btn" :class="{ active: filterPanelOpen }" @click="filterPanelOpen=!filterPanelOpen" title="Filtres & Ressources">
|
||||||
<span v-html="ICON.wrench"></span>
|
<!-- Settings/filter icon (sliders, not wrench) — `wrench` is now
|
||||||
|
reserved for the materials filter to avoid the visual clash
|
||||||
|
where two distinct concerns shared the same wrench glyph. -->
|
||||||
|
<span class="sb-icon-svg" v-html="ICON.sliders"></span>
|
||||||
<span v-if="filterStatus||filterGroup||selectedResIds.length||filterTags.length||hideAbsent" class="sbs-badge" style="position:relative;top:-4px;right:2px"></span>
|
<span v-if="filterStatus||filterGroup||selectedResIds.length||filterTags.length||hideAbsent" class="sbs-badge" style="position:relative;top:-4px;right:2px"></span>
|
||||||
</button>
|
</button>
|
||||||
<button v-if="teamJobs.length" class="sb-icon-btn" :class="{ active: projectsPanelOpen }" @click="projectsPanelOpen=!projectsPanelOpen" title="Projets">
|
<button v-if="teamJobs.length" class="sb-icon-btn" :class="{ active: projectsPanelOpen }" @click="projectsPanelOpen=!projectsPanelOpen" title="Projets">
|
||||||
👥 <span class="sbs-count" style="position:relative;top:-2px;right:auto">{{ teamJobs.length }}</span>
|
<span class="sb-icon-svg" v-html="ICON.users"></span>
|
||||||
|
<span class="sbs-count" style="position:relative;top:-2px;right:auto">{{ teamJobs.length }}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="sb-header-center">
|
<div class="sb-header-center">
|
||||||
|
|
@ -1267,7 +1311,8 @@ onUnmounted(() => {
|
||||||
<button v-for="v in [['day','Jour'],['week','Semaine'],['month','Mois']]" :key="v[0]" :class="{ active: currentView===v[0] }" @click="currentView=v[0]">{{ v[1] }}</button>
|
<button v-for="v in [['day','Jour'],['week','Semaine'],['month','Mois']]" :key="v[0]" :class="{ active: currentView===v[0] }" @click="currentView=v[0]">{{ v[1] }}</button>
|
||||||
</div>
|
</div>
|
||||||
<button class="sb-icon-btn sb-planning-toggle" :class="{ active: planningMode }" @click="planningMode=!planningMode" title="Mode planification — afficher les disponibilités">
|
<button class="sb-icon-btn sb-planning-toggle" :class="{ active: planningMode }" @click="planningMode=!planningMode" title="Mode planification — afficher les disponibilités">
|
||||||
🗓 <span v-if="currentView!=='month'" style="font-size:0.72rem">Planning</span>
|
<span class="sb-icon-svg" v-html="ICON.calendar"></span>
|
||||||
|
<span v-if="currentView!=='month'" style="font-size:0.72rem">Planning</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="sb-header-right">
|
<div class="sb-header-right">
|
||||||
|
|
@ -1276,12 +1321,17 @@ onUnmounted(() => {
|
||||||
(badges, surcharge) ou qui sont des CTA principaux (Publier, + WO).
|
(badges, surcharge) ou qui sont des CTA principaux (Publier, + WO).
|
||||||
Le reste descend dans le menu ⋯ pour libérer la largeur. -->
|
Le reste descend dans le menu ⋯ pour libérer la largeur. -->
|
||||||
<span v-if="overloadedTechs.length" class="sb-overload-alert" :title="overloadedTechs.map(o => o.tech.fullName + ' ' + o.pct + '%').join(', ')">
|
<span v-if="overloadedTechs.length" class="sb-overload-alert" :title="overloadedTechs.map(o => o.tech.fullName + ' ' + o.pct + '%').join(', ')">
|
||||||
⚠️ {{ overloadedTechs.length }} surchargé{{ overloadedTechs.length > 1 ? 's' : '' }}
|
<span class="sb-icon-svg" v-html="ICON.alertTri"></span>
|
||||||
|
{{ overloadedTechs.length }} surchargé{{ overloadedTechs.length > 1 ? 's' : '' }}
|
||||||
</span>
|
</span>
|
||||||
<button class="sb-icon-btn" :class="{ active: bottomPanelOpen }" @click="bottomPanelOpen=!bottomPanelOpen" title="Jobs non assignées">
|
<button class="sb-icon-btn" :class="{ active: bottomPanelOpen }" @click="bottomPanelOpen=!bottomPanelOpen" title="Jobs non assignées">
|
||||||
📋 <span v-if="unscheduledJobs.length" class="sbs-count" style="position:relative;top:-2px;right:auto">{{ unscheduledJobs.length }}</span>
|
<span class="sb-icon-svg" v-html="ICON.clipboard"></span>
|
||||||
|
<span v-if="unscheduledJobs.length" class="sbs-count" style="position:relative;top:-2px;right:auto">{{ unscheduledJobs.length }}</span>
|
||||||
|
</button>
|
||||||
|
<button v-if="currentView==='day'" class="sb-icon-btn" :class="{ active: mapVisible }" @click="mapVisible=!mapVisible" title="Carte">
|
||||||
|
<span class="sb-icon-svg" v-html="ICON.map"></span>
|
||||||
|
<span>Carte</span>
|
||||||
</button>
|
</button>
|
||||||
<button v-if="currentView==='day'" class="sb-icon-btn" :class="{ active: mapVisible }" @click="mapVisible=!mapVisible" title="Carte">🗺 Carte</button>
|
|
||||||
<button class="sb-wo-btn" style="background:#7c3aed" @click="publishModalOpen=true" title="Publier & envoyer l'horaire">
|
<button class="sb-wo-btn" style="background:#7c3aed" @click="publishModalOpen=true" title="Publier & envoyer l'horaire">
|
||||||
Publier <span v-if="draftCount" class="sbs-count" style="position:relative;top:-2px;right:auto;background:#ef4444">{{ draftCount }}</span>
|
Publier <span v-if="draftCount" class="sbs-count" style="position:relative;top:-2px;right:auto;background:#ef4444">{{ draftCount }}</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -1292,25 +1342,27 @@ onUnmounted(() => {
|
||||||
ressources/GPS, lien ERPNext. Tout ce qui n'est pas dans le hot
|
ressources/GPS, lien ERPNext. Tout ce qui n'est pas dans le hot
|
||||||
path du dispatcher au quotidien. -->
|
path du dispatcher au quotidien. -->
|
||||||
<div class="sb-menu-wrap">
|
<div class="sb-menu-wrap">
|
||||||
<button class="sb-icon-btn sb-menu-btn" :class="{ active: moreMenuOpen }" @click.stop="moreMenuOpen = !moreMenuOpen" title="Plus d'options">⋯</button>
|
<button class="sb-icon-btn sb-menu-btn" :class="{ active: moreMenuOpen }" @click.stop="moreMenuOpen = !moreMenuOpen" title="Plus d'options">
|
||||||
|
<span class="sb-icon-svg" v-html="ICON.moreH"></span>
|
||||||
|
</button>
|
||||||
<div v-if="moreMenuOpen" class="sb-menu-dropdown" @click.stop>
|
<div v-if="moreMenuOpen" class="sb-menu-dropdown" @click.stop>
|
||||||
<button class="sb-menu-item" @click="refreshData(); moreMenuOpen=false">
|
<button class="sb-menu-item" @click="refreshData(); moreMenuOpen=false">
|
||||||
<span class="sb-menu-icon">↻</span> Actualiser
|
<span class="sb-icon-svg" v-html="ICON.rotateCw"></span> Actualiser
|
||||||
</button>
|
</button>
|
||||||
<button class="sb-menu-item" :class="{ active: nlpVisible }" @click="nlpVisible=!nlpVisible; moreMenuOpen=false">
|
<button class="sb-menu-item" :class="{ active: nlpVisible }" @click="nlpVisible=!nlpVisible; moreMenuOpen=false">
|
||||||
<span class="sb-menu-icon">✨</span> Assistant IA
|
<span class="sb-icon-svg" v-html="ICON.sparkles"></span> Assistant IA
|
||||||
</button>
|
</button>
|
||||||
<button class="sb-menu-item" :class="{ active: showOfferPool }" @click="showOfferPool=!showOfferPool; if(showOfferPool) loadOffers(); moreMenuOpen=false">
|
<button class="sb-menu-item" :class="{ active: showOfferPool }" @click="showOfferPool=!showOfferPool; if(showOfferPool) loadOffers(); moreMenuOpen=false">
|
||||||
<span class="sb-menu-icon">📡</span> Offres aux techs
|
<span class="sb-icon-svg" v-html="ICON.signal"></span> Offres aux techs
|
||||||
<span v-if="activeOfferCount" class="sbs-count" style="margin-left:auto;background:#4ade80;color:#000">{{ activeOfferCount }}</span>
|
<span v-if="activeOfferCount" class="sbs-count" style="margin-left:auto;background:#4ade80;color:#000">{{ activeOfferCount }}</span>
|
||||||
</button>
|
</button>
|
||||||
<div class="sb-menu-sep"></div>
|
<div class="sb-menu-sep"></div>
|
||||||
<button class="sb-menu-item" @click="gpsSettingsOpen=true; moreMenuOpen=false">
|
<button class="sb-menu-item" @click="gpsSettingsOpen=true; moreMenuOpen=false">
|
||||||
<span class="sb-menu-icon">👥</span> Ressources & GPS
|
<span class="sb-icon-svg" v-html="ICON.users"></span> Ressources & GPS
|
||||||
</button>
|
</button>
|
||||||
<div class="sb-menu-sep"></div>
|
<div class="sb-menu-sep"></div>
|
||||||
<a class="sb-menu-item" :href="erpUrl + '/desk'" target="_blank" @click="moreMenuOpen=false">
|
<a class="sb-menu-item" :href="erpUrl + '/desk'" target="_blank" @click="moreMenuOpen=false">
|
||||||
<span class="sb-menu-icon">↗</span> Ouvrir ERPNext
|
<span class="sb-icon-svg" v-html="ICON.externalLink"></span> Ouvrir ERPNext
|
||||||
<span class="sb-erp-dot" :class="{ ok: store.erpStatus==='ok' }" style="margin-left:auto" :title="{ ok:'ERPNext ✓', error:'Hors ligne', loading:'Connexion…' }[store.erpStatus]||'ERPNext'"></span>
|
<span class="sb-erp-dot" :class="{ ok: store.erpStatus==='ok' }" style="margin-left:auto" :title="{ ok:'ERPNext ✓', error:'Hors ligne', loading:'Connexion…' }[store.erpStatus]||'ERPNext'"></span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,16 @@
|
||||||
.sb-menu-dropdown-left { right:auto; left:0; min-width:180px; }
|
.sb-menu-dropdown-left { right:auto; left:0; min-width:180px; }
|
||||||
/* Current-view button: shows the active tab name + a chevron. */
|
/* Current-view button: shows the active tab name + a chevron. */
|
||||||
.sb-tab-current { display:inline-flex; align-items:center; gap:6px; }
|
.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); } }
|
@keyframes sb-menu-fade { from { opacity:0; transform:translateY(-4px); } to { opacity:1; transform:translateY(0); } }
|
||||||
.sb-menu-item {
|
.sb-menu-item {
|
||||||
display:flex; align-items:center; gap:10px; width:100%;
|
display:flex; align-items:center; gap:10px; width:100%;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user