diff --git a/apps/ops/src/pages/PlanificationPage.vue b/apps/ops/src/pages/PlanificationPage.vue
index aece4ef..61dd7ed 100644
--- a/apps/ops/src/pages/PlanificationPage.vue
+++ b/apps/ops/src/pages/PlanificationPage.vue
@@ -109,6 +109,7 @@
{{ d.dow }}
{{ d.dnum }}
{{ gapByDay[d.iso] }}
Marquer fériéF
+ {{ tk.h }}
@@ -123,9 +124,6 @@
-
- {{ cellLabel(t.id, d.iso) }}
-
@@ -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 */
|