phase 10
This commit is contained in:
+59
-2
@@ -1,5 +1,5 @@
|
||||
import { h } from 'preact';
|
||||
import { useState } from 'preact/hooks';
|
||||
import { useState, useMemo } from 'preact/hooks';
|
||||
import InterfaceList from './InterfaceList';
|
||||
import Canvas from './Canvas';
|
||||
import { wsClient } from './lib/ws';
|
||||
@@ -11,11 +11,23 @@ interface Props {
|
||||
onEdit?: (iface?: Interface) => void;
|
||||
}
|
||||
|
||||
/** Format a Date as a datetime-local input value (YYYY-MM-DDTHH:MM) */
|
||||
function toLocalInput(d: Date): string {
|
||||
const pad = (n: number) => String(n).padStart(2, '0');
|
||||
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;
|
||||
}
|
||||
|
||||
export default function ViewMode({ onEdit }: Props) {
|
||||
const [currentInterface, setCurrentInterface] = useState<Interface | null>(null);
|
||||
const [parseError, setParseError] = useState<string | null>(null);
|
||||
const [wsStatus, setWsStatus] = useState('connecting');
|
||||
|
||||
// Historical time range — null means live
|
||||
const now = useMemo(() => new Date(), []);
|
||||
const [startInput, setStartInput] = useState(() => toLocalInput(new Date(now.getTime() - 3600_000)));
|
||||
const [endInput, setEndInput] = useState(() => toLocalInput(now));
|
||||
const [timeRange, setTimeRange] = useState<{ start: string; end: string } | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const unsub = wsClient.status.subscribe(setWsStatus);
|
||||
return unsub;
|
||||
@@ -41,6 +53,19 @@ export default function ViewMode({ onEdit }: Props) {
|
||||
}
|
||||
}
|
||||
|
||||
function handleLoadHistory() {
|
||||
if (!startInput || !endInput) return;
|
||||
const start = new Date(startInput).toISOString();
|
||||
const end = new Date(endInput).toISOString();
|
||||
setTimeRange({ start, end });
|
||||
}
|
||||
|
||||
function handleGoLive() {
|
||||
setTimeRange(null);
|
||||
}
|
||||
|
||||
const isLive = timeRange === null;
|
||||
|
||||
return (
|
||||
<div class="view-mode">
|
||||
<header class="toolbar">
|
||||
@@ -51,6 +76,38 @@ 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>
|
||||
)}
|
||||
@@ -84,7 +141,7 @@ export default function ViewMode({ onEdit }: Props) {
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Canvas iface={currentInterface} onNavigate={handleNavigate} />
|
||||
<Canvas iface={currentInterface} onNavigate={handleNavigate} timeRange={timeRange} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user