diff --git a/apps/ops/src/components/shared/RouteMap.vue b/apps/ops/src/components/shared/RouteMap.vue
index 1f324e8..efcaf3d 100644
--- a/apps/ops/src/components/shared/RouteMap.vue
+++ b/apps/ops/src/components/shared/RouteMap.vue
@@ -80,8 +80,11 @@ function renderMarkers (routes) {
homeMk.push({ mk, el: wrap, rid: r.id })
}
for (const s of (r.stops || [])) {
+ // Pastille = ROUND-RECT (pill) : icône du type de job + numéro d'ordre (best practice quand 2 infos ; cf. outils de tournée).
const wrap = document.createElement('div'); wrap.className = 'rm-mk'; wrap.dataset.rid = String(r.id)
- const disc = document.createElement('div'); disc.className = 'rm-disc'; disc.style.background = r.color; disc.textContent = String(s.seq)
+ const disc = document.createElement('div'); disc.className = 'rm-pill'; disc.style.background = r.color
+ const ic = /^[a-z0-9_]+$/.test(String(s.icon || '')) ? s.icon : 'place'
+ disc.innerHTML = '' + ic + '' + s.seq + ''
wrap.appendChild(disc)
const mk = new mapboxgl.Marker({ element: wrap, anchor: 'center' }).setLngLat([+s.lon, +s.lat]).addTo(map)
const rec = { mk, el: wrap, disc, rid: r.id, lngLat: [+s.lon, +s.lat], seq: s.seq, tech: r.name, subject: s.subject || '', stop: s, fan: null, members: null }
@@ -211,17 +214,18 @@ watch(() => props.routes, () => { draw() }, { deep: true })
diff --git a/apps/ops/src/pages/PlanificationPage.vue b/apps/ops/src/pages/PlanificationPage.vue
index 55283fa..3ab67ca 100644
--- a/apps/ops/src/pages/PlanificationPage.vue
+++ b/apps/ops/src/pages/PlanificationPage.vue
@@ -2362,6 +2362,16 @@ function skillIcon (sk) {
if (/factur|paiement|compta/.test(s)) return 'receipt_long'
return 'bolt'
}
+// Icône de compétence pour un MARQUEUR carte (DOM, police material-icons) : UNIQUEMENT des ligatures material (pas les SVG
+// custom de skillSym) — sinon le glyphe ne s'affiche pas dans un hors q-icon.
+function markerIcon (skill) {
+ const s = String(skill || '').toLowerCase()
+ if (/t[ée]l[ée]vis|\btv\b|iptv/.test(s)) return 'live_tv'
+ if (/install/.test(s)) return 'construction'
+ if (/monteur|poteau|hauteur|nacelle|a[ée]rien|grimp/.test(s)) return 'local_shipping'
+ const ic = skillIcon(skill)
+ return (typeof ic === 'string' && /^[a-z0-9_]+$/.test(ic)) ? ic : 'build'
+}
function onTagsChange (t, items) {
const newLabels = (items || []).map(x => typeof x === 'string' ? x : x.tag).filter(Boolean)
const removed = (t.skills || []).filter(s => !newLabels.includes(s)) // compétences retirées → vérifier l'impact sur les jobs assignés
@@ -4476,7 +4486,7 @@ const suggestRoutes = computed(() => {
if (e.lat == null || e.lon == null || !isFinite(+e.lat) || !isFinite(+e.lon) || Math.abs(+e.lat) < 0.01) continue // 0,0 / invalides exclus (sinon la carte dézoome jusqu'au golfe de Guinée)
const k = (+e.lat).toFixed(4) + ',' + (+e.lon).toFixed(4)
const ex = seen.get(k)
- if (ex) { ex.n++; ex.subject = ex.n + ' jobs — ' + ex.first } else { const s = { lat: +e.lat, lon: +e.lon, seq: stops.length + 1, subject: e.subject, first: e.subject, n: 1, name: e.jobName, entry: e }; seen.set(k, s); stops.push(s) }
+ if (ex) { ex.n++; ex.subject = ex.n + ' jobs — ' + ex.first } else { const s = { lat: +e.lat, lon: +e.lon, seq: stops.length + 1, subject: e.subject, first: e.subject, n: 1, name: e.jobName, entry: e, icon: markerIcon(e.skill) }; seen.set(k, s); stops.push(s) }
}
if (!stops.length) continue
const home = techOrigin(g.techId) // domicile, sinon bureau TARGO
@@ -4502,7 +4512,7 @@ const allDayRoutes = computed(() => { // TOUTES les tournées du jour (couleurs
if (!jobs.length) continue
jobs.sort((a, b) => (a.route_order || 9999) - (b.route_order || 9999) || ((a.start_h ?? 99) - (b.start_h ?? 99))) // ordre de tournée réel
const h = techOrigin(t.id) // domicile, sinon bureau TARGO
- out.push({ id: t.id, name: t.name, color: routeColor(ci++), home: h ? { lat: h.lat, lon: h.lon } : null, stops: jobs.map((j, i) => ({ lat: +j.lat, lon: +j.lon, seq: i + 1, subject: j.subject, name: j.name, job: j })) })
+ out.push({ id: t.id, name: t.name, color: routeColor(ci++), home: h ? { lat: h.lat, lon: h.lon } : null, stops: jobs.map((j, i) => ({ lat: +j.lat, lon: +j.lon, seq: i + 1, subject: j.subject, name: j.name, job: j, icon: markerIcon(j.skill) })) })
}
return out
})