Garde: 2 horaires par règle — semaine (soir 17h-minuit) vs fin de semaine (8h-minuit)
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) <noreply@anthropic.com>
This commit is contained in:
parent
0320c8716f
commit
70c89b2cea
|
|
@ -269,7 +269,7 @@
|
||||||
<q-list v-if="gardeRules.length" dense bordered class="rounded-borders q-mb-md">
|
<q-list v-if="gardeRules.length" dense bordered class="rounded-borders q-mb-md">
|
||||||
<q-item v-for="(r, i) in gardeRules" :key="r.id">
|
<q-item v-for="(r, i) in gardeRules" :key="r.id">
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label class="text-weight-medium">{{ r.dept }} · {{ shiftName(r.shift) }}</q-item-label>
|
<q-item-label class="text-weight-medium">{{ r.dept }} · {{ shiftName(r.shift) }}<span v-if="r.shiftWeekend"> · WE : {{ shiftName(r.shiftWeekend) }}</span></q-item-label>
|
||||||
<q-item-label caption>{{ gardeDowLabel(r) }} · dès {{ (r.anchor || '').slice(5) }} · {{ gardeSeqLabel(r) }}</q-item-label>
|
<q-item-label caption>{{ gardeDowLabel(r) }} · dès {{ (r.anchor || '').slice(5) }} · {{ gardeSeqLabel(r) }}</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
<q-item-section side class="row no-wrap">
|
<q-item-section side class="row no-wrap">
|
||||||
|
|
@ -281,8 +281,9 @@
|
||||||
<div class="text-caption text-weight-medium q-mb-xs">{{ editingGardeId ? 'Modifier la règle' : 'Nouvelle règle' }}</div>
|
<div class="text-caption text-weight-medium q-mb-xs">{{ editingGardeId ? 'Modifier la règle' : 'Nouvelle règle' }}</div>
|
||||||
<div class="row q-col-gutter-sm items-end">
|
<div class="row q-col-gutter-sm items-end">
|
||||||
<q-select dense outlined v-model="newGardeRule.dept" :options="groupNames" use-input fill-input hide-selected new-value-mode="add-unique" input-debounce="0" label="Département (optionnel)" style="width:170px" hint="existant ou tape un nom" />
|
<q-select dense outlined v-model="newGardeRule.dept" :options="groupNames" use-input fill-input hide-selected new-value-mode="add-unique" input-debounce="0" label="Département (optionnel)" style="width:170px" hint="existant ou tape un nom" />
|
||||||
<q-select dense outlined v-model="newGardeRule.shift" :options="gardeTemplateOptions" emit-value map-options label="Shift de garde" style="width:190px" />
|
<q-select dense outlined v-model="newGardeRule.shift" :options="gardeTemplateOptions" emit-value map-options label="Shift semaine (soir)" style="width:180px" />
|
||||||
<q-input dense outlined type="date" v-model="newGardeRule.anchor" label="Rotation démarre la semaine du" style="width:210px"><q-tooltip>Donnée de référence : la séquence commence à cette semaine (ancrage stable dans le temps).</q-tooltip></q-input>
|
<q-select dense outlined clearable v-model="newGardeRule.shiftWeekend" :options="gardeTemplateOptions" emit-value map-options label="Shift fin de semaine" style="width:180px" hint="optionnel — sinon = shift semaine" />
|
||||||
|
<q-input dense outlined type="date" v-model="newGardeRule.anchor" label="Rotation démarre la semaine du" style="width:200px"><q-tooltip>Donnée de référence : la séquence commence à cette semaine (ancrage stable dans le temps).</q-tooltip></q-input>
|
||||||
</div>
|
</div>
|
||||||
<div class="q-mt-sm row items-center q-gutter-xs">
|
<div class="q-mt-sm row items-center q-gutter-xs">
|
||||||
<span class="text-caption text-grey-7 q-mr-xs">Plages (hors bureau) :</span>
|
<span class="text-caption text-grey-7 q-mr-xs">Plages (hors bureau) :</span>
|
||||||
|
|
@ -395,7 +396,7 @@ const activeCell = ref(null) // dernière case cliquée {id, name, iso} — pour
|
||||||
const anchor = ref(null)
|
const anchor = ref(null)
|
||||||
const demand = ref([]); const holidays = ref([]); const weekTemplates = ref([])
|
const demand = ref([]); const holidays = ref([]); const weekTemplates = ref([])
|
||||||
const gardeRules = ref([]); const showGarde = ref(false)
|
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 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 history = ref([]); const future = ref([])
|
||||||
const search = ref(''); const groupFilter = ref(null); const maxHours = ref(40)
|
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 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 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) {
|
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
|
editingGardeId.value = r.id
|
||||||
}
|
}
|
||||||
const WD_SEMAINE = [1, 2, 3, 4, 5]; const WD_FINSEM = [6, 0]
|
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 saveGarde () { localStorage.setItem(LS_GARDE, JSON.stringify(gardeRules.value)) }
|
||||||
function addGardeRule () {
|
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 }
|
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)
|
if (editingGardeId.value) gardeRules.value = gardeRules.value.map(r => r.id === editingGardeId.value ? rule : r)
|
||||||
else gardeRules.value = [...gardeRules.value, rule]
|
else gardeRules.value = [...gardeRules.value, rule]
|
||||||
saveGarde(); editingGardeId.value = null; newGardeRule.steps = []; newGardeRule.weekdays = []
|
saveGarde(); editingGardeId.value = null; newGardeRule.steps = []; newGardeRule.weekdays = []
|
||||||
|
|
@ -700,16 +701,18 @@ async function applyGardeRules () {
|
||||||
const ws = addDaysISO(wk0, i * 7)
|
const ws = addDaysISO(wk0, i * 7)
|
||||||
for (let k = 0; k < 7; k++) {
|
for (let k = 0; k < 7; k++) {
|
||||||
const d = addDaysISO(ws, k); const dow = dowOf(d)
|
const d = addDaysISO(ws, k); const dow = dowOf(d)
|
||||||
|
const weekend = (dow === 0 || dow === 6)
|
||||||
for (const rule of gardeRules.value) {
|
for (const rule of gardeRules.value) {
|
||||||
if (!rule.weekdays.includes(dow)) continue
|
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 id = rotationTech(rule, d); if (!id) continue
|
||||||
const t = techs.value.find(x => x.id === id)
|
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 {
|
try {
|
||||||
const r = await roster.applyGardeHorizon(wk0, weeks, list, shifts)
|
const r = await roster.applyGardeHorizon(wk0, weeks, list, shifts)
|
||||||
showGarde.value = false; await loadWeek()
|
showGarde.value = false; await loadWeek()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user