planif: horaires (lot) — recherche/skill/tout-aucun ; grille 2 sem. par défaut (sélecteur retiré)
- WeeklyScheduleEditor (lot) : recherche par nom + chips « par compétence » (sélectionne les techs qui l'ont) + boutons Tout / Aucun. Passe t.skills depuis openSchedGenBulk. Avant : liste de chips sans filtre. - Grille : 2 semaines (days=14) par défaut, sélecteur « Semaine/2 sem. » retiré (évitait les changements d'étendue accidentels). - (Rappel : onScheduleApply recharge déjà la grille → les quarts générés en lot apparaissent après application.) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
712f113e29
commit
969d5af9f9
|
|
@ -29,6 +29,13 @@ fill(SCHEDULE_PRESETS[0].schedule)
|
|||
watch(() => props.modelValue, (o) => { if (o) { fill(SCHEDULE_PRESETS[0].schedule); weeks.value = mode.value === 'recurring' ? 6 : 1; selected.value = new Set((props.techs || []).map(t => t.id)) } })
|
||||
watch(mode, (m) => { weeks.value = m === 'recurring' ? 6 : 1 })
|
||||
function toggleTech (id) { const s = new Set(selected.value); s.has(id) ? s.delete(id) : s.add(id); selected.value = s }
|
||||
// Sélection des techs (mode lot) : recherche par nom · sélection par compétence · tout / aucun
|
||||
const techSearch = ref('')
|
||||
const allSkills = computed(() => { const s = new Set(); for (const t of (props.techs || [])) for (const sk of (t.skills || [])) s.add(sk); return [...s].sort() })
|
||||
const filteredTechs = computed(() => { const q = techSearch.value.trim().toLowerCase(); return q ? (props.techs || []).filter(t => String(t.name || '').toLowerCase().includes(q)) : (props.techs || []) })
|
||||
function selectAllShown () { const s = new Set(selected.value); for (const t of filteredTechs.value) s.add(t.id); selected.value = s }
|
||||
function selectNone () { selected.value = new Set() }
|
||||
function selectBySkill (sk) { const s = new Set(selected.value); for (const t of (props.techs || [])) if ((t.skills || []).includes(sk)) s.add(t.id); selected.value = s }
|
||||
|
||||
function dayHours (d) { if (!d || !d.on) return 0; const [h1, m1] = d.start.split(':').map(Number); const [h2, m2] = d.end.split(':').map(Number); let mn = (h2 * 60 + m2) - (h1 * 60 + m1); if (mn < 0) mn += 1440; return Math.round(mn / 60 * 10) / 10 }
|
||||
const totalDays = () => DAYS.filter(([k]) => sched[k]?.on).length
|
||||
|
|
@ -59,11 +66,24 @@ function submit () {
|
|||
</q-btn-toggle>
|
||||
<!-- Mode LOT : choisir les techniciens à qui appliquer le modèle (tous cochés par défaut) -->
|
||||
<div v-if="bulk">
|
||||
<div class="text-caption text-grey-7 q-mb-xs">Appliquer à ({{ selected.size }}/{{ techs.length }}) — clic pour (dé)cocher :</div>
|
||||
<div class="row q-gutter-xs" style="max-height:96px;overflow:auto">
|
||||
<span v-for="t in techs" :key="t.id" class="wse-tech" :class="{ on: selected.has(t.id) }" @click="toggleTech(t.id)">
|
||||
<div class="row items-center q-mb-xs">
|
||||
<div class="text-caption text-grey-7">Appliquer à <b>{{ selected.size }}</b>/{{ techs.length }}</div>
|
||||
<q-space />
|
||||
<q-btn flat dense no-caps size="sm" color="primary" label="Tout" @click="selectAllShown" />
|
||||
<q-btn flat dense no-caps size="sm" color="grey-7" label="Aucun" @click="selectNone" />
|
||||
</div>
|
||||
<q-input v-model="techSearch" dense outlined clearable placeholder="Chercher un nom…" class="q-mb-xs">
|
||||
<template #prepend><q-icon name="search" size="18px" /></template>
|
||||
</q-input>
|
||||
<div v-if="allSkills.length" class="row q-gutter-xs q-mb-xs items-center">
|
||||
<span class="text-caption text-grey-6">Par compétence :</span>
|
||||
<span v-for="sk in allSkills" :key="sk" class="wse-skill" @click="selectBySkill(sk)"><q-icon name="add" size="11px" />{{ sk }}</span>
|
||||
</div>
|
||||
<div class="row q-gutter-xs" style="max-height:120px;overflow:auto">
|
||||
<span v-for="t in filteredTechs" :key="t.id" class="wse-tech" :class="{ on: selected.has(t.id) }" @click="toggleTech(t.id)">
|
||||
<q-icon :name="selected.has(t.id) ? 'check' : 'add'" size="12px" />{{ t.name }}
|
||||
</span>
|
||||
<div v-if="!filteredTechs.length" class="text-caption text-grey-5 q-pa-xs">Aucun tech pour « {{ techSearch }} »</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Modèles (1 clic remplit le formulaire) -->
|
||||
|
|
@ -113,4 +133,6 @@ function submit () {
|
|||
.wse-repos { color: #94a3b8; font-style: italic; font-size: 12.5px; margin-left: 8px; }
|
||||
.wse-tech { display: inline-flex; align-items: center; gap: 3px; padding: 2px 9px; border: 1.5px solid #cbd5e1; border-radius: 12px; font-size: 12px; font-weight: 600; color: #64748b; background: #fff; cursor: pointer; user-select: none; }
|
||||
.wse-tech.on { border-color: #6366f1; background: #eef2ff; color: #4338ca; }
|
||||
.wse-skill { display: inline-flex; align-items: center; gap: 2px; padding: 2px 8px; border: 1px dashed #94a3b8; border-radius: 12px; font-size: 11px; font-weight: 600; color: #475569; background: #f8fafc; cursor: pointer; user-select: none; }
|
||||
.wse-skill:hover { border-color: #6366f1; color: #4338ca; background: #eef2ff; }
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -44,7 +44,6 @@
|
|||
<q-btn dense flat icon="keyboard_double_arrow_right" @click="navWeek(1)"><q-tooltip>Semaine suivante</q-tooltip></q-btn>
|
||||
</q-btn-group>
|
||||
<q-input dense outlined type="date" v-model="boardDate" style="width:150px"><q-tooltip>{{ boardView === 'kanban' ? 'Jour affiché (mode Jour)' : 'Début de la fenêtre (mode Semaine)' }}</q-tooltip></q-input>
|
||||
<q-select v-show="boardView === 'grid'" dense outlined v-model="days" :options="[{ label: 'Semaine', value: 7 }, { label: '2 sem.', value: 14 }]" style="width:108px" emit-value map-options @update:model-value="onDaysChange"><q-tooltip>Étendue de la grille</q-tooltip></q-select>
|
||||
<q-btn dense flat round icon="undo" :disable="!history.length" @click="undo"><q-tooltip>Annuler (Ctrl+Z)</q-tooltip></q-btn>
|
||||
<q-btn dense flat round icon="redo" :disable="!future.length" @click="redo"><q-tooltip>Rétablir (Ctrl+Shift+Z)</q-tooltip></q-btn>
|
||||
<q-separator vertical class="q-mx-xs" />
|
||||
|
|
@ -2065,7 +2064,7 @@ const unassignedByDay = computed(() => {
|
|||
const dailyStats = ref([])
|
||||
const solverStats = ref(null)
|
||||
const loading = ref(false); const generating = ref(false); const publishing = ref(false); const applying = ref(false)
|
||||
const days = ref(7)
|
||||
const days = ref(14) // 2 semaines par défaut (sélecteur retiré pour éviter les changements d'étendue accidentels)
|
||||
const start = ref(thisMonday()) // défaut = semaine COURANTE (cohérent avec « Auj. ») — pas la semaine suivante
|
||||
const lastWeek = reactive({ start: start.value, days: days.value })
|
||||
const showDemand = ref(false)
|
||||
|
|
@ -2646,7 +2645,7 @@ async function applyWeekPreset (t, dows, min, max) {
|
|||
// ── Génération de quarts HEBDO par tech (modèles + N semaines) — écrit DIRECTEMENT les Shift Assignment (Publié). ──
|
||||
const schedGenOpen = ref(false); const schedGenTechs = ref([])
|
||||
function openSchedGen (t) { schedGenTechs.value = [{ id: t.id, name: t.name }]; skillMenuShown.value = false; schedGenOpen.value = true } // 1 tech
|
||||
function openSchedGenBulk () { schedGenTechs.value = (visibleTechs.value || []).map(t => ({ id: t.id, name: t.name })); schedGenOpen.value = true } // LOT : tous les techs visibles
|
||||
function openSchedGenBulk () { schedGenTechs.value = (visibleTechs.value || []).map(t => ({ id: t.id, name: t.name, skills: t.skills || [] })); schedGenOpen.value = true } // LOT : tous les techs visibles (+ compétences pour filtrer)
|
||||
const _hmNum = (s) => { const [h, m] = String(s || '').split(':').map(Number); return (h || 0) + (m || 0) / 60 }
|
||||
async function onScheduleApply ({ schedule, weeks, techIds, mode }) {
|
||||
const targets = (schedGenTechs.value || []).filter(t => (techIds || []).includes(t.id)); if (!targets.length) return
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user