Planification: cellule = barre de dispo seule sur une échelle de temps (règle horaire en-tête)
- En-tête de chaque jour: règle horaire (graduations alignées sur l'axe adaptatif, ex 8/12/16/20). - Cellule réduite à LA barre de dispo (dégradé matin→soir, occupé assombri, garde hachuré) — plus de texte d'intervalle : on lit la position contre la règle, détails au survol. - Barre un peu plus haute (11px). Retrait cellLabel/cell-int/cell-chips. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
738d315785
commit
d7867e2a62
|
|
@ -109,6 +109,7 @@
|
|||
<div class="dow">{{ d.dow }}</div><div class="dnum">{{ d.dnum }}</div>
|
||||
<q-badge v-if="gapByDay[d.iso]" color="red" floating style="top:2px;right:2px">{{ gapByDay[d.iso] }}</q-badge>
|
||||
<div class="hol-toggle" :class="{ on: isHoliday(d.iso) }" @click.stop="toggleHoliday(d.iso)"><q-tooltip>Marquer férié</q-tooltip>F</div>
|
||||
<div class="hdr-ruler"><span v-for="tk in axisTicks" :key="tk.h" class="tick" :style="{ left: tk.left }">{{ tk.h }}</span></div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
|
@ -123,9 +124,6 @@
|
|||
</td>
|
||||
<td v-for="(d, di) in dayList" :key="d.iso" class="cell" :class="{ weekend: d.weekend, holiday: isHoliday(d.iso), sel: isSelected(t.id, d.iso), dirty: isCellDirty(t.id, d.iso) }" @mousedown="onDown(ti, di, $event)" @mouseenter="onEnter(ti, di)" @click="onCellClick(t, d, $event, ti, di)">
|
||||
<template v-if="cellsOf(t.id, d.iso).length">
|
||||
<div class="cell-chips">
|
||||
<span v-if="cellOcc(t.id, d.iso)" class="cell-int">{{ cellLabel(t.id, d.iso) }}</span>
|
||||
</div>
|
||||
<div v-if="cellOcc(t.id, d.iso)" class="tl">
|
||||
<div v-for="(b, bi) in cellBands(t.id, d.iso)" :key="'b' + bi" class="tl-shift" :class="{ oncall: b.oncall }" :style="{ left: b.left, width: b.width, background: b.bg || undefined }"></div>
|
||||
<div v-for="(b, bi) in cellOcc(t.id, d.iso).blocks" :key="'j' + bi" class="tl-blk" :style="blockStyle(b, cellOcc(t.id, d.iso).pct)"></div>
|
||||
|
|
@ -396,6 +394,13 @@ const axisBounds = computed(() => {
|
|||
lo = Math.max(0, Math.floor(lo)); hi = Math.min(24, Math.ceil(hi)); if (hi - lo < 4) hi = Math.min(24, lo + 4)
|
||||
return { min: lo, max: hi }
|
||||
})
|
||||
// Graduations horaires pour la règle d'en-tête (alignées sur l'axe adaptatif)
|
||||
const axisTicks = computed(() => {
|
||||
const b = axisBounds.value; const span = (b.max - b.min) || 24
|
||||
const step = span > 13 ? 4 : (span > 7 ? 3 : 2); const out = []
|
||||
for (let h = Math.ceil(b.min / step) * step; h <= b.max; h += step) out.push({ h, left: ((h - b.min) / span * 100) + '%' })
|
||||
return out
|
||||
})
|
||||
function pos (s, e) { const b = axisBounds.value; const span = (b.max - b.min) || 24; const L = Math.max(0, (s - b.min) / span * 100); const R = Math.min(100, (e - b.min) / span * 100); return { left: L + '%', width: Math.max(1.5, R - L) + '%' } }
|
||||
// Couleur selon l'heure : bleu pâle le matin → violet le soir
|
||||
function todColor (h) { const t = Math.max(0, Math.min(1, (h - 6) / 15)); return 'hsl(' + Math.round(210 + t * 60) + ',' + Math.round(62 - t * 6) + '%,' + Math.round(83 - t * 22) + '%)' }
|
||||
|
|
@ -416,8 +421,6 @@ function cellBands (techId, iso) {
|
|||
function blockStyle (blk, pct) { return { ...pos(blk.s, Math.min(blk.e, 24)), background: (pct != null && pct >= 100) ? 'rgba(229,57,53,.6)' : 'rgba(0,0,0,.28)' } }
|
||||
// Fenêtre des shifts (garde=true → seulement les quarts de garde ; garde=false → réguliers)
|
||||
function winOf (techId, iso, garde) { let s = Infinity; let e = -Infinity; for (const a of cellsOf(techId, iso)) { const t = tplByName.value[a.shift]; if (!t || (!!t.on_call) !== garde) continue; const st = hToNum(t.start_time); const en = hToNum(t.end_time); if (st != null) s = Math.min(s, st); if (en != null) e = Math.max(e, en) } return isFinite(s) ? { s, e } : null }
|
||||
// Libellé inline = intervalle début–fin seulement (pas d'icône : le timeline + l'heure suffisent)
|
||||
function cellLabel (techId, iso) { const w = winOf(techId, iso, false) || winOf(techId, iso, true); return w ? (fmtH(w.s) + '–' + fmtH(w.e)) : '' }
|
||||
const occCells = computed(() => {
|
||||
const m = {}; const ct = cellsByTechDay.value
|
||||
for (const techId in ct) for (const iso in ct[techId]) {
|
||||
|
|
@ -671,9 +674,10 @@ th.clk, td.clk { cursor: pointer; }
|
|||
.cell-dirty-demo { display: inline-block; min-width: 18px; padding: 0 5px; border-radius: 4px; font-weight: 700; font-size: 11px; background: #1976d2; color: #fff; box-shadow: inset 0 0 0 2px #ff9800; }
|
||||
.ch-h { opacity: .7; font-weight: 400; font-size: 9px; margin-left: 1px; }
|
||||
.free { color: #ccc; }
|
||||
.cell-chips { line-height: 1; white-space: nowrap; }
|
||||
.cell-int { font-size: 9px; color: #555; font-weight: 600; margin-left: 3px; white-space: nowrap; }
|
||||
.tl { position: relative; height: 8px; min-width: 58px; background: #f1f3f5; border-radius: 2px; margin-top: 3px; overflow: hidden; }
|
||||
.hdr-ruler { position: relative; height: 11px; margin-top: 3px; }
|
||||
.hdr-ruler .tick { position: absolute; top: 2px; transform: translateX(-50%); font-size: 8px; color: #aab; line-height: 1; font-weight: 400; }
|
||||
.hdr-ruler .tick::before { content: ''; position: absolute; top: -3px; left: 50%; width: 1px; height: 2px; background: #d0d0d8; }
|
||||
.tl { position: relative; height: 11px; min-width: 64px; background: #f1f3f5; border-radius: 2px; margin: 2px 0; overflow: hidden; }
|
||||
.tl-shift { position: absolute; top: 0; bottom: 0; background: #ccd2d8; border-radius: 1px; } /* fenêtre dispo = neutre */
|
||||
.tl-shift.oncall { background: repeating-linear-gradient(45deg, #d7ccc8 0, #d7ccc8 2px, transparent 2px, transparent 4px); box-shadow: inset 0 0 0 1px #a1887f; } /* garde = hachuré (réserve, non offrable) */
|
||||
.tl-blk { position: absolute; top: 0; bottom: 0; border-radius: 1px; } /* occupé = assombrit la dispo */
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user