fix(ops): job overdue = due date, not created; day-list from carousel
Retours terrain : 1. « SLA dépassé » / en-retard se basait sur la CRÉATION du ticket (jobSlaBadge → creation), ce qui marque en retard des jobs planifiés plus tard (le client peut demander une visite quelques jours après l'incident). Désormais l'échéance = la DATE DE VISITE PRÉVUE (scheduled_date) : badge « En retard » seulement si scheduled_date < aujourd'hui ; futur/aujourd'hui = à l'heure. Repli SLA-création uniquement pour les jobs SANS date planifiée (backlog). (Le dispatch utilisait déjà scheduled_date pour le périmètre — inchangé.) 2. Le carrousel « N à répartir » (Tournées) est plus long à scanner que la liste par jour du legacy. La pastille « N à répartir » est maintenant cliquable → ouvre la liste VERTICALE des jobs de ce jour (panneau « Jobs à assigner » via openDayInPanel, filtré sur le jour) avec recherche + chips type/compétence + tri par ville. Meilleure lecture, surtout sur mobile. Vérifié live (Tournées, 06/07) : clic « à répartir » ouvre le panneau filtré (25 jobs du jour) ; 0 nouvelle erreur. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
bae7303d66
commit
4c8242eccb
|
|
@ -498,7 +498,7 @@
|
|||
<!-- Jobs DUS ce jour, NON assignés (mis en évidence) — chips secteur filtrent liste+carte · clic = détail · « Suggérer » = dispatch auto -->
|
||||
<div v-if="routeDayUnassigned.length" class="rt-unassigned">
|
||||
<div class="rt-ua-top">
|
||||
<span class="rt-ua-lbl"><q-icon name="inbox" size="15px" color="deep-orange-7" /> <b>{{ routeDayUnassignedView.length }}</b> à répartir<span v-if="routeSectorSel.size" class="text-grey-6"> / {{ routeDayUnassigned.length }}</span> :</span>
|
||||
<button type="button" class="rt-ua-lbl rt-ua-open" @click="openDayInPanel(routesDay)"><q-icon name="inbox" size="15px" color="deep-orange-7" /> <b>{{ routeDayUnassignedView.length }}</b> à répartir<span v-if="routeSectorSel.size" class="text-grey-6"> / {{ routeDayUnassigned.length }}</span><q-icon name="open_in_full" size="12px" class="q-ml-xs" /><q-tooltip>Voir la liste des jobs de ce jour (recherche · ville · compétence)</q-tooltip></button>
|
||||
<template v-if="routeSectors.length > 1">
|
||||
<button v-for="s in routeSectors" :key="s.sector" type="button" class="rt-sect" :class="{ on: routeSectorSel.has(s.sector) }" @click="toggleRouteSector(s.sector)"><q-icon name="place" size="11px" />{{ s.sector }}<span class="rt-sect-n">{{ s.n }}</span></button>
|
||||
<button v-if="routeSectorSel.size" type="button" class="rt-sect rt-sect-x" @click="routeSectorSel = new Set()">✕ tout</button>
|
||||
|
|
@ -4288,7 +4288,17 @@ const poolByName = computed(() => { const m = {}; for (const j of (assignPanel.j
|
|||
const { slaFor: _slaForTicket } = useSla()
|
||||
const PRIO_CAP = { high: 'High', 'élevée': 'High', elevee: 'High', urgent: 'Urgent', medium: 'Medium', moyenne: 'Medium', normal: 'Medium', low: 'Low', basse: 'Low' }
|
||||
function jobSlaBadge (job) { // { state, color, text, icon, label } pour at_risk/breached SEULEMENT (états actionnables) ; null sinon
|
||||
if (!job || !job.creation) return null
|
||||
if (!job) return null
|
||||
// ⚠️ Échéance = DATE DE VISITE PRÉVUE (scheduled_date), PAS la création : un job planifié dans le futur n'est PAS en retard
|
||||
// même si l'incident est ancien (le client peut avoir demandé une visite plus tard). La création ne sert que faute de date.
|
||||
const sched = job.scheduled_date
|
||||
if (sched && sched !== 'Sans date' && /^\d{4}-\d{2}-\d{2}$/.test(sched)) {
|
||||
// Badge « en retard » UNIQUEMENT si la date de visite est PASSÉE (aujourd'hui/futur = à l'heure, pas de badge).
|
||||
if (sched < todayISO()) { const b = SLA_BADGE.breached; return { state: 'breached', color: b.color, text: b.text, icon: b.icon, label: 'Visite prévue en retard — ' + fmtDueLabel(sched), short: 'En retard' } }
|
||||
return null
|
||||
}
|
||||
// Aucune date de visite planifiée : SLA classique (vieillissement du backlog depuis la création).
|
||||
if (!job.creation) return null
|
||||
const prio = PRIO_CAP[String(job.priority || jobPriorityOf(job.name) || 'medium').toLowerCase()] || 'Medium'
|
||||
// job → ticket synthétique : first_responded_on renseigné → on SAUTE la bande « réponse » et on prend la bande RÉSOLUTION depuis la création.
|
||||
const s = _slaForTicket({ issue_type: job.job_type || undefined, priority: prio, creation: job.creation, first_responded_on: job.creation, status: (String(job.status || '').toLowerCase() === 'completed' ? 'Closed' : job.status) })
|
||||
|
|
@ -5731,6 +5741,8 @@ onBeforeRouteLeave(() => { if (dirty.value && !window.confirm(DIRTY_MSG)) return
|
|||
.rt-ua-jobs::-webkit-scrollbar { height: 6px; }
|
||||
.rt-ua-jobs::-webkit-scrollbar-thumb { background: #fdba74; border-radius: 3px; }
|
||||
.rt-ua-lbl { font-size: 12px; color: #9a3412; display: inline-flex; align-items: center; gap: 3px; }
|
||||
.rt-ua-open { background: #fff; border: 1px solid #fed7aa; border-radius: 12px; padding: 2px 8px; cursor: pointer; font-weight: 600; }
|
||||
.rt-ua-open:hover { border-color: #fb923c; background: #fff7ed; }
|
||||
.rt-sect { display: inline-flex; align-items: center; gap: 2px; padding: 2px 8px; border: 1px solid #cbd5e1; border-radius: 12px; background: #fff; font-size: 11.5px; font-weight: 600; color: #475569; cursor: pointer; user-select: none; }
|
||||
.rt-sect.on { background: #6366f1; border-color: #6366f1; color: #fff; }
|
||||
.rt-sect-n { font-weight: 800; margin-left: 2px; opacity: .85; }
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user