103 lines
12 KiB
HTML
103 lines
12 KiB
HTML
<!DOCTYPE html><html lang="fr"><head><meta charset="UTF-8"><title>Lean 6σ Dashboard — WEVIA EM</title><style>
|
||
*{box-sizing:border-box;margin:0;padding:0}body{font-family:-apple-system,sans-serif;background:#0a0e1a;color:#e2e8f0;padding:20px}
|
||
.hd{background:linear-gradient(135deg,#dc2626,#991b1b);padding:22px 28px;border-radius:12px;margin-bottom:20px}
|
||
.hd h1{color:white;font-size:26px}.hd .sub{color:rgba(255,255,255,.85);margin-top:6px;font-size:13px}
|
||
.maturity{display:grid;grid-template-columns:1fr auto;gap:20px;align-items:center;background:linear-gradient(135deg,#7c2d12,#431407);padding:20px;border-radius:12px;margin-bottom:20px}
|
||
.maturity .label{font-size:13px;color:#fcd34d}
|
||
.maturity .score{font-size:56px;font-weight:900;color:#fbbf24;font-family:monospace}
|
||
.maturity .bar{height:8px;background:#1e293b;border-radius:4px;overflow:hidden;margin-top:8px}
|
||
.maturity .bar-fill{height:100%;background:linear-gradient(90deg,#dc2626,#fbbf24,#22c55e);transition:width .5s}
|
||
.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:14px;margin-bottom:20px}
|
||
.card{background:#111827;border:1px solid #1e293b;border-radius:10px;padding:16px;position:relative;transition:all .2s}
|
||
.card:hover{border-color:#dc2626;transform:translateY(-2px)}
|
||
.card h3{font-size:13px;color:#fbbf24;text-transform:uppercase;letter-spacing:1px;margin-bottom:8px}
|
||
.card .n{font-size:32px;font-weight:800;color:#e2e8f0;font-family:monospace}
|
||
.card .unit{font-size:14px;color:#64748b;margin-left:4px}
|
||
.card .l{font-size:11px;color:#94a3b8;margin-top:6px}
|
||
.card .icon{position:absolute;top:16px;right:16px;font-size:22px;opacity:.7}
|
||
.section{margin-top:28px}
|
||
.section h2{color:#fbbf24;font-size:16px;margin-bottom:12px;border-bottom:1px solid #1e293b;padding-bottom:6px}
|
||
table{width:100%;border-collapse:collapse;background:#111827;border-radius:8px;overflow:hidden}
|
||
th{background:#1e293b;color:#fbbf24;text-align:left;padding:10px;font-size:11px;text-transform:uppercase;letter-spacing:1px}
|
||
td{padding:10px;border-top:1px solid #1e293b;font-size:12px}
|
||
.badge{padding:2px 8px;border-radius:4px;font-size:10px;font-weight:600}
|
||
.sev-5{background:rgba(220,38,38,.2);color:#f87171}
|
||
.sev-4{background:rgba(234,88,12,.2);color:#fb923c}
|
||
.sev-3{background:rgba(234,179,8,.2);color:#facc15}
|
||
.sev-2{background:rgba(34,197,94,.2);color:#4ade80}
|
||
.sev-1{background:rgba(59,130,246,.2);color:#60a5fa}
|
||
.status-completed{background:rgba(34,197,94,.2);color:#22c55e}
|
||
.status-in-progress{background:rgba(234,179,8,.2);color:#facc15}
|
||
.status-identified{background:rgba(59,130,246,.2);color:#60a5fa}
|
||
.status-analyzed{background:rgba(168,85,247,.2);color:#c084fc}
|
||
.status-fixed{background:rgba(34,197,94,.2);color:#22c55e}
|
||
.status-fixing{background:rgba(234,179,8,.2);color:#facc15}
|
||
.status-planned{background:rgba(107,114,128,.2);color:#9ca3af}
|
||
.andon-red{color:#f87171}.andon-yellow{color:#facc15}.andon-green{color:#4ade80}
|
||
.tabs{display:flex;gap:6px;margin:20px 0;border-bottom:1px solid #1e293b;overflow-x:auto}
|
||
.tab{padding:10px 16px;cursor:pointer;border-bottom:3px solid transparent;font-size:12px;font-weight:600;color:#94a3b8;white-space:nowrap}
|
||
.tab.active{border-color:#fbbf24;color:#fbbf24}
|
||
.panel{display:none}.panel.active{display:block}
|
||
.tenant-sel{display:flex;gap:10px;align-items:center;margin-bottom:16px}
|
||
.tenant-sel select{padding:8px;background:#111827;border:1px solid #1e293b;color:#e2e8f0;border-radius:6px}
|
||
</style></head><body>
|
||
<div class="hd"><h1>🎯 Lean 6σ Dashboard</h1><div class="sub">Muda · Poka-Yoke · Kaizen · Gemba · PDCA · Andon · 5S · A3 — pilotage opérationnel WEVIA EM</div></div>
|
||
<div class="tenant-sel"><label>Tenant:</label><select id="tenant" onchange="reload()"></select></div>
|
||
<div class="maturity">
|
||
<div>
|
||
<div class="label">MATURITY SCORE (Lean 6σ global)</div>
|
||
<div class="bar"><div class="bar-fill" id="mat-bar" style="width:0%"></div></div>
|
||
<div style="font-size:11px;color:#fcd34d;margin-top:6px">Composé de: 5S (30%) + Poka-Yoke efficiency (30%) + Kaizen volume (20%) + Gemba frequency (20%)</div>
|
||
</div>
|
||
<div class="score" id="mat-score">-</div>
|
||
</div>
|
||
<div class="grid" id="stats">Chargement...</div>
|
||
<div class="tabs">
|
||
<div class="tab active" onclick="swt(event,'muda')">🗑️ Muda</div>
|
||
<div class="tab" onclick="swt(event,'poka')">🛡️ Poka-Yoke</div>
|
||
<div class="tab" onclick="swt(event,'kaizen')">📈 Kaizen</div>
|
||
<div class="tab" onclick="swt(event,'gemba')">🚶 Gemba</div>
|
||
<div class="tab" onclick="swt(event,'pdca')">🔄 PDCA</div>
|
||
<div class="tab" onclick="swt(event,'andon')">🚨 Andon</div>
|
||
<div class="tab" onclick="swt(event,'5s')">🧹 5S</div>
|
||
<div class="tab" onclick="swt(event,'a3')">📋 A3</div>
|
||
</div>
|
||
<div id="panel-muda" class="panel active"><table><thead><tr><th>Type</th><th>Sev</th><th>Description</th><th>VS</th><th>Impact €</th><th>Status</th></tr></thead><tbody id="muda-body"></tbody></table></div>
|
||
<div id="panel-poka" class="panel"><table><thead><tr><th>Process</th><th>Device</th><th>Mécanisme</th><th>Validation</th><th>Efficacité</th></tr></thead><tbody id="poka-body"></tbody></table></div>
|
||
<div id="panel-kaizen" class="panel"><table><thead><tr><th>Titre</th><th>Dept</th><th>Problème</th><th>Savings €</th><th>Savings h</th><th>Durée</th><th>Status</th></tr></thead><tbody id="kaizen-body"></tbody></table></div>
|
||
<div id="panel-gemba" class="panel"><table><thead><tr><th>Date</th><th>Location</th><th>Walker</th><th>Observations</th><th>Actions</th><th>Muda</th><th>Durée</th></tr></thead><tbody id="gemba-body"></tbody></table></div>
|
||
<div id="panel-pdca" class="panel"><table><thead><tr><th>Titre</th><th>Phase</th><th>KPI</th><th>Baseline</th><th>Target</th><th>Actual</th><th>Gap</th></tr></thead><tbody id="pdca-body"></tbody></table></div>
|
||
<div id="panel-andon" class="panel"><table><thead><tr><th>Station</th><th>Sev</th><th>Message</th><th>Status</th><th>Résolution</th><th>Time</th></tr></thead><tbody id="andon-body"></tbody></table></div>
|
||
<div id="panel-5s" class="panel"><table><thead><tr><th>Area</th><th>Seiri</th><th>Seiton</th><th>Seiso</th><th>Seiketsu</th><th>Shitsuke</th><th>Total /25</th><th>Auditeur</th></tr></thead><tbody id="fives-body"></tbody></table></div>
|
||
<div id="panel-a3" class="panel"><table><thead><tr><th>Titre</th><th>Status</th><th>Background</th><th>Gap Analysis</th><th>Root Causes</th><th>Owner</th></tr></thead><tbody id="a3-body"></tbody></table></div>
|
||
<script>
|
||
const T=()=>document.getElementById('tenant').value||'weval';
|
||
async function loadTenants(){const r=await fetch('/api/em/tenant');const d=await r.json();const s=document.getElementById('tenant');s.innerHTML=(d.tenants||[]).map(t=>`<option value="${t.tenant_id}">${t.tenant_id} — ${t.name}</option>`).join('')}
|
||
function swt(e,p){document.querySelectorAll('.tab').forEach(x=>x.classList.remove('active'));e.target.classList.add('active');document.querySelectorAll('.panel').forEach(x=>x.classList.remove('active'));document.getElementById('panel-'+p).classList.add('active')}
|
||
async function loadDash(){
|
||
const d=await fetch(`/api/em/lean6sigma-dashboard?tenant=${T()}`).then(r=>r.json());
|
||
const m=d.maturity_score||0;
|
||
document.getElementById('mat-score').textContent=m;
|
||
document.getElementById('mat-bar').style.width=m+'%';
|
||
document.getElementById('stats').innerHTML=`
|
||
<div class="card"><div class="icon">🗑️</div><h3>Muda</h3><div class="n">${d.muda?.count||0}</div><div class="l">${(d.muda?.impact_euro||0).toLocaleString()}€ impact · ${d.muda?.impact_hours||0}h</div></div>
|
||
<div class="card"><div class="icon">🛡️</div><h3>Poka-Yoke</h3><div class="n">${d.poka_yoke?.count||0}</div><div class="l">${d.poka_yoke?.avg_efficiency_pct||0}% efficacité moyenne</div></div>
|
||
<div class="card"><div class="icon">📈</div><h3>Kaizen</h3><div class="n">${d.kaizen?.count||0}</div><div class="l">${(d.kaizen?.total_savings_euro||0).toLocaleString()}€ économisés</div></div>
|
||
<div class="card"><div class="icon">🚶</div><h3>Gemba</h3><div class="n">${d.gemba?.walks||0}</div><div class="l">${d.gemba?.muda_spotted||0} muda spotted</div></div>
|
||
<div class="card"><div class="icon">🔄</div><h3>PDCA Active</h3><div class="n">${d.pdca_active||0}</div><div class="l">Cycles Plan/Do/Check</div></div>
|
||
<div class="card"><div class="icon">🚨</div><h3>Andon Open</h3><div class="n">${d.andon_open||0}</div><div class="l">Alertes actives</div></div>
|
||
<div class="card"><div class="icon">🧹</div><h3>5S Avg</h3><div class="n">${d.five_s_avg_score||0}<span class="unit">/25</span></div><div class="l">Maturité 5S</div></div>
|
||
<div class="card"><div class="icon">📋</div><h3>A3 Open</h3><div class="n">${d.a3_open||0}</div><div class="l">Problem-solving actifs</div></div>`;
|
||
}
|
||
async function loadMuda(){const d=await fetch(`/api/em/muda?tenant=${T()}`).then(r=>r.json());document.getElementById('muda-body').innerHTML=(d.entries||[]).map(e=>`<tr><td>${e.muda_type}</td><td><span class="badge sev-${e.severity}">sev${e.severity}</span></td><td>${e.description}</td><td>${e.vs_id||'-'}</td><td>${Number(e.impact_euro||0).toLocaleString()}€</td><td><span class="badge status-${e.status}">${e.status}</span></td></tr>`).join('')}
|
||
async function loadPoka(){const d=await fetch(`/api/em/poka-yoke?tenant=${T()}`).then(r=>r.json());document.getElementById('poka-body').innerHTML=(d.devices||[]).map(p=>`<tr><td>${p.process}</td><td><span class="badge">${p.device_type}</span></td><td>${p.mechanism}</td><td>${p.validation}</td><td>${p.efficiency_pct}%</td></tr>`).join('')}
|
||
async function loadKaizen(){const d=await fetch(`/api/em/kaizen?tenant=${T()}`).then(r=>r.json());document.getElementById('kaizen-body').innerHTML=(d.events||[]).map(k=>`<tr><td>${k.title}</td><td>${k.dept}</td><td>${k.problem}</td><td>${Number(k.savings_euro||0).toLocaleString()}€</td><td>${k.savings_hours||0}h</td><td>${k.duration_days||0}j</td><td><span class="badge status-${k.status}">${k.status}</span></td></tr>`).join('')}
|
||
async function loadGemba(){const d=await fetch(`/api/em/gemba?tenant=${T()}`).then(r=>r.json());document.getElementById('gemba-body').innerHTML=(d.walks||[]).map(w=>`<tr><td>${new Date(w.created_at).toLocaleDateString()}</td><td>${w.location}</td><td>${w.walker}</td><td>${(w.observations||[]).length} obs</td><td>${(w.actions||[]).length} actions</td><td>${w.muda_spotted}</td><td>${w.walk_duration_min}min</td></tr>`).join('')}
|
||
async function loadPdca(){const d=await fetch(`/api/em/pdca?tenant=${T()}`).then(r=>r.json());document.getElementById('pdca-body').innerHTML=(d.cycles||[]).map(p=>{const gap=p.actual&&p.target?((p.actual-p.target)/Math.max(1,p.target)*100).toFixed(1)+'%':'-';return `<tr><td>${p.title}</td><td><span class="badge">${p.phase}</span></td><td>${p.kpi_name}</td><td>${p.baseline}</td><td>${p.target}</td><td>${p.actual||'-'}</td><td>${gap}</td></tr>`}).join('')}
|
||
async function loadAndon(){const d=await fetch(`/api/em/andon?tenant=${T()}`).then(r=>r.json());document.getElementById('andon-body').innerHTML=(d.alerts||[]).map(a=>`<tr><td>${a.station}</td><td class="andon-${a.severity}">●${a.severity}</td><td>${a.message}</td><td><span class="badge status-${a.status==='open'?'in-progress':'completed'}">${a.status}</span></td><td>${a.resolved_by||'-'}</td><td>${a.resolution_time_min||'-'}min</td></tr>`).join('')}
|
||
async function loadFiveS(){const d=await fetch(`/api/em/five-s?tenant=${T()}`).then(r=>r.json());document.getElementById('fives-body').innerHTML=(d.audits||[]).map(a=>`<tr><td>${a.area}</td><td>${a.seiri}</td><td>${a.seiton}</td><td>${a.seiso}</td><td>${a.seiketsu}</td><td>${a.shitsuke}</td><td><strong>${a.total_score}/25</strong></td><td>${a.auditor}</td></tr>`).join('')}
|
||
async function loadA3(){const d=await fetch(`/api/em/a3?tenant=${T()}`).then(r=>r.json());document.getElementById('a3-body').innerHTML=(d.reports||[]).map(r=>`<tr><td>${r.title}</td><td><span class="badge status-${r.status}">${r.status}</span></td><td>${(r.background||'').substring(0,100)}...</td><td>${(r.gap_analysis||'').substring(0,80)}...</td><td>${(r.root_causes||[]).length} causes</td><td>${r.owner}</td></tr>`).join('')}
|
||
async function reload(){await loadDash();await loadMuda();await loadPoka();await loadKaizen();await loadGemba();await loadPdca();await loadAndon();await loadFiveS();await loadA3()}
|
||
(async()=>{await loadTenants();await reload()})();
|
||
</script></body></html>
|