Implemented epics read/write

This commit is contained in:
Martino Ferrari
2026-04-27 12:42:10 +02:00
parent b76b7f0ba8
commit 1bda25454b
32 changed files with 1553 additions and 281 deletions
+58 -36
View File
@@ -1,11 +1,10 @@
import { h } from 'preact';
import { useState, useMemo } from 'preact/hooks';
import { useState, useMemo, useEffect } from 'preact/hooks';
import InterfaceList from './InterfaceList';
import Canvas from './Canvas';
import { wsClient } from './lib/ws';
import { parseInterface } from './lib/xml';
import type { Interface } from './lib/types';
import { useEffect } from 'preact/hooks';
import ContextualHelp from './ContextualHelp';
import HelpModal from './HelpModal';
@@ -25,6 +24,7 @@ export default function ViewMode({ onEdit }: Props) {
const [wsStatus, setWsStatus] = useState('connecting');
const [showHelp, setShowHelp] = useState(false);
const [helpSection, setHelpSection] = useState('start');
const [showTimeNav, setShowTimeNav] = useState(false);
function openHelp(section = 'start') { setHelpSection(section); setShowHelp(true); }
@@ -52,13 +52,24 @@ export default function ViewMode({ onEdit }: Props) {
try {
const res = await fetch(`/api/v1/interfaces/${encodeURIComponent(interfaceId)}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const xml = await res.text();
handleLoad(xml);
handleLoad(await res.text());
} catch (err) {
setParseError(`Failed to load interface "${interfaceId}": ${err instanceof Error ? err.message : err}`);
}
}
/** Load interface by ID and pass parsed object to onEdit */
async function handleEditById(id: string) {
try {
const res = await fetch(`/api/v1/interfaces/${encodeURIComponent(id)}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const iface = parseInterface(await res.text());
onEdit?.(iface);
} catch (err) {
setParseError(`Failed to load interface for editing: ${err instanceof Error ? err.message : err}`);
}
}
function handleLoadHistory() {
if (!startInput || !endInput) return;
const start = new Date(startInput).toISOString();
@@ -82,38 +93,6 @@ export default function ViewMode({ onEdit }: Props) {
)}
</div>
<div class="toolbar-center">
{/* Time range picker */}
<div class="time-nav">
<button
class={`toolbar-btn${isLive ? ' toolbar-btn-active' : ''}`}
title="Switch to live data"
onClick={handleGoLive}
>
Live
</button>
<input
class="time-input"
type="datetime-local"
value={startInput}
onInput={(e) => setStartInput((e.target as HTMLInputElement).value)}
title="History start"
/>
<span class="time-sep"></span>
<input
class="time-input"
type="datetime-local"
value={endInput}
onInput={(e) => setEndInput((e.target as HTMLInputElement).value)}
title="History end"
/>
<button
class="toolbar-btn"
title="Load historical data for this time range"
onClick={handleLoadHistory}
>
Load
</button>
</div>
{parseError && (
<span class="parse-error" title={parseError}>Parse error check console</span>
)}
@@ -123,6 +102,13 @@ export default function ViewMode({ onEdit }: Props) {
<span class="status-dot"></span>
{wsStatus}
</div>
<button
class={`toolbar-btn${showTimeNav ? ' toolbar-btn-active' : ''}`}
title="Toggle historical time navigation"
onClick={() => setShowTimeNav(v => !v)}
>
History
</button>
<ContextualHelp mode="view" onOpenManual={openHelp} />
<button
class="icon-btn help-manual-btn"
@@ -141,10 +127,46 @@ export default function ViewMode({ onEdit }: Props) {
</div>
</header>
{/* Historical time range bar — hidden by default */}
{showTimeNav && (
<div class="time-nav-bar">
<button
class={`toolbar-btn${isLive ? ' toolbar-btn-active' : ''}`}
title="Switch to live data"
onClick={handleGoLive}
>
Live
</button>
<input
class="time-input"
type="datetime-local"
value={startInput}
onInput={(e) => setStartInput((e.target as HTMLInputElement).value)}
title="History start"
/>
<span class="time-sep"></span>
<input
class="time-input"
type="datetime-local"
value={endInput}
onInput={(e) => setEndInput((e.target as HTMLInputElement).value)}
title="History end"
/>
<button
class="toolbar-btn"
title="Load historical data for this time range"
onClick={handleLoadHistory}
>
Load
</button>
</div>
)}
<div class="content">
<InterfaceList
onLoad={handleLoad}
onEdit={onEdit}
onEditId={handleEditById}
onSelect={async (id) => {
try {
const res = await fetch(`/api/v1/interfaces/${encodeURIComponent(id)}`);