Version 2.0.1 — Ajustements interface v2

This commit is contained in:
2026-04-16 17:00:00 +02:00
parent d2afbf0dca
commit 8bc26c326f
3 changed files with 138 additions and 66 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"manifest_version": 3, "manifest_version": 3,
"name": "Planning Techniciens — Vue claire", "name": "Planning Techniciens — Vue claire",
"version": "2.0.0", "version": "2.0.1",
"description": "Réaffiche le planning du jour (itsma.vd.ch) avec pompier, absents, tooltips enrichis et thème clair/sombre.", "description": "Réaffiche le planning du jour (itsma.vd.ch) avec pompier, absents, tooltips enrichis et thème clair/sombre.",
"permissions": [ "permissions": [
"activeTab", "activeTab",
+73 -34
View File
@@ -176,10 +176,29 @@ html, body {
} }
.stats { .stats {
padding: 10px 20px 0 20px; display: flex;
flex-wrap: wrap;
align-items: baseline;
gap: 8px;
padding: 12px 20px 4px 20px;
font-size: 13px;
color: var(--text-muted); color: var(--text-muted);
}
.global-stat b {
color: var(--text);
font-weight: 600;
}
.global-stat-main b {
font-size: 16px;
}
.global-stat-sub {
color: var(--text-faint);
font-size: 12px; font-size: 12px;
} }
.global-stat-sep {
color: var(--text-faint);
opacity: 0.5;
}
/* ========================================================================== /* ==========================================================================
Grille de cartes Grille de cartes
@@ -284,19 +303,6 @@ html, body {
background: var(--danger-soft); background: var(--danger-soft);
} }
.timeline-pompier::before {
content: "En pompier toute la journée";
position: absolute;
top: 2px;
right: 14px;
font-size: 10px;
color: var(--danger);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.04em;
opacity: 0.7;
}
.timeline-bar { .timeline-bar {
position: relative; position: relative;
height: 20px; height: 20px;
@@ -387,23 +393,43 @@ html, body {
font-family: var(--mono); font-family: var(--mono);
} }
/* Stats matin / après-midi / total */ /* Stats par carte : total en gros, matin/aprem en secondaire */
.card-stats { .card-stats {
display: flex; display: flex;
gap: 16px; align-items: baseline;
padding: 6px 14px 8px 14px; justify-content: space-between;
padding: 10px 14px;
background: var(--bg-muted);
border-bottom: 1px solid var(--border);
}
.stat-total {
display: flex;
align-items: baseline;
gap: 6px;
}
.stat-total-num {
font-size: 22px;
font-weight: 700;
color: var(--text);
line-height: 1;
}
.stat-total-lbl {
font-size: 12px; font-size: 12px;
color: var(--text-muted); color: var(--text-muted);
border-bottom: 1px solid var(--border);
background: var(--bg-muted);
} }
.card-stats .stat-chunk b { .stat-split {
color: var(--text); display: flex;
align-items: center;
gap: 6px;
font-size: 12px;
color: var(--text-faint);
}
.stat-split-item b {
color: var(--text-muted);
font-weight: 600; font-weight: 600;
} }
.card-stats .stat-chunk.total { .stat-split-sep {
margin-left: auto; opacity: 0.4;
color: var(--text);
} }
/* Note de statut pompier/absent en haut de carte */ /* Note de statut pompier/absent en haut de carte */
@@ -482,31 +508,44 @@ html, body {
min-width: 0; min-width: 0;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 2px; gap: 1px;
} }
.intervention-title { /* La référence S260xxx_xxxxx est mise en avant */
.intervention-refhdr {
font-family: var(--mono);
font-size: 13px; font-size: 13px;
font-weight: 600;
color: var(--text); color: var(--text);
letter-spacing: 0.02em;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.intervention-refhdr.no-ref {
font-family: var(--font);
font-weight: normal;
color: var(--text-faint);
}
/* Titre type d'intervention en secondaire */
.intervention-title {
font-size: 12px;
color: var(--text-muted);
font-weight: 500;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
} }
.intervention-meta { .intervention-meta {
font-size: 12px; font-size: 11px;
color: var(--text-muted); color: var(--text-faint);
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
} }
.intervention-ref {
font-family: var(--mono);
font-size: 11px;
color: var(--text-faint);
}
.intervention-copy { .intervention-copy {
flex-shrink: 0; flex-shrink: 0;
padding: 4px 8px; padding: 4px 8px;
+64 -31
View File
@@ -683,21 +683,34 @@ function ddmmyyyyToDateNum(s) {
} }
function computeStats(techs, targetDate) { function computeStats(techs, targetDate) {
let pompiers = 0, absents = 0, activeInterventions = 0; let pompiers = 0, absents = 0;
let totalInterventions = 0, morning = 0, afternoon = 0;
for (const tech of techs) { for (const tech of techs) {
const isPompier = tech.interventions.some(iv => iv.isPompier); const isPompier = tech.interventions.some(iv => iv.isPompier);
const isAbsent = isTechAbsent(tech, targetDate); const isAbsent = isTechAbsent(tech, targetDate);
if (isPompier) pompiers++; if (isPompier) pompiers++;
if (isAbsent) absents++; if (isAbsent) absents++;
if (!isPompier && !isAbsent) {
activeInterventions += tech.interventions.length; // Compter TOUTES les vraies interventions (même celles des pompiers)
const real = tech.interventions.filter(iv =>
iv.type !== "AL-Absence" && !iv.isPompier
);
totalInterventions += real.length;
for (const iv of real) {
const s = timeToMinutes(iv.startTime);
if (s !== null && s < 12 * 60) morning++;
else if (s !== null) afternoon++;
} }
} }
return { return {
totalTechs: techs.length, totalTechs: techs.length,
pompiers, pompiers,
absents, absents,
activeInterventions totalInterventions,
morning,
afternoon
}; };
} }
@@ -768,11 +781,16 @@ function renderCaptureInfo(data) {
function renderStats(data) { function renderStats(data) {
const el = document.getElementById("stats"); const el = document.getElementById("stats");
const s = data.stats; const s = data.stats;
el.textContent = el.innerHTML = `
`${s.totalTechs} techs · ` + <span class="global-stat global-stat-main"><b>${s.totalInterventions}</b> intervention${s.totalInterventions > 1 ? "s" : ""}</span>
`${s.pompiers} pompier${s.pompiers > 1 ? "s" : ""} · ` + <span class="global-stat global-stat-sub">(${s.morning} matin · ${s.afternoon} après-midi)</span>
`${s.absents} absent${s.absents > 1 ? "s" : ""} · ` + <span class="global-stat-sep">·</span>
`${s.activeInterventions} intervention${s.activeInterventions > 1 ? "s" : ""} active${s.activeInterventions > 1 ? "s" : ""}`; <span class="global-stat"><b>${s.totalTechs}</b> techs</span>
<span class="global-stat-sep">·</span>
<span class="global-stat"><b>${s.pompiers}</b> pompier${s.pompiers > 1 ? "s" : ""}</span>
<span class="global-stat-sep">·</span>
<span class="global-stat"><b>${s.absents}</b> absent${s.absents > 1 ? "s" : ""}</span>
`;
el.classList.remove("hidden"); el.classList.remove("hidden");
} }
@@ -817,19 +835,20 @@ function buildCard(tech, targetDate) {
nameEl.textContent = tech.name; nameEl.textContent = tech.name;
header.appendChild(nameEl); header.appendChild(nameEl);
const badge = document.createElement("div"); // Badge de statut (pompier/absent uniquement). Les techs actifs n'ont pas de badge
badge.className = "card-tech-badge"; // — leurs stats complètes sont juste en dessous.
if (isPompier) { if (isPompier || isAbsent) {
badge.classList.add("badge-pompier"); const badge = document.createElement("div");
badge.textContent = "Pompier" + (realInterventions.length > 0 ? ` · ${realInterventions.length} int.` : ""); badge.className = "card-tech-badge";
} else if (isAbsent) { if (isPompier) {
badge.classList.add("badge-absent"); badge.classList.add("badge-pompier");
badge.textContent = "Absent" + (realInterventions.length > 0 ? ` · ${realInterventions.length} int.` : ""); badge.textContent = "Pompier";
} else { } else {
badge.classList.add("badge-count"); badge.classList.add("badge-absent");
badge.textContent = realInterventions.length + " int."; badge.textContent = "Absent";
}
header.appendChild(badge);
} }
header.appendChild(badge);
card.appendChild(header); card.appendChild(header);
// --- Body --- // --- Body ---
@@ -899,14 +918,20 @@ function buildCard(tech, targetDate) {
// 4. Timeline (pour pompier aussi, avec fond spécial ; pour absent si interventions) // 4. Timeline (pour pompier aussi, avec fond spécial ; pour absent si interventions)
body.appendChild(buildTimeline(realInterventions, pompierBlocks, absenceBlocks, card, isPompier, isAbsent)); body.appendChild(buildTimeline(realInterventions, pompierBlocks, absenceBlocks, card, isPompier, isAbsent));
// 5. Mini-stats matin/après-midi // 5. Stats matin/après-midi (total bien mis en avant)
if (realInterventions.length > 0) { if (realInterventions.length > 0) {
const stats = document.createElement("div"); const stats = document.createElement("div");
stats.className = "card-stats"; stats.className = "card-stats";
stats.innerHTML = ` stats.innerHTML = `
<span class="stat-chunk"><b>${morning}</b> matin</span> <div class="stat-total">
<span class="stat-chunk"><b>${afternoon}</b> après-midi</span> <span class="stat-total-num">${realInterventions.length}</span>
<span class="stat-chunk total"><b>${realInterventions.length}</b> total</span> <span class="stat-total-lbl">intervention${realInterventions.length > 1 ? "s" : ""}</span>
</div>
<div class="stat-split">
<span class="stat-split-item"><b>${morning}</b> matin</span>
<span class="stat-split-sep">·</span>
<span class="stat-split-item"><b>${afternoon}</b> après-midi</span>
</div>
`; `;
body.appendChild(stats); body.appendChild(stats);
} }
@@ -951,25 +976,33 @@ function buildInterventionRow(iv, cardEl) {
} }
row.appendChild(timeEl); row.appendChild(timeEl);
// Contenu (titre + meta) // Contenu (ref en avant + titre + meta)
const content = document.createElement("div"); const content = document.createElement("div");
content.className = "intervention-content"; content.className = "intervention-content";
// Ligne 1 : la référence S260xxx_xxxxx (en évidence)
const refHeader = document.createElement("div");
refHeader.className = "intervention-refhdr";
if (iv.ref) {
refHeader.textContent = iv.ref;
} else {
refHeader.textContent = "—";
refHeader.classList.add("no-ref");
}
content.appendChild(refHeader);
// Ligne 2 : type (livraison / récupération / etc.) en plus discret
const title = document.createElement("div"); const title = document.createElement("div");
title.className = "intervention-title"; title.className = "intervention-title";
title.textContent = shortTitle(iv); title.textContent = shortTitle(iv);
content.appendChild(title); content.appendChild(title);
// Ligne 3 : contact / lieu
const meta = document.createElement("div"); const meta = document.createElement("div");
meta.className = "intervention-meta"; meta.className = "intervention-meta";
meta.textContent = shortMeta(iv); meta.textContent = shortMeta(iv);
content.appendChild(meta); content.appendChild(meta);
const refEl = document.createElement("div");
refEl.className = "intervention-ref";
refEl.textContent = iv.ref || "";
content.appendChild(refEl);
row.appendChild(content); row.appendChild(content);
// Bouton copier // Bouton copier