From 763e63d9c6d4e785350ea1e38cc08888faab5d57 Mon Sep 17 00:00:00 2001 From: Quentin Rouiller Date: Tue, 21 Apr 2026 16:24:24 +0200 Subject: [PATCH] =?UTF-8?q?v5.0.15=20=E2=80=94=20Absences=20partielles=20a?= =?UTF-8?q?ffich=C3=A9es=20comme=20rows=20(gris=20fonc=C3=A9)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- manifest.json | 2 +- viewer.css | 29 +++++++++++++++++----- viewer.js | 66 +++++++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 86 insertions(+), 11 deletions(-) diff --git a/manifest.json b/manifest.json index 7a4052b..1328d98 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 3, "name": "Planification", - "version": "5.0.14", + "version": "5.0.15", "description": "Vue claire et rapide du planning des techniciens EasyVista. Regroupe interventions et réservations par tech, affiche horaires, contact, lieu, catégorie et statut en un coup d'œil.", "permissions": ["activeTab", "scripting", "storage", "tabs", "alarms"], "host_permissions": [ diff --git a/viewer.css b/viewer.css index 564a91f..bc51b2f 100644 --- a/viewer.css +++ b/viewer.css @@ -689,12 +689,9 @@ html, body { .timeline-slot.status-resolved { background: var(--c-resolved); } .timeline-slot.kind-absence { - background: repeating-linear-gradient( - 45deg, - var(--text-faint) 0 6px, - var(--bg-muted) 6px 12px - ); - opacity: 0.6; + /* v5.0.15 : uni gris-noir au lieu de rayé, plus lisible */ + background: #2a2f36; + border-right: 1px solid var(--bg-elevated); } .timeline-slot:hover, @@ -1117,6 +1114,26 @@ html, body { color: var(--c-reservation); font-family: var(--font); letter-spacing: 0.02em; + /* v5.0.15 : étendre le titre sur toute la largeur de la carte pour le + vrai centrage (sinon il n'est centré que dans sa colonne grid) */ + grid-column: 1 / -1; + text-align: center; + padding-left: 62px; /* compense la colonne time (58px + gap) */ + padding-right: 0; +} + +/* v5.0.15 : absence partielle (demi-journée) affichée comme une row */ +.iv-ref-header.is-absence-title { + color: var(--c-absence, #a0a8b2); + font-family: var(--font); + letter-spacing: 0.02em; + grid-column: 1 / -1; + text-align: center; + padding-left: 62px; + padding-right: 0; +} +.intervention-v2.color-absence .intervention-dot { + background: var(--c-absence, #2a2f36); } .iv-reservation-par { font-size: 13px; diff --git a/viewer.js b/viewer.js index 63cafae..083713a 100644 --- a/viewer.js +++ b/viewer.js @@ -131,6 +131,7 @@ function deriveShortTitle(iv) { function deriveColorKey(iv) { if (iv.type === "AL-Reservation") return "reservation"; + if (iv.type === "AL-Absence") return "absence"; // v5.0.15 : couleur noire/gris foncé if (iv.ref && /^I\d/.test(iv.ref)) return "incident"; if (isRollOut(iv)) return "rollout"; if (isRecupAction(iv)) return "recup"; @@ -4412,6 +4413,25 @@ function buildCard(tech, isoDate) { body.appendChild(buildInterventionRow(iv, card)); } + // v5.0.15 : afficher aussi les absences partielles (demi-journée) comme + // des rows, avec le même style que les réservations mais en gris foncé. + // Les absences qui couvrent toute la journée sont déjà traitées plus haut + // (carte "Absent toute la journée") et ne doivent pas être dupliquées ici. + if (!isAbsent) { + const partialAbsences = absenceBlocks.filter(ab => { + if (ab.isPompier) return false; + const s = timeToMinutes(ab.startTime); + const e = timeToMinutes(ab.endTime); + if (s === null || e === null) return false; + return !(s <= DAY_START && e >= DAY_END); + }); + // Trier par heure de début + partialAbsences.sort((a, b) => (a.startTime || "").localeCompare(b.startTime || "")); + for (const ab of partialAbsences) { + body.appendChild(buildInterventionRow(ab, card)); + } + } + card.appendChild(body); return card; } @@ -4773,7 +4793,7 @@ function buildInterventionRow(iv, cardEl) { cardEl._rowIdxCounter = ivIdx + 1; row.dataset.ivIdx = ivIdx; - if (iv.formLink && !iv.ghost) { + if (iv.formLink && !iv.ghost && iv.type !== "AL-Absence") { row.classList.add("clickable"); // v4.1.8 : plus de title au survol (info déjà dans le tooltip en bas) @@ -4811,6 +4831,10 @@ function buildInterventionRow(iv, cardEl) { if (iv.type === "AL-Reservation") { refHeader.textContent = "Réservation"; refHeader.classList.add("is-reservation-title"); + } else if (iv.type === "AL-Absence") { + // v5.0.15 : absence partielle (demi-journée) affichée comme une row + refHeader.textContent = "Absence"; + refHeader.classList.add("is-absence-title"); } else if (iv.ref) { refHeader.textContent = iv.ref; } else { @@ -4819,8 +4843,8 @@ function buildInterventionRow(iv, cardEl) { } row.appendChild(refHeader); - // Check ✓ + bouton copier à droite de la ref (pas pour réservation) - if (statusClass && iv.type !== "AL-Reservation") { + // Check ✓ + bouton copier à droite de la ref (pas pour réservation / absence) + if (statusClass && iv.type !== "AL-Reservation" && iv.type !== "AL-Absence") { const statusEl = document.createElement("div"); statusEl.className = "iv-status-check"; // v4.2.5 : ✓✓ double pour clôturé/résolu (statut officiel EasyVista) @@ -4833,7 +4857,7 @@ function buildInterventionRow(iv, cardEl) { } row.appendChild(statusEl); } - if (iv.ref && iv.type !== "AL-Reservation") { + if (iv.ref && iv.type !== "AL-Reservation" && iv.type !== "AL-Absence") { const copyBtn = document.createElement("button"); copyBtn.className = "intervention-copy"; copyBtn.type = "button"; @@ -4913,6 +4937,40 @@ function buildInterventionRow(iv, cardEl) { return row; } + // v5.0.15 : absence partielle (demi-journée) affichée comme une row au + // même style que les réservations mais en gris foncé, avec le type d'absence + // (Congés, Maladie, Pompier) comme sujet. + if (iv.type === "AL-Absence") { + // Bloc "Par Nom, Prénom" si on a un créateur + if (iv.reservationCreator) { + const parEl = document.createElement("div"); + parEl.className = "iv-reservation-par"; + parEl.textContent = "Par " + iv.reservationCreator; + rightCol.appendChild(parEl); + } + // Type d'absence (Congés, Maladie, Pompier) si dispo dans label + const absenceTypeMatch = (iv.label || "").match(/^([^/]+?)\s*(?:\/|$)/); + const absenceType = absenceTypeMatch ? absenceTypeMatch[1].trim() : null; + if (absenceType) { + const sujetEl = document.createElement("div"); + sujetEl.className = "iv-reservation-sujet"; + sujetEl.textContent = "Type : " + absenceType; + rightCol.appendChild(sujetEl); + } + row.appendChild(rightCol); + + // Tooltip au hover (avec bouton supprimer) + row.addEventListener("mouseenter", (e) => { + showTooltip(e, iv, row); + highlightIntervention(cardEl, ivIdx, true); + }); + row.addEventListener("mouseleave", () => { + hideTooltip(); + highlightIntervention(cardEl, ivIdx, false); + }); + return row; + } + // v4.1.2 : priorité à iv.infobulle (venant du xhr2 = données réelles vérifiées // par le tech sur place) puis fallback sur iv.bulleContact/iv.bulleLieu // (venant de attr1/attr2 = planification initiale, parfois incorrecte).