Garde: dept libre + liste techs complète + réordonner la rotation + éditer + 2 sem. consécutives
Fix listes vides: département = champ libre optionnel (existants OU texte), liste des techs = TOUS (plus de désactivation sur dept). Réordonnancement de la rotation (↑/↓), édition d'une règle (crayon → recharge dans le formulaire → Mettre à jour). Champ « Sem. consécutives / tech » (mettre 2 = un tech fait 2 semaines de suite). Annuler l'édition. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
fe60eeb485
commit
05b5b16a5d
|
|
@ -272,13 +272,17 @@
|
|||
<q-item-label class="text-weight-medium">{{ r.dept }} · {{ shiftName(r.shift) }}</q-item-label>
|
||||
<q-item-label caption>{{ gardeDowLabel(r) }} · toutes les {{ r.periodWeeks }} sem. · rotation : {{ r.techs.map(id => (techs.find(t => t.id === id) || {}).name || id).join(' → ') }}</q-item-label>
|
||||
</q-item-section>
|
||||
<q-item-section side><q-btn flat dense round size="sm" icon="delete" color="grey-6" @click="removeGardeRule(i)" /></q-item-section>
|
||||
<q-item-section side class="row no-wrap">
|
||||
<q-btn flat dense round size="sm" icon="edit" color="primary" @click="editGardeRule(r)"><q-tooltip>Modifier (ordre, techs, période)</q-tooltip></q-btn>
|
||||
<q-btn flat dense round size="sm" icon="delete" color="grey-6" @click="removeGardeRule(i)" />
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
<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">
|
||||
<q-select dense outlined v-model="newGardeRule.dept" :options="groupOptions" emit-value map-options label="Département" style="width:170px" />
|
||||
<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:180px" 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-input dense outlined type="number" min="1" v-model.number="newGardeRule.periodWeeks" label="Toutes les (sem.)" style="width:130px" />
|
||||
<q-input dense outlined type="number" min="1" v-model.number="newGardeRule.periodWeeks" label="Sem. consécutives / tech" style="width:160px"><q-tooltip>2 = chaque tech fait 2 semaines de suite avant de passer au suivant</q-tooltip></q-input>
|
||||
</div>
|
||||
<div class="q-mt-sm row items-center q-gutter-xs">
|
||||
<span class="text-caption text-grey-7 q-mr-xs">Jours :</span>
|
||||
|
|
@ -286,9 +290,19 @@
|
|||
<q-btn flat dense size="sm" no-caps label="Tous les soirs" @click="newGardeRule.weekdays = [1,2,3,4,5,6,0]" />
|
||||
<q-btn flat dense size="sm" no-caps label="Week-ends" @click="newGardeRule.weekdays = [5,6,0]" />
|
||||
</div>
|
||||
<q-select dense outlined multiple use-chips v-model="newGardeRule.techs" :options="deptTechs" emit-value map-options label="Techs en rotation (dans l'ordre de sélection)" class="q-mt-sm" :disable="!newGardeRule.dept" hint="L'ordre de sélection = l'ordre de la rotation" />
|
||||
<q-select dense outlined multiple use-chips v-model="newGardeRule.techs" :options="techOptions" emit-value map-options label="Techs en rotation" class="q-mt-sm" hint="Ajoute les techs ; règle l'ordre ci-dessous" />
|
||||
<div v-if="newGardeRule.techs.length" class="q-mt-xs">
|
||||
<div class="text-caption text-grey-7">Ordre de rotation :</div>
|
||||
<div v-for="(id, i) in newGardeRule.techs" :key="id" class="row items-center no-wrap">
|
||||
<span class="text-caption" style="min-width:24px">{{ i + 1 }}.</span>
|
||||
<span class="text-caption col">{{ techName(id) }}</span>
|
||||
<q-btn flat dense round size="xs" icon="arrow_upward" :disable="i === 0" @click="moveTech(i, -1)" />
|
||||
<q-btn flat dense round size="xs" icon="arrow_downward" :disable="i === newGardeRule.techs.length - 1" @click="moveTech(i, 1)" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row items-center q-mt-md">
|
||||
<q-btn dense unelevated color="brown" icon="add" label="Ajouter la règle" @click="addGardeRule" />
|
||||
<q-btn dense unelevated color="brown" :icon="editingGardeId ? 'save' : 'add'" :label="editingGardeId ? 'Mettre à jour' : 'Ajouter la règle'" @click="addGardeRule" />
|
||||
<q-btn v-if="editingGardeId" flat dense class="q-ml-xs" label="Annuler" @click="editingGardeId = null; newGardeRule.techs = []; newGardeRule.weekdays = []" />
|
||||
<q-space />
|
||||
<q-btn dense unelevated color="primary" icon="auto_awesome" label="Appliquer à la semaine" @click="applyGardeRules" />
|
||||
</div>
|
||||
|
|
@ -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 () {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user