Files
Digitaltwin/web/src/panels/KpiRow.jsx
T

80 lines
2.8 KiB
React
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Line KPI tiles.
*
* OEE gets a hero figure; its three factors sit beside it as their own tiles with
* magnitude meters. Deliberately tiles rather than a chart: these are five
* single headline numbers, and a bar chart of five unrelated measures would be
* harder to read, not easier.
*/
import { useStore } from '../store.js';
function Meter({ value, color }) {
return (
<div className="meter">
<span style={{ width: `${Math.max(0, Math.min(100, value))}%`, background: color }} />
</div>
);
}
/** Colour the OEE factors by how bad the loss is, always beside a number. */
function factorColor(v) {
if (v >= 0.95) return 'var(--good)';
if (v >= 0.85) return 'var(--series-1)';
if (v >= 0.70) return 'var(--warning)';
return 'var(--critical)';
}
export function KpiRow() {
const kpi = useStore((s) => (s.frame ? s.frame.kpi : null));
const windowSec = useStore((s) => (s.meta ? s.meta.kpiWindowSec : 1200));
if (!kpi) {
return <div className="card"><div className="empty">Waiting for telemetry…</div></div>;
}
const factors = [
{ label: 'Availability', v: kpi.availability, foot: 'Stoppage losses' },
{ label: 'Performance', v: kpi.performance, foot: 'Micro-stops, slow cycles' },
{ label: 'Quality', v: kpi.quality, foot: 'Rejects at INS-04' },
];
return (
<div className="kpi-row">
<div className="tile hero">
<span className="label">OEE · rolling {Math.round(windowSec / 60)} min</span>
<span className="value" style={{ color: factorColor(kpi.oee) }}>
{(kpi.oee * 100).toFixed(1)}<span className="unit">%</span>
</span>
<Meter value={kpi.oee * 100} color={factorColor(kpi.oee)} />
<span className="foot">Availability × Performance × Quality</span>
</div>
{factors.map((f) => (
<div className="tile" key={f.label}>
<span className="label">{f.label}</span>
<span className="value">{(f.v * 100).toFixed(1)}<span className="unit">%</span></span>
<Meter value={f.v * 100} color={factorColor(f.v)} />
<span className="foot">{f.foot}</span>
</div>
))}
<div className="tile">
<span className="label">Output</span>
<span className="value">{kpi.throughputPerHour.toFixed(0)}<span className="unit">/h</span></span>
<span className="foot">
{kpi.good.toLocaleString()} good · {kpi.rejected.toLocaleString()} rejected
</span>
</div>
<div className="tile">
<span className="label">Energy</span>
<span className="value">{kpi.energyKw.toFixed(0)}<span className="unit">kW</span></span>
<span className="foot">
{kpi.energyPerUnit > 0 ? `${kpi.energyPerUnit.toFixed(3)} kWh/unit` : '—'}
</span>
</div>
</div>
);
}