auto-sync via WEVIA git_sync_all intent 2026-04-20T03:38:46+02:00
Some checks failed
WEVAL NonReg / nonreg (push) Has been cancelled
Some checks failed
WEVAL NonReg / nonreg (push) Has been cancelled
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"ok": true,
|
||||
"version": "V83-business-kpi",
|
||||
"ts": "2026-04-20T01:36:20+00:00",
|
||||
"ts": "2026-04-20T01:38:27+00:00",
|
||||
"summary": {
|
||||
"total_categories": 7,
|
||||
"total_kpis": 56,
|
||||
|
||||
@@ -208,7 +208,27 @@ body{background:var(--bg);color:var(--fg);font-family:"DM Sans","Inter",sans-ser
|
||||
} catch(e) { console.error("V71 fetch fail", e); return; }
|
||||
|
||||
const catalog = full.catalog || {};
|
||||
const summary = (full.summary || {});
|
||||
// V73 FIX - action=full does NOT return summary, compute from catalog
|
||||
let summary = full.summary || {};
|
||||
if (!summary.ok) {
|
||||
// Fetch summary separately as fallback
|
||||
try {
|
||||
const rS = await fetch("/api/wevia-v83-business-kpi.php?action=summary",{cache:"no-store"});
|
||||
const sJ = await rS.json();
|
||||
summary = sJ.summary || {};
|
||||
} catch(e) { console.warn("[V71] summary fetch fail", e); }
|
||||
}
|
||||
// Always recompute from catalog (source of truth) if still missing
|
||||
if (!summary.ok) {
|
||||
summary = {ok:0, warn:0, fail:0, wire_needed:0, total_kpis:0, data_completeness_pct:0};
|
||||
for (const c of Object.values(catalog)) {
|
||||
for (const k of (c.kpis||[])) {
|
||||
summary.total_kpis++;
|
||||
if (k.status && summary[k.status] !== undefined) summary[k.status]++;
|
||||
}
|
||||
}
|
||||
summary.data_completeness_pct = summary.total_kpis ? Math.round((summary.ok/summary.total_kpis)*1000)/10 : 0;
|
||||
}
|
||||
|
||||
// ============= Chart 1: Status doughnut =============
|
||||
const counts = {ok: summary.ok||0, warn: summary.warn||0, fail: summary.fail||0, wire_needed: summary.wire_needed||0};
|
||||
@@ -252,18 +272,30 @@ body{background:var(--bg);color:var(--fg);font-family:"DM Sans","Inter",sans-ser
|
||||
const allKpis = [];
|
||||
for (const c of Object.values(catalog)) {
|
||||
for (const k of (c.kpis||[])) {
|
||||
if (typeof k.value === "number" && typeof k.target === "number" && k.target > 0 && k.value > 0) {
|
||||
allKpis.push({label: k.label, value: k.value, target: k.target, unit: k.unit, pct: Math.min(100, (k.value/k.target)*100)});
|
||||
if (typeof k.value === "number" && typeof k.target === "number" && k.target > 0) {
|
||||
// V73: show actual pct (can be <100 for below-target, cap at 150 visually for over-performers)
|
||||
const rawPct = (k.value / k.target) * 100;
|
||||
allKpis.push({label: k.label, value: k.value, target: k.target, unit: k.unit, pct: Math.min(150, rawPct), rawPct: Math.round(rawPct), status: k.status});
|
||||
}
|
||||
}
|
||||
}
|
||||
allKpis.sort((a,b) => b.pct - a.pct);
|
||||
// V73: show mix of below/on/above target for insight
|
||||
allKpis.sort((a,b) => {
|
||||
// Prioritize variance from 100 (show extremes)
|
||||
return Math.abs(b.pct - 100) - Math.abs(a.pct - 100);
|
||||
});
|
||||
const top8 = allKpis.slice(0, 8);
|
||||
const barColor = (pct, status) => {
|
||||
if (status === "fail" || pct < 50) return "linear-gradient(90deg,#fc8181,#f6ad55)";
|
||||
if (status === "warn" || pct < 90) return "linear-gradient(90deg,#f6ad55,#ecc94b)";
|
||||
if (pct > 120) return "linear-gradient(90deg,#48bb78,#6c9ef8)";
|
||||
return "linear-gradient(90deg,#48bb78,#38b2ac)";
|
||||
};
|
||||
document.getElementById("v71-top-bars").innerHTML = top8.map(k => `
|
||||
<div class="v71-bar-row">
|
||||
<div class="l" title="${k.label}">${k.label.substring(0,22)}</div>
|
||||
<div class="bar-wrap"><div class="bar" style="width:${k.pct}%"></div></div>
|
||||
<div class="v">${Math.round(k.pct)}%</div>
|
||||
<div class="v71-bar-row" title="${k.label}: ${k.value.toLocaleString("fr-FR")} / target ${k.target.toLocaleString("fr-FR")} ${k.unit}">
|
||||
<div class="l">${k.label.substring(0,22)}</div>
|
||||
<div class="bar-wrap"><div class="bar" style="width:${Math.min(100, k.pct)}%;background:${barColor(k.pct, k.status)}"></div></div>
|
||||
<div class="v">${k.rawPct}%</div>
|
||||
</div>`).join("");
|
||||
|
||||
// ============= Per-KPI sparklines (deterministic pseudo-trend) =============
|
||||
|
||||
Reference in New Issue
Block a user