working epics ioc and tested

This commit is contained in:
Martino Ferrari
2026-05-04 21:13:36 +02:00
parent 90669c5fd6
commit 0a5a85e4c4
25 changed files with 1550 additions and 136 deletions
+121
View File
@@ -0,0 +1,121 @@
import { h } from 'preact';
import { useEffect, useState } from 'preact/hooks';
import { getMetaStore, getSignalStore } from './lib/stores';
import type { SignalRef, SignalMeta, SignalValue } from './lib/types';
interface Props {
signal: SignalRef;
onClose: () => void;
}
function qualityColor(q: string): string {
switch (q) {
case 'good': return '#4ade80';
case 'uncertain': return '#fbbf24';
case 'bad': return '#f87171';
default: return '#6b7280';
}
}
function fmt(v: any): string {
if (v === null || v === undefined) return '—';
if (typeof v === 'number') return Number.isFinite(v) ? v.toPrecision(6).replace(/\.?0+$/, '') : String(v);
if (Array.isArray(v)) return `[${v.slice(0, 8).map((x: any) => fmt(x)).join(', ')}${v.length > 8 ? ', …' : ''}]`;
return String(v);
}
function fmtTs(ts: string | null): string {
if (!ts) return '—';
try { return new Date(ts).toLocaleString(); } catch { return ts; }
}
export default function InfoPanel({ signal, onClose }: Props) {
const [meta, setMeta] = useState<SignalMeta | null>(null);
const [sv, setSv] = useState<SignalValue>({ value: null, quality: 'unknown', ts: null });
useEffect(() => {
const unsubM = getMetaStore(signal).subscribe(setMeta);
const unsubV = getSignalStore(signal).subscribe(setSv);
return () => { unsubM(); unsubV(); };
}, [signal.ds, signal.name]);
return (
<div class="info-panel">
<div class="info-panel-header">
<span class="info-panel-title">Signal Info</span>
<button class="info-panel-close" onClick={onClose} title="Close"></button>
</div>
<div class="info-panel-body">
<table class="info-table">
<tbody>
<tr><td class="info-key">Datasource</td><td class="info-val info-mono">{signal.ds}</td></tr>
<tr><td class="info-key">Name</td><td class="info-val info-mono">{signal.name}</td></tr>
</tbody>
</table>
<div class="info-sep" />
<table class="info-table">
<tbody>
<tr>
<td class="info-key">Value</td>
<td class="info-val">
<span
class="quality-dot"
style={`background:${qualityColor(sv.quality)};display:inline-block;margin-right:5px;vertical-align:middle;`}
/>
{fmt(sv.value)}{meta?.unit ? ` ${meta.unit}` : ''}
</td>
</tr>
<tr><td class="info-key">Quality</td><td class="info-val">{sv.quality}</td></tr>
<tr><td class="info-key">Timestamp</td><td class="info-val">{fmtTs(sv.ts)}</td></tr>
</tbody>
</table>
{meta && (
<div>
<div class="info-sep" />
<table class="info-table">
<tbody>
<tr><td class="info-key">Type</td><td class="info-val">{meta.type}</td></tr>
{meta.unit && <tr><td class="info-key">Unit</td><td class="info-val">{meta.unit}</td></tr>}
<tr><td class="info-key">Writable</td><td class="info-val">{meta.writable ? 'Yes' : 'No'}</td></tr>
{(meta.displayLow !== 0 || meta.displayHigh !== 0) && (
<tr>
<td class="info-key">Display range</td>
<td class="info-val">{meta.displayLow} {meta.displayHigh}</td>
</tr>
)}
</tbody>
</table>
{meta.enumStrings && meta.enumStrings.length > 0 && (
<div>
<div class="info-sep" />
<div class="info-section-hdr">States</div>
<table class="info-table">
<tbody>
{meta.enumStrings.map((s, i) => (
<tr key={i}>
<td class="info-key">{i}</td>
<td class="info-val info-mono">{s}</td>
</tr>
))}
</tbody>
</table>
</div>
)}
{meta.description && (
<div>
<div class="info-sep" />
<div class="info-section-hdr">Description</div>
<div class="info-desc">{meta.description}</div>
</div>
)}
</div>
)}
</div>
</div>
);
}