import { useEffect, useState } from "react"; import { api, type BlockHistoryPoint, type ControlAction, type DeviceStatus, type QuarterHourStatus } from "./api"; import { PeakHistoryChart } from "./PeakHistoryChart"; const THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1000; export function Dashboard() { const [quarterHour, setQuarterHour] = useState(null); const [devices, setDevices] = useState([]); const [history, setHistory] = useState([]); const [actions, setActions] = useState([]); const [error, setError] = useState(null); useEffect(() => { let cancelled = false; async function refresh() { try { const [status, hist, acts] = await Promise.all([ api.getStatus(), api.getHistory(Date.now() - THIRTY_DAYS_MS), api.getActions(10), ]); if (cancelled) return; setQuarterHour(status.quarterHour); setDevices(status.devices); setHistory(hist); setActions(acts); setError(null); } catch (err) { if (!cancelled) setError(err instanceof Error ? err.message : "Failed to load status"); } } refresh(); const interval = setInterval(refresh, 10_000); return () => { cancelled = true; clearInterval(interval); }; }, []); if (error) { return (

Error

{error}

); } if (!quarterHour) { return
Loading…
; } const overTarget = quarterHour.overTarget; const shedCount = devices.filter((d) => d.shed).length; return ( <>

Current quarter-hour block

{(quarterHour.projectedAverageW / 1000).toFixed(2)} kW
projected average · target {(quarterHour.targetW / 1000).toFixed(2)} kW · running average{" "} {(quarterHour.runningAverageW / 1000).toFixed(2)} kW ·{" "} {Math.round(quarterHour.elapsedFractionOfBlock * 100)}% through block
{overTarget ? `Over target · ${shedCount} device(s) shed` : "Under target"}

Devices

{devices.length === 0 ? (

No devices configured yet — add them in the Devices tab.

) : ( devices .slice() .sort((a, b) => a.priority - b.priority) .map((d) => (
{d.name}
{d.entityId} · priority {d.priority} {!d.available ? " · unavailable" : ""}
{d.manualOverride && Manual} {d.shed ? "Shed" : "Active"}
)) )}

Daily peak (last 30 days)

Recent control actions

{actions.length === 0 ? (

No actions yet.

) : ( {actions.map((a, i) => ( ))}
Time Device Action Reason
{new Date(a.timestampMs).toLocaleTimeString()} {a.entityId} {a.action} {a.reason}
)}
); }