diff --git a/apps/ops/src/pages/PlanificationPage.vue b/apps/ops/src/pages/PlanificationPage.vue index 4e5e951..3fe3c5d 100644 --- a/apps/ops/src/pages/PlanificationPage.vue +++ b/apps/ops/src/pages/PlanificationPage.vue @@ -642,8 +642,7 @@ function openGarde () { if (!newGardeRule.anchor) newGardeRule.anchor = mondayIS function addTechToSeq () { if (gardePick.value) { newGardeRule.steps.push({ tech: gardePick.value, weeks: 1 }); gardePick.value = null } } function moveTech (i, dir) { const a = newGardeRule.steps; const j = i + dir; if (j < 0 || j >= a.length) return; const x = a[i]; a.splice(i, 1); a.splice(j, 0, x) } function editGardeRule (r) { - const steps = (r.steps && r.steps.length) ? r.steps.map(s => ({ tech: s.tech, weeks: Number(s.weeks) || 1 })) : (r.techs || []).map(t => ({ tech: t, weeks: r.periodWeeks || 1 })) - Object.assign(newGardeRule, { dept: r.dept || '', shift: r.shift, weekdays: [...(r.weekdays || [])], anchor: r.anchor || mondayISO(start.value), steps }) + Object.assign(newGardeRule, { dept: r.dept || '', shift: r.shift, weekdays: [...(r.weekdays || [])], anchor: r.anchor || mondayISO(start.value), steps: ruleSteps(r) }) editingGardeId.value = r.id } const WD_SEMAINE = [1, 2, 3, 4, 5]; const WD_FINSEM = [6, 0] @@ -653,10 +652,17 @@ function toggleGardeDow (v) { const i = newGardeRule.weekdays.indexOf(v); if (i // ── Moteur de rotation : on PARCOURT la séquence semaine par semaine depuis l'ANCRAGE ── function cycleWeeks (steps) { return (steps || []).reduce((s, x) => s + (Number(x.weeks) || 1), 0) } function stepTechAt (steps, w) { for (const s of steps) { const n = Number(s.weeks) || 1; if (w < n) return s.tech; w -= n } return steps[0] && steps[0].tech } +// Rétrocompat : règle au nouveau format (steps) OU à l'ancien (techs[]+periodWeeks). Collapse les doublons consécutifs. +function ruleSteps (r) { + if (r.steps && r.steps.length) return r.steps.map(s => ({ tech: s.tech, weeks: Number(s.weeks) || 1 })) + const p = r.periodWeeks || 1; const out = [] + for (const t of (r.techs || [])) { const last = out[out.length - 1]; if (last && last.tech === t) last.weeks += p; else out.push({ tech: t, weeks: p }) } + return out +} +function ruleAnchor (r) { return r.anchor || GARDE_EPOCH } // ancrage stable si non défini (vieilles règles) function rotationTech (rule, iso) { - const steps = rule.steps || []; const cyc = cycleWeeks(steps); if (!cyc) return null - const anchor = rule.anchor || mondayISO(iso) - const w0 = (((weekNo(iso) - weekNo(anchor)) % cyc) + cyc) % cyc + const steps = ruleSteps(rule); const cyc = cycleWeeks(steps); if (!cyc) return null + const w0 = (((weekNo(iso) - weekNo(ruleAnchor(rule))) % cyc) + cyc) % cyc for (let k = 0; k < cyc; k++) { const id = stepTechAt(steps, (w0 + k) % cyc); if (id && !isAbsent(id, iso)) return id } // saut d'absent return stepTechAt(steps, w0) } @@ -682,7 +688,7 @@ function addGardeRule () { } function removeGardeRule (i) { gardeRules.value = gardeRules.value.filter((_, j) => j !== i); saveGarde(); if (editingGardeId.value && !gardeRules.value.some(r => r.id === editingGardeId.value)) editingGardeId.value = null } function gardeDowLabel (r) { return (r.weekdays || []).map(w => (GARDE_DOW.find(x => x.v === w) || {}).l).join('') } -function gardeSeqLabel (r) { return (r.steps || []).map(s => ((techs.value.find(t => t.id === s.tech) || {}).name || s.tech) + (s.weeks > 1 ? ' ×' + s.weeks : '')).join(' → ') } +function gardeSeqLabel (r) { return ruleSteps(r).map(s => ((techs.value.find(t => t.id === s.tech) || {}).name || s.tech) + (s.weeks > 1 ? ' ×' + s.weeks : '')).join(' → ') } // Génère les gardes de la semaine affichée selon les règles (rotation par département) const gardeHorizon = ref(8) // nb de semaines à matérialiser (évènement récurrent) // Génère la garde sur un HORIZON (plusieurs semaines) et l'écrit directement (publié) → navigable semaine par semaine.