From 70c89b2cea0d77e5de8864f519664741405b89ca Mon Sep 17 00:00:00 2001 From: louispaulb Date: Thu, 4 Jun 2026 22:33:07 -0400 Subject: [PATCH] =?UTF-8?q?Garde:=202=20horaires=20par=20r=C3=A8gle=20?= =?UTF-8?q?=E2=80=94=20semaine=20(soir=2017h-minuit)=20vs=20fin=20de=20sem?= =?UTF-8?q?aine=20(8h-minuit)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Une règle a maintenant un shift SEMAINE + un shift FIN DE SEMAINE (optionnel). La rotation reste UNE séquence (même tech sur la semaine) ; la génération choisit l'horaire selon le jour : weekend (sam/dim) → shiftWeekend, sinon shift semaine. Wipe couvre les 2 shifts. Modèles semés: « Garde soir 17h-00h » (17:00-23:59) + « Garde fin de sem. 8h-00h » (08:00-23:59), on_call. Co-Authored-By: Claude Opus 4.8 (1M context) --- apps/ops/src/pages/PlanificationPage.vue | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/apps/ops/src/pages/PlanificationPage.vue b/apps/ops/src/pages/PlanificationPage.vue index 3fe3c5d..b2e862f 100644 --- a/apps/ops/src/pages/PlanificationPage.vue +++ b/apps/ops/src/pages/PlanificationPage.vue @@ -269,7 +269,7 @@ - {{ r.dept }} · {{ shiftName(r.shift) }} + {{ r.dept }} · {{ shiftName(r.shift) }} · WE : {{ shiftName(r.shiftWeekend) }} {{ gardeDowLabel(r) }} · dès {{ (r.anchor || '').slice(5) }} · {{ gardeSeqLabel(r) }} @@ -281,8 +281,9 @@
{{ editingGardeId ? 'Modifier la règle' : 'Nouvelle règle' }}
- - Donnée de référence : la séquence commence à cette semaine (ancrage stable dans le temps). + + + Donnée de référence : la séquence commence à cette semaine (ancrage stable dans le temps).
Plages (hors bureau) : @@ -395,7 +396,7 @@ const activeCell = ref(null) // dernière case cliquée {id, name, iso} — pour const anchor = ref(null) const demand = ref([]); const holidays = ref([]); const weekTemplates = ref([]) const gardeRules = ref([]); const showGarde = ref(false) -const newGardeRule = reactive({ dept: '', shift: '', weekdays: [], anchor: '', steps: [] }) +const newGardeRule = reactive({ dept: '', shift: '', shiftWeekend: '', weekdays: [], anchor: '', steps: [] }) const GARDE_DOW = [{ v: 1, l: 'L' }, { v: 2, l: 'M' }, { v: 3, l: 'M' }, { v: 4, l: 'J' }, { v: 5, l: 'V' }, { v: 6, l: 'S' }, { v: 0, l: 'D' }] const history = ref([]); const future = ref([]) const search = ref(''); const groupFilter = ref(null); const maxHours = ref(40) @@ -642,7 +643,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) { - Object.assign(newGardeRule, { dept: r.dept || '', shift: r.shift, weekdays: [...(r.weekdays || [])], anchor: r.anchor || mondayISO(start.value), steps: ruleSteps(r) }) + Object.assign(newGardeRule, { dept: r.dept || '', shift: r.shift, shiftWeekend: r.shiftWeekend || '', 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] @@ -680,7 +681,7 @@ const gardePreview = computed(() => { function saveGarde () { localStorage.setItem(LS_GARDE, JSON.stringify(gardeRules.value)) } function addGardeRule () { if (!newGardeRule.shift || !newGardeRule.steps.length || !newGardeRule.weekdays.length) { $q.notify({ type: 'warning', message: 'Shift, jours et au moins un tech requis' }); return } - const rule = { id: editingGardeId.value || Date.now(), dept: newGardeRule.dept || '—', shift: newGardeRule.shift, weekdays: [...newGardeRule.weekdays], anchor: newGardeRule.anchor || mondayISO(start.value), steps: newGardeRule.steps.map(s => ({ tech: s.tech, weeks: Number(s.weeks) || 1 })) } + const rule = { id: editingGardeId.value || Date.now(), dept: newGardeRule.dept || '—', shift: newGardeRule.shift, shiftWeekend: newGardeRule.shiftWeekend || '', weekdays: [...newGardeRule.weekdays], anchor: newGardeRule.anchor || mondayISO(start.value), steps: newGardeRule.steps.map(s => ({ tech: s.tech, weeks: Number(s.weeks) || 1 })) } if (editingGardeId.value) gardeRules.value = gardeRules.value.map(r => r.id === editingGardeId.value ? rule : r) else gardeRules.value = [...gardeRules.value, rule] saveGarde(); editingGardeId.value = null; newGardeRule.steps = []; newGardeRule.weekdays = [] @@ -700,16 +701,18 @@ async function applyGardeRules () { const ws = addDaysISO(wk0, i * 7) for (let k = 0; k < 7; k++) { const d = addDaysISO(ws, k); const dow = dowOf(d) + const weekend = (dow === 0 || dow === 6) for (const rule of gardeRules.value) { if (!rule.weekdays.includes(dow)) continue - const tpl = tplByName.value[rule.shift]; if (!tpl) continue + const shiftName = (weekend && rule.shiftWeekend) ? rule.shiftWeekend : rule.shift + const tpl = tplByName.value[shiftName]; if (!tpl) continue const id = rotationTech(rule, d); if (!id) continue const t = techs.value.find(x => x.id === id) - list.push({ tech: id, tech_name: t ? t.name : id, date: d, shift: rule.shift, hours: tpl.hours || 8, zone: tpl.zone || '' }) + list.push({ tech: id, tech_name: t ? t.name : id, date: d, shift: shiftName, hours: tpl.hours || 8, zone: tpl.zone || '' }) } } } - const shifts = [...new Set(gardeRules.value.map(r => r.shift))] + const shifts = [...new Set(gardeRules.value.flatMap(r => [r.shift, r.shiftWeekend].filter(Boolean)))] try { const r = await roster.applyGardeHorizon(wk0, weeks, list, shifts) showGarde.value = false; await loadWeek()