Skip to main content

Step 7: Assemble the dashboard

This step shows how the components compose into the final App.jsx that ships with the reference repo. The three components you built each play a role:

  • LiveChart renders each metric card in the charts row.
  • SessionButton sits inside the device's metadata panel, where it also drives a session duration timer and an energy accumulator (see DeviceCard.jsx in the repo).
  • SampleRateDialog opens from the Set sample rate button in the device card header, and wraps SampleRateForm in a modal.

Here is dashboard/src/App.jsx:

import { useEffect, useState } from "react";

import { TopBar } from "./components/TopBar.jsx";
import { DeviceCard } from "./components/DeviceCard.jsx";
import { LiveChart } from "./components/LiveChart.jsx";
import { EventsCard, AlertsCard } from "./components/EventsAlerts.jsx";
import { useSimState } from "./hooks/useSimState.js";

const TWEAK_DEFAULTS = {
accentColor: "#157A3E",
deviceName: "SF-DEMO-001",
tempThreshold: 58,
pricePerKwh: 29.5,
};

export default function App() {
const [tweaks] = useState(TWEAK_DEFAULTS);
const { state, devSpikeTemp, acknowledgeAlert } = useSimState({
tempThreshold: tweaks.tempThreshold,
pricePerKwh: tweaks.pricePerKwh,
});

useEffect(() => {
document.documentElement.style.setProperty("--blue", tweaks.accentColor);
}, [tweaks.accentColor]);

return (
<div>
<TopBar />
<main style={{ padding: "20px 24px 32px", maxWidth: 1440, margin: "0 auto" }}>
<h1>Charger 1</h1>

<DeviceCard simState={state} onDevSpike={devSpikeTemp} tweaks={tweaks} />

<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 12 }}>
<LiveChart title="Power draw" subtitle="power · mW · 5 min (live)" metric="power" color="#157A3E" unit="mW" />
<LiveChart title="Current draw" subtitle="current · mA · 5 min (live)" metric="current" color="#E09E00" unit="mA" />
<LiveChart title="Bus voltage" subtitle="volt · V · 5 min (live)" metric="volt" color="#1E5FAE" unit="V" />
</div>

<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}>
<EventsCard events={state.events} />
<AlertsCard alerts={state.alerts} onAck={acknowledgeAlert} />
</div>
</main>
</div>
);
}

TopBar, EventsCard, AlertsCard, and useSimState are UI chrome and simulated state that drive the visual demo. They aren't part of the RelayX integration. The components that matter for this tutorial are DeviceCard and LiveChart.

DeviceCard is a layout wrapper that composes the components you built. Inside it:

  • The left metadata panel uses useOnline, useDeviceConfig, and renders SessionButton at the bottom.
  • The right grid uses useRelayLatest to drive three ArcGauge readouts (not covered in this tutorial; see the UI Kit reference).
  • The header has a Set sample rate button that opens SampleRateDialog, which wraps SampleRateForm.

See dashboard/src/components/DeviceCard.jsx in the reference repo for the full source.

Test it

With the device running, reload http://localhost:5173. The full dashboard renders:

  • A top bar
  • A header with "Charger 1" and a styled device card (metadata panel + gauges)
  • A Start session button in the metadata panel. Press it and the relay closes, the gauges move, the charts draw new points, and the session duration + energy fields start accumulating. Press Stop session and they freeze.
  • A Set sample rate button in the card header. Open the dialog, set 250 ms, and all charts tighten their cadence to four times per second.
  • Three live charts and the simulated events + alerts panels below.

That is the full end-to-end loop. You built the three components that carry the RelayX integration. The rest is styling and layout you can adapt to your own design system.

References