diff --git a/apps/ops/src/pages/PlanificationPage.vue b/apps/ops/src/pages/PlanificationPage.vue
index 62430c8..d0d1bba 100644
--- a/apps/ops/src/pages/PlanificationPage.vue
+++ b/apps/ops/src/pages/PlanificationPage.vue
@@ -286,11 +286,13 @@
Occurrences consécutives avant de passer au tech suivant (1 = change à chaque {{ newGardeRule.unit === 'week' ? 'semaine' : 'jour de garde' }})
- Jours :
- {{ dw.l }}
-
-
+ Plages (hors bureau) :
+
+
+ ·
+ {{ dw.l }}
+ Combinables. La garde couvre les heures du shift choisi (hors bureau) ; pour des heures différentes semaine/week-end, crée 2 règles.
@@ -305,6 +307,12 @@
Retirer
+
+
Aperçu (qui est de garde, prochaines semaines) :
+
+ sem. {{ p.week.slice(8) }}/{{ p.week.slice(5, 7) }} → {{ p.name }}
+
+
@@ -379,7 +387,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: [], periodWeeks: 1, unit: 'occ', techs: [] })
+const newGardeRule = reactive({ dept: '', shift: '', weekdays: [], periodWeeks: 1, unit: 'week', techs: [] })
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)
@@ -611,7 +619,10 @@ const groupNames = computed(() => [...new Set(techs.value.map(t => t.group).filt
const editingGardeId = ref(null); const gardePick = ref(null)
function addTechToSeq () { if (gardePick.value) { newGardeRule.techs.push(gardePick.value); gardePick.value = null } } // doublons permis (tours inégaux)
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, unit: r.unit || 'occ', techs: [...r.techs] }); editingGardeId.value = r.id }
+function editGardeRule (r) { Object.assign(newGardeRule, { dept: r.dept || '', shift: r.shift, weekdays: [...r.weekdays], periodWeeks: r.periodWeeks || 1, unit: r.unit || 'week', techs: [...r.techs] }); editingGardeId.value = r.id }
+const WD_SEMAINE = [1, 2, 3, 4, 5]; const WD_FINSEM = [6, 0]
+function isSetActive (set) { return set.length && set.every(v => newGardeRule.weekdays.includes(v)) }
+function toggleWeekdaysSet (set) { if (isSetActive(set)) newGardeRule.weekdays = newGardeRule.weekdays.filter(v => !set.includes(v)); else newGardeRule.weekdays = [...new Set([...newGardeRule.weekdays, ...set])] }
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)) }
@@ -624,7 +635,20 @@ function occurrenceIndex (rule, iso) {
return cnt
}
// Index de rotation : par OCCURRENCE de garde (défaut → la séquence continue chaque jour de garde) ou par SEMAINE (bloc).
-function gardeIndex (rule, iso) { return Math.floor(((rule.unit || 'occ') === 'week' ? weekIndex(iso) : occurrenceIndex(rule, iso)) / (rule.periodWeeks || 1)) }
+function gardeIndex (rule, iso) { return Math.floor(((rule.unit || 'week') === 'week' ? weekIndex(iso) : occurrenceIndex(rule, iso)) / (rule.periodWeeks || 1)) }
+// Aperçu : qui est de garde sur les prochaines semaines (depuis la semaine affichée) — reflète la file en cours d'édition
+const gardePreview = computed(() => {
+ const rule = newGardeRule; if (!rule.techs.length || !rule.weekdays.length) return []
+ const out = []; const wk0 = mondayISO(start.value)
+ for (let i = 0; i < 8; i++) {
+ const ws = addDaysISO(wk0, i * 7); let iso = null
+ for (let k = 0; k < 7; k++) { const d = addDaysISO(ws, k); if (rule.weekdays.includes(dowOf(d))) { iso = d; break } }
+ if (!iso) continue
+ const base = gardeIndex(rule, iso); const id = rule.techs[((base % rule.techs.length) + rule.techs.length) % rule.techs.length]
+ out.push({ week: ws, name: (techs.value.find(t => t.id === id) || {}).name || id })
+ }
+ return out
+})
// Tech de garde pour une date ; saute un tech absent au profit du suivant.
function rotationTech (rule, iso) {
if (!rule.techs || !rule.techs.length) return null