71 lines
2.3 KiB
React
71 lines
2.3 KiB
React
/**
|
|
* Active alarms, most severe first.
|
|
*
|
|
* Every alarm carries its severity as a WORD as well as a colour, and "Explain"
|
|
* hands the alarm to the copilot - which is the moment the demo stops looking
|
|
* like a dashboard and starts looking like a twin that understands itself.
|
|
*/
|
|
|
|
import { useStore } from '../store.js';
|
|
import { duration } from '../lib/format.js';
|
|
|
|
const SEVERITY_ORDER = ['critical', 'major', 'warning', 'predictive', 'info'];
|
|
|
|
export function AlarmFeed({ onExplain }) {
|
|
const frame = useStore((s) => s.frame);
|
|
const select = useStore((s) => s.select);
|
|
if (!frame) return null;
|
|
|
|
const alarms = frame.analytics.alarms;
|
|
const counts = SEVERITY_ORDER.map((sev) => ({
|
|
sev,
|
|
n: alarms.filter((a) => a.severity === sev).length,
|
|
})).filter((x) => x.n > 0);
|
|
|
|
return (
|
|
<div className="card">
|
|
<h2 className="card-title">
|
|
Alarms
|
|
<span className="hint">
|
|
{counts.length
|
|
? counts.map((c) => `${c.n} ${c.sev}`).join(' · ')
|
|
: 'all clear'}
|
|
</span>
|
|
</h2>
|
|
|
|
{alarms.length === 0 ? (
|
|
<div className="empty">
|
|
No active alarms. All signals are within limits and no threshold crossings are projected.
|
|
</div>
|
|
) : (
|
|
<div className="alarm-list">
|
|
{alarms.map((a) => (
|
|
<div className={`alarm ${a.severity}`} key={a.key}>
|
|
<div className="alarm-top">
|
|
<span className={`sev ${a.severity}`}>
|
|
{a.severity === 'predictive' ? 'Trend' : a.severity}
|
|
</span>
|
|
<button
|
|
className="who"
|
|
style={{ background: 'none', border: 'none', padding: 0, cursor: 'pointer' }}
|
|
onClick={() => select(a.station)}
|
|
title={`Select ${a.station}`}
|
|
>
|
|
{a.station}{a.signal ? `.${a.signal}` : ''}
|
|
</button>
|
|
<span className="muted small" style={{ marginLeft: 'auto' }}>
|
|
{duration(frame.t - a.raisedAt)} ago
|
|
</span>
|
|
</div>
|
|
<div className="msg">{a.message}</div>
|
|
<div className="actions">
|
|
<button onClick={() => onExplain(a)}>Explain this</button>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|