diff --git a/apps/ops/src/pages/PlanificationPage.vue b/apps/ops/src/pages/PlanificationPage.vue
index 8b524cf..dc32b68 100644
--- a/apps/ops/src/pages/PlanificationPage.vue
+++ b/apps/ops/src/pages/PlanificationPage.vue
@@ -272,13 +272,17 @@
{{ r.dept }} · {{ shiftName(r.shift) }}
{{ gardeDowLabel(r) }} · toutes les {{ r.periodWeeks }} sem. · rotation : {{ r.techs.map(id => (techs.find(t => t.id === id) || {}).name || id).join(' → ') }}
-
+
+ Modifier (ordre, techs, période)
+
+
+
{{ editingGardeId ? 'Modifier la règle' : 'Nouvelle règle' }}
-
+
-
+ 2 = chaque tech fait 2 semaines de suite avant de passer au suivant
Jours :
@@ -286,9 +290,19 @@
-
+
+
+
Ordre de rotation :
+
+ {{ i + 1 }}.
+ {{ techName(id) }}
+
+
+
+
-
+
+
@@ -588,7 +602,11 @@ function loadLS () { try { demand.value = JSON.parse(localStorage.getItem(LS_DEM
// ── Rotation de garde par département (récurrence + rotation) ────────────────
const GARDE_EPOCH = '2026-01-05' // lundi de référence pour l'index de semaine
const gardeTemplateOptions = computed(() => templates.value.slice().sort((a, b) => (b.on_call ? 1 : 0) - (a.on_call ? 1 : 0)).map(t => ({ label: t.template_name + (t.on_call ? ' 🛡️' : ''), value: t.name })))
-const deptTechs = computed(() => techs.value.filter(t => !newGardeRule.dept || t.group === newGardeRule.dept).map(t => ({ label: t.name, value: t.id })))
+const groupNames = computed(() => [...new Set(techs.value.map(t => t.group).filter(Boolean))].sort())
+const editingGardeId = ref(null)
+function techName (id) { const t = techs.value.find(x => x.id === id); return t ? t.name : id }
+function moveTech (i, dir) { const a = newGardeRule.techs; 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], periodWeeks: r.periodWeeks || 1, techs: [...r.techs] }); editingGardeId.value = r.id }
function d2ms (iso) { const a = iso.split('-').map(Number); return Date.UTC(a[0], a[1] - 1, a[2]) }
function mondayISO (iso) { return addDaysISO(iso, -((dowOf(iso) + 6) % 7)) }
function weekIndex (iso) { return Math.round((d2ms(mondayISO(iso)) - d2ms(GARDE_EPOCH)) / (7 * 86400000)) }
@@ -602,11 +620,14 @@ function rotationTech (rule, iso) {
function toggleGardeDow (v) { const i = newGardeRule.weekdays.indexOf(v); if (i >= 0) newGardeRule.weekdays.splice(i, 1); else newGardeRule.weekdays.push(v) }
function saveGarde () { localStorage.setItem(LS_GARDE, JSON.stringify(gardeRules.value)) }
function addGardeRule () {
- if (!newGardeRule.dept || !newGardeRule.shift || !newGardeRule.techs.length || !newGardeRule.weekdays.length) { $q.notify({ type: 'warning', message: 'Département, shift, jours et techs requis' }); return }
- gardeRules.value = [...gardeRules.value, { id: Date.now(), dept: newGardeRule.dept, shift: newGardeRule.shift, weekdays: [...newGardeRule.weekdays], periodWeeks: newGardeRule.periodWeeks || 1, techs: [...newGardeRule.techs] }]
- saveGarde(); newGardeRule.techs = []; $q.notify({ type: 'positive', message: 'Règle de garde ajoutée' })
+ if (!newGardeRule.shift || !newGardeRule.techs.length || !newGardeRule.weekdays.length) { $q.notify({ type: 'warning', message: 'Shift, jours et techs requis (département optionnel)' }); return }
+ const rule = { id: editingGardeId.value || Date.now(), dept: newGardeRule.dept || '—', shift: newGardeRule.shift, weekdays: [...newGardeRule.weekdays], periodWeeks: newGardeRule.periodWeeks || 1, techs: [...newGardeRule.techs] }
+ 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.techs = []; newGardeRule.weekdays = []
+ $q.notify({ type: 'positive', message: 'Règle de garde enregistrée' })
}
-function removeGardeRule (i) { gardeRules.value = gardeRules.value.filter((_, j) => j !== i); saveGarde() }
+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('') }
// Génère les gardes de la semaine affichée selon les règles (rotation par département)
function applyGardeRules () {