Planification: fix menu (régression cellHours) + copier/coller + slider d'ajustement dans le menu
BUG: le menu de case plantait sur les cases occupées (cellHours retiré mais encore appelé l.259)
→ c'est ce qui cassait le copier-coller au clic. Corrigé ({{ a.hours }}h).
- Copier/Coller déplacés dans le MENU de la case (clic simple, plus besoin de Cmd+clic) :
« Copier cette case » / « Coller ». (Boutons barre + Ctrl/Cmd+C/V conservés.)
- « Ajuster l'horaire (glisser) » : q-range dans le menu → élargir/réduire le créneau de dispo,
+ toggle Garde. Applique = trouve/crée un modèle auto-nommé (8h–18h) et remplace la case.
→ la dispo élargie est aussitôt offerte au booking (modèle standard).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c2f3e4d666
commit
7f6d314cc0
|
|
@ -256,14 +256,27 @@
|
|||
<q-item-label header>{{ menu.tech && menu.tech.name }} — {{ menu.day && menu.day.dnum }}</q-item-label>
|
||||
<q-item v-for="a in menuCellShifts" :key="'c' + a.shift">
|
||||
<q-item-section avatar><span class="code-chip" :style="chip(cellColor(a))">{{ cellCode(a) }}</span></q-item-section>
|
||||
<q-item-section>{{ a.shift_name || a.shift }} <span class="text-grey-6">{{ cellHours(a) }}h</span></q-item-section>
|
||||
<q-item-section>{{ a.shift_name || a.shift }} <span class="text-grey-6">{{ a.hours }}h</span></q-item-section>
|
||||
<q-item-section side><q-btn flat dense round size="sm" icon="close" color="grey-7" @click="removeShiftFromMenu(a)"><q-tooltip>Retirer</q-tooltip></q-btn></q-item-section>
|
||||
</q-item>
|
||||
<q-separator />
|
||||
<q-item-label header>Ajouter un shift</q-item-label>
|
||||
<q-item v-for="t in templates" :key="t.name" clickable v-close-popup @click="addFromMenu(t)"><q-item-section avatar><span class="code-chip" :style="chip(t.color)">{{ code(t) }}</span></q-item-section><q-item-section>{{ t.template_name }}</q-item-section></q-item>
|
||||
<q-separator v-if="menuCellShifts.length" />
|
||||
<q-item v-if="menuCellShifts.length" clickable v-close-popup @click="clearOne"><q-item-section class="text-grey-7">Libérer tout</q-item-section></q-item>
|
||||
<q-separator />
|
||||
<q-item clickable v-close-popup @click="copyFromMenu"><q-item-section avatar><q-icon name="content_copy" size="18px" /></q-item-section><q-item-section>Copier cette case</q-item-section></q-item>
|
||||
<q-item clickable v-close-popup @click="pasteFromMenu" :disable="!cellClipboard.length"><q-item-section avatar><q-icon name="content_paste" size="18px" /></q-item-section><q-item-section>Coller{{ cellClipboard.length ? ' (' + cellClipboard.length + ')' : '' }}</q-item-section></q-item>
|
||||
<q-separator />
|
||||
<q-item-label header>Ajuster l'horaire (glisser)</q-item-label>
|
||||
<div class="q-px-md q-pb-sm" style="min-width:230px" @click.stop>
|
||||
<q-range v-model="menuRange" :min="0" :max="24" :step="0.5" snap label :left-label-value="fmtH(menuRange.min) + 'h'" :right-label-value="fmtH(menuRange.max) + 'h'" color="primary" />
|
||||
<div class="row items-center no-wrap q-gutter-sm q-mt-xs">
|
||||
<span class="text-caption text-weight-bold">{{ fmtH(menuRange.min) }}h–{{ fmtH(menuRange.max) }}h</span>
|
||||
<q-space />
|
||||
<q-toggle dense v-model="menuOnCall" :true-value="1" :false-value="0" label="Garde" color="brown" />
|
||||
<q-btn dense unelevated size="sm" color="primary" label="Appliquer" @click="applyMenuRange" />
|
||||
</div>
|
||||
</div>
|
||||
</q-list>
|
||||
</q-menu>
|
||||
</q-page>
|
||||
|
|
@ -549,6 +562,7 @@ function applyTemplate (tm) {
|
|||
|
||||
// édition + sélection
|
||||
const menu = reactive({ show: false, target: null, tech: null, day: null })
|
||||
const menuRange = ref({ min: 8, max: 16 }); const menuOnCall = ref(0)
|
||||
function rect (sti, sdi, eti, edi) {
|
||||
const t0 = Math.min(sti, eti), t1 = Math.max(sti, eti), d0 = Math.min(sdi, edi), d1 = Math.max(sdi, edi)
|
||||
const out = []
|
||||
|
|
@ -566,7 +580,10 @@ function onCellClick (t, d, ev, ti, di) {
|
|||
if (justDragged.value) { justDragged.value = false; return }
|
||||
if (ev.shiftKey && anchor.value) { selectBlock(ti, di); return }
|
||||
if (ev.ctrlKey || ev.metaKey) { const k = t.id + '|' + d.iso; selection.value = selSet.value.has(k) ? selection.value.filter(x => x !== k) : [...selection.value, k]; anchor.value = { ti, di }; return }
|
||||
selection.value = []; anchor.value = { ti, di }; menu.tech = t; menu.day = d; menu.target = ev.currentTarget; menu.show = true
|
||||
selection.value = []; anchor.value = { ti, di }; menu.tech = t; menu.day = d; menu.target = ev.currentTarget
|
||||
const wr = winOf(t.id, d.iso, false); const wg = winOf(t.id, d.iso, true); const w0 = wr || wg
|
||||
menuRange.value = w0 ? { min: w0.s, max: w0.e } : { min: 8, max: 16 }; menuOnCall.value = wr ? 0 : (wg ? 1 : 0)
|
||||
menu.show = true
|
||||
}
|
||||
function selectBlock (ti, di) { const a = anchor.value; const t0 = Math.min(a.ti, ti); const t1 = Math.max(a.ti, ti); const d0 = Math.min(a.di, di); const d1 = Math.max(a.di, di); const add = []; for (let i = t0; i <= t1; i++) for (let j = d0; j <= d1; j++) add.push(visibleTechs.value[i].id + '|' + dayList.value[j].iso); selection.value = [...new Set([...selection.value, ...add])] }
|
||||
function maybeSelectCol (di) { const ks = visibleTechs.value.map(t => t.id + '|' + dayList.value[di].iso); const all = ks.every(k => selSet.value.has(k)); selection.value = all ? selection.value.filter(k => !ks.includes(k)) : [...new Set([...selection.value, ...ks])] }
|
||||
|
|
@ -575,6 +592,17 @@ const menuCellShifts = computed(() => (menu.tech && menu.day) ? cellsOf(menu.tec
|
|||
function addFromMenu (tpl) { if (menu.tech && menu.day) { pushHistory(); addShift(menu.tech.id, menu.tech.name, menu.day.iso, tpl) } }
|
||||
function removeShiftFromMenu (a) { pushHistory(); removeShift(a.tech, a.date, a.shift) }
|
||||
function clearOne () { if (menu.tech && menu.day) { pushHistory(); clearLocal(menu.tech.id, menu.day.iso); menu.show = false } }
|
||||
// Menu : copier / coller (marche au clic, sans Cmd+clic) + ajuster l'horaire au slider
|
||||
function copyFromMenu () { if (!menu.tech || !menu.day) return; cellClipboard.value = cellsOf(menu.tech.id, menu.day.iso).map(a => a.shift); $q.notify({ type: 'positive', message: cellClipboard.value.length ? (cellClipboard.value.length + ' shift(s) copié(s) — ouvre une autre case puis Coller') : 'Case vide copiée (Coller la videra)' }) }
|
||||
function pasteFromMenu () { if (!menu.tech || !menu.day) return; pushHistory(); if (!cellClipboard.value.length) { clearLocal(menu.tech.id, menu.day.iso) } else { for (const name of cellClipboard.value) { const tpl = tplByName.value[name]; if (tpl) addShift(menu.tech.id, menu.tech.name, menu.day.iso, tpl) } } menu.show = false }
|
||||
async function applyMenuRange () {
|
||||
if (!menu.tech || !menu.day) return
|
||||
const s = numToTime(menuRange.value.min); const e = numToTime(menuRange.value.max)
|
||||
const nm = fmtH(menuRange.value.min) + 'h–' + fmtH(menuRange.value.max) + 'h' + (menuOnCall.value ? ' (garde)' : '')
|
||||
let tpl = templates.value.find(t => t.template_name === nm)
|
||||
if (!tpl) { try { await roster.createTemplate({ template_name: nm, start_time: s + ':00', end_time: e + ':00', hours: calcHours(s, e), color: menuOnCall.value ? '#6d4c41' : '#1976d2', default_required: 1, on_call: menuOnCall.value ? 1 : 0 }); await refreshTemplates(); tpl = templates.value.find(t => t.template_name === nm) } catch (e2) { err(e2); return } }
|
||||
if (tpl) { pushHistory(); setCellReplace(menu.tech.id, menu.tech.name, menu.day.iso, tpl); menu.show = false }
|
||||
}
|
||||
function assignBulk (tpl) { pushHistory(); for (const k of selection.value) { const [tid, iso] = k.split('|'); const t = techs.value.find(x => x.id === tid); addShift(tid, t ? t.name : tid, iso, tpl) } selection.value = [] }
|
||||
function clearBulk () { pushHistory(); for (const k of selection.value) { const [tid, iso] = k.split('|'); clearLocal(tid, iso) } selection.value = [] }
|
||||
// Copier-coller une case (bâtir l'horaire vite) : copie les shifts de la 1re case sélectionnée → colle dans les autres
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user