From 746a8de6df460e773b34dc4e212fa2d0919f9272 Mon Sep 17 00:00:00 2001 From: louispaulb Date: Tue, 7 Jul 2026 10:57:39 -0400 Subject: [PATCH] =?UTF-8?q?planif:=20fix=20figement=20carte=20des=20tourn?= =?UTF-8?q?=C3=A9es=20sugg=C3=A9r=C3=A9es=20(boucle=20de=20rendu)=20+=20tr?= =?UTF-8?q?ac=C3=A9s=20routiers=20r=C3=A9els?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cause : :routes="suggestRoutes.map(...)" inline créait un NOUVEAU tableau à chaque rendu → watch deep de RouteMap → draw → emit('metrics') → onRouteMetrics mutait realRoutes → re-rendu → nouveau tableau → BOUCLE INFINIE (onglet figé ; le remplacement asynchrone OSRM ne finissait jamais → lignes à vol d'oiseau). Fix : computed CACHÉ suggestMapRoutes (réf stable) + onRouteMetrics idempotent. La géométrie routière OSRM (vérifiée côté serveur : 200, ~405 pts) s'affiche maintenant. Co-Authored-By: Claude Opus 4.8 --- apps/ops/src/pages/PlanificationPage.vue | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/apps/ops/src/pages/PlanificationPage.vue b/apps/ops/src/pages/PlanificationPage.vue index 0dccaa3..fa779af 100644 --- a/apps/ops/src/pages/PlanificationPage.vue +++ b/apps/ops/src/pages/PlanificationPage.vue @@ -1141,7 +1141,7 @@
- +
{{ r.techName }} · {{ r.stops.length }} arrêt(s) · 🚗 {{ realRoutes[r.techId].km }} km / {{ realRoutes[r.techId].mins }} min · 🚗 ≈{{ r.km }} km Aucun job géolocalisé ce jour (utilise « Localiser » sur le pool pour les placer sur la carte). @@ -3452,7 +3452,15 @@ function refreshAssignMap () { function destroyAssignMap () { assignLasso.value = false; if (_assignMapRO) { try { _assignMapRO.disconnect() } catch (e) {} _assignMapRO = null } if (_assignMap) { try { _assignMap.remove() } catch (e) {} _assignMap = null } } // ── Grande carte des TOURNÉES proposées (revue Suggérer) : ligne domicile→arrêts par tech, 1 couleur/tech, 1 jour ── // Carte de la revue = composant partagé (P2). Les métriques réelles (OSRM) remontent ici pour la légende. -function onRouteMetrics (m) { for (const k in realRoutes) delete realRoutes[k]; Object.assign(realRoutes, m) } +function onRouteMetrics (m) { + // Idempotent : ne muter realRoutes QUE si les valeurs changent (sinon re-rendu inutile → risque de boucle avec RouteMap) + const keys = Object.keys(m || {}) + let same = keys.length === Object.keys(realRoutes).length + if (same) for (const k of keys) { const a = realRoutes[k], b = m[k]; if (!a || a.km !== b.km || a.mins !== b.mins) { same = false; break } } + if (same) return + for (const k in realRoutes) delete realRoutes[k] + Object.assign(realRoutes, m) +} // D v2 — routes RÉELLES : Mapbox Directions par tech (domicile→arrêts) → géométrie routière + distance/durée réelles. Cache par signature. const _routeCache = new Map() const realRoutes = reactive({}) // techId → { km, mins } (la légende préfère ces valeurs réelles quand présentes) @@ -5019,6 +5027,10 @@ const suggestRoutes = computed(() => { } return out }) +// RÉFÉRENCE STABLE pour — computed CACHÉ (ne change QUE si suggestRoutes change). +// AVANT : `:routes="suggestRoutes.map(...)"` inline créait un NOUVEAU tableau à CHAQUE rendu → le watch deep de RouteMap +// redessinait → emit('metrics') → onRouteMetrics muta realRoutes → re-rendu → nouveau tableau → BOUCLE INFINIE (figeait la carte). +const suggestMapRoutes = computed(() => suggestRoutes.value.map(r => ({ id: r.techId, name: r.techName, color: r.color, home: r.home, stops: r.stops }))) // Carte de la revue = (réactif sur suggestRoutes) — il ne reste qu'à défauter le jour affiché à l'ouverture de la vue carte. watch(() => suggestDlg.view, (v) => { if (v === 'map' && !suggestMapDay.value) suggestMapDay.value = suggestWindow.value[0] || '' }) // ── P3 — Onglet « Tournées » : tournées RÉELLES (jobs assignés, occupation) de la journée choisie, 1 couleur/tech, tracés OSRM (RouteMap). ──