Implemented epics read/write
This commit is contained in:
+58
-36
@@ -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)}`);
|
||||
|
||||
Reference in New Issue
Block a user