From 8586b9310ae8d33975622cf3a0bbdd615c2fc0d4 Mon Sep 17 00:00:00 2001 From: louispaulb Date: Tue, 7 Jul 2026 09:56:12 -0400 Subject: [PATCH] =?UTF-8?q?planif:=20Sugg=C3=A9rer=20ne=20fige=20plus=20?= =?UTF-8?q?=E2=80=94=20timeout=20solveur=20+=20dialogue=20toujours=20ferma?= =?UTF-8?q?ble?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - roster API : AbortController (timeout 45s, 90s pour /optimize-plan) → un solveur/hub lent ou injoignable lève au lieu de laisser l'await pendre à l'infini (suggestDlg.building coincé à true). Au-delà → repli heuristique local, dialogue utilisable. - q-dialog Suggérer : @hide remet building=false (jamais coincé-ouvert au ré-open). Co-Authored-By: Claude Opus 4.8 --- apps/ops/src/api/roster.js | 36 +++++++++++------------- apps/ops/src/pages/PlanificationPage.vue | 2 +- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/apps/ops/src/api/roster.js b/apps/ops/src/api/roster.js index f2611ba..54fd1d7 100644 --- a/apps/ops/src/api/roster.js +++ b/apps/ops/src/api/roster.js @@ -5,25 +5,23 @@ */ import { HUB_URL as HUB } from 'src/config/hub' -async function jget (path) { - const r = await fetch(HUB + path) - if (!r.ok) throw new Error('Roster API ' + r.status) - return r.json() -} -async function jpost (path, body) { - const r = await fetch(HUB + path, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(body || {}), - }) - if (!r.ok) throw new Error('Roster API ' + r.status) - return r.json() -} -async function jput (path, body) { - const r = await fetch(HUB + path, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body || {}) }) - if (!r.ok) throw new Error('Roster API ' + r.status) - return r.json() +// Toute requête a un TIMEOUT (AbortController) : un solveur/hub lent ou injoignable ne doit JAMAIS +// laisser l'interface figée (ex. « Suggérer » qui tourne sans fin). Au-delà, on lève → repli local. +async function _fetch (path, init = {}, ms = 45000) { + const ctl = new AbortController() + const timer = setTimeout(() => ctl.abort(), ms) + try { + const r = await fetch(HUB + path, { ...init, signal: ctl.signal }) + if (!r.ok) throw new Error('Roster API ' + r.status) + return await r.json() + } catch (e) { + if (e && e.name === 'AbortError') throw new Error('Roster API timeout (' + Math.round(ms / 1000) + 's) — serveur trop lent ou injoignable') + throw e + } finally { clearTimeout(timer) } } +async function jget (path, ms) { return _fetch(path, {}, ms) } +async function jpost (path, body, ms) { return _fetch(path, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body || {}) }, ms) } +async function jput (path, body, ms) { return _fetch(path, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body || {}) }, ms) } export const listTechnicians = () => jget('/roster/technicians') export const listTemplates = () => jget('/roster/templates') @@ -105,7 +103,7 @@ export const unassignedJobs = () => jget('/roster/unassigned-jobs') // Optimisation de tournées (VRPTW + compétences + temps sur place) via le solveur OR-Tools. payload = { jobs[], vehicles[], ... } export const optimizeRoutes = (payload) => jpost('/roster/optimize-routes', payload) // P4 — orchestration COMPLÈTE côté hub (pool+quarts+occupation+règles d'adresse) : { dates?|days?, tech_ids?, opts, dur_overrides, job_windows } → { plan[], unassigned[], assumed_shift } -export const optimizePlanHub = (payload) => jpost('/roster/optimize-plan', payload) +export const optimizePlanHub = (payload) => jpost('/roster/optimize-plan', payload, 90000) // solveur VRP : timeout 90s → sinon repli local (jamais figé) // Proxys OSRM auto-hébergé (points [[lon,lat],…]) — tracés + matrices SANS Mapbox payant. export const osrmRoute = (points) => jpost('/roster/osrm-route', { points }) // → { km, mins, geometry, legs[] (temps entre arrêts) } export const osrmTable = (points) => jpost('/roster/osrm-table', { points }) // → { durations (s), distances (m) } même forme que Mapbox Matrix diff --git a/apps/ops/src/pages/PlanificationPage.vue b/apps/ops/src/pages/PlanificationPage.vue index 901e0ab..0dccaa3 100644 --- a/apps/ops/src/pages/PlanificationPage.vue +++ b/apps/ops/src/pages/PlanificationPage.vue @@ -1054,7 +1054,7 @@ - +