forked from FroSteel/Planification
Version 2.0.1 — Ajustements interface v2
This commit is contained in:
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"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.",
|
||||
"permissions": [
|
||||
"activeTab",
|
||||
|
||||
+73
-34
@@ -176,10 +176,29 @@ html, body {
|
||||
}
|
||||
|
||||
.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);
|
||||
}
|
||||
.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;
|
||||
}
|
||||
.global-stat-sep {
|
||||
color: var(--text-faint);
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Grille de cartes
|
||||
@@ -284,19 +303,6 @@ html, body {
|
||||
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 {
|
||||
position: relative;
|
||||
height: 20px;
|
||||
@@ -387,23 +393,43 @@ html, body {
|
||||
font-family: var(--mono);
|
||||
}
|
||||
|
||||
/* Stats matin / après-midi / total */
|
||||
/* Stats par carte : total en gros, matin/aprem en secondaire */
|
||||
.card-stats {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
padding: 6px 14px 8px 14px;
|
||||
align-items: baseline;
|
||||
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;
|
||||
color: var(--text-muted);
|
||||
border-bottom: 1px solid var(--border);
|
||||
background: var(--bg-muted);
|
||||
}
|
||||
.card-stats .stat-chunk b {
|
||||
color: var(--text);
|
||||
.stat-split {
|
||||
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;
|
||||
}
|
||||
.card-stats .stat-chunk.total {
|
||||
margin-left: auto;
|
||||
color: var(--text);
|
||||
.stat-split-sep {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
/* Note de statut pompier/absent en haut de carte */
|
||||
@@ -482,31 +508,44 @@ html, body {
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
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-weight: 600;
|
||||
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;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.intervention-meta {
|
||||
font-size: 12px;
|
||||
color: var(--text-muted);
|
||||
font-size: 11px;
|
||||
color: var(--text-faint);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.intervention-ref {
|
||||
font-family: var(--mono);
|
||||
font-size: 11px;
|
||||
color: var(--text-faint);
|
||||
}
|
||||
|
||||
.intervention-copy {
|
||||
flex-shrink: 0;
|
||||
padding: 4px 8px;
|
||||
|
||||
@@ -683,21 +683,34 @@ function ddmmyyyyToDateNum(s) {
|
||||
}
|
||||
|
||||
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) {
|
||||
const isPompier = tech.interventions.some(iv => iv.isPompier);
|
||||
const isAbsent = isTechAbsent(tech, targetDate);
|
||||
if (isPompier) pompiers++;
|
||||
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 {
|
||||
totalTechs: techs.length,
|
||||
pompiers,
|
||||
absents,
|
||||
activeInterventions
|
||||
totalInterventions,
|
||||
morning,
|
||||
afternoon
|
||||
};
|
||||
}
|
||||
|
||||
@@ -768,11 +781,16 @@ function renderCaptureInfo(data) {
|
||||
function renderStats(data) {
|
||||
const el = document.getElementById("stats");
|
||||
const s = data.stats;
|
||||
el.textContent =
|
||||
`${s.totalTechs} techs · ` +
|
||||
`${s.pompiers} pompier${s.pompiers > 1 ? "s" : ""} · ` +
|
||||
`${s.absents} absent${s.absents > 1 ? "s" : ""} · ` +
|
||||
`${s.activeInterventions} intervention${s.activeInterventions > 1 ? "s" : ""} active${s.activeInterventions > 1 ? "s" : ""}`;
|
||||
el.innerHTML = `
|
||||
<span class="global-stat global-stat-main"><b>${s.totalInterventions}</b> intervention${s.totalInterventions > 1 ? "s" : ""}</span>
|
||||
<span class="global-stat global-stat-sub">(${s.morning} matin · ${s.afternoon} après-midi)</span>
|
||||
<span class="global-stat-sep">·</span>
|
||||
<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");
|
||||
}
|
||||
|
||||
@@ -817,19 +835,20 @@ function buildCard(tech, targetDate) {
|
||||
nameEl.textContent = tech.name;
|
||||
header.appendChild(nameEl);
|
||||
|
||||
const badge = document.createElement("div");
|
||||
badge.className = "card-tech-badge";
|
||||
if (isPompier) {
|
||||
badge.classList.add("badge-pompier");
|
||||
badge.textContent = "Pompier" + (realInterventions.length > 0 ? ` · ${realInterventions.length} int.` : "");
|
||||
} else if (isAbsent) {
|
||||
badge.classList.add("badge-absent");
|
||||
badge.textContent = "Absent" + (realInterventions.length > 0 ? ` · ${realInterventions.length} int.` : "");
|
||||
} else {
|
||||
badge.classList.add("badge-count");
|
||||
badge.textContent = realInterventions.length + " int.";
|
||||
// Badge de statut (pompier/absent uniquement). Les techs actifs n'ont pas de badge
|
||||
// — leurs stats complètes sont juste en dessous.
|
||||
if (isPompier || isAbsent) {
|
||||
const badge = document.createElement("div");
|
||||
badge.className = "card-tech-badge";
|
||||
if (isPompier) {
|
||||
badge.classList.add("badge-pompier");
|
||||
badge.textContent = "Pompier";
|
||||
} else {
|
||||
badge.classList.add("badge-absent");
|
||||
badge.textContent = "Absent";
|
||||
}
|
||||
header.appendChild(badge);
|
||||
}
|
||||
header.appendChild(badge);
|
||||
card.appendChild(header);
|
||||
|
||||
// --- Body ---
|
||||
@@ -899,14 +918,20 @@ function buildCard(tech, targetDate) {
|
||||
// 4. Timeline (pour pompier aussi, avec fond spécial ; pour absent si interventions)
|
||||
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) {
|
||||
const stats = document.createElement("div");
|
||||
stats.className = "card-stats";
|
||||
stats.innerHTML = `
|
||||
<span class="stat-chunk"><b>${morning}</b> matin</span>
|
||||
<span class="stat-chunk"><b>${afternoon}</b> après-midi</span>
|
||||
<span class="stat-chunk total"><b>${realInterventions.length}</b> total</span>
|
||||
<div class="stat-total">
|
||||
<span class="stat-total-num">${realInterventions.length}</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);
|
||||
}
|
||||
@@ -951,25 +976,33 @@ function buildInterventionRow(iv, cardEl) {
|
||||
}
|
||||
row.appendChild(timeEl);
|
||||
|
||||
// Contenu (titre + meta)
|
||||
// Contenu (ref en avant + titre + meta)
|
||||
const content = document.createElement("div");
|
||||
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");
|
||||
title.className = "intervention-title";
|
||||
title.textContent = shortTitle(iv);
|
||||
content.appendChild(title);
|
||||
|
||||
// Ligne 3 : contact / lieu
|
||||
const meta = document.createElement("div");
|
||||
meta.className = "intervention-meta";
|
||||
meta.textContent = shortMeta(iv);
|
||||
content.appendChild(meta);
|
||||
|
||||
const refEl = document.createElement("div");
|
||||
refEl.className = "intervention-ref";
|
||||
refEl.textContent = iv.ref || "";
|
||||
content.appendChild(refEl);
|
||||
|
||||
row.appendChild(content);
|
||||
|
||||
// Bouton copier
|
||||
|
||||
Reference in New Issue
Block a user