demo D260
Real-Time Operations Dashboard: One Feed, Many Grids in React
One feed drives orders, shipments and incidents grids and a throughput chart, with no backend
createDataRouter · MockWebSocket · opsFeed
This is a ready-made operations console: one feed drives an orders grid, a shipments grid and an incidents grid plus a throughput chart, all with no backend. It is a working starting point you can lift and point at real data, showing several live views sharing one source.
This is the React version. The grid mounts through the createLatticeGrid adapter, which takes its configuration as ordinary props and hands back the live grid through a ref. The grid below is the same one every other tab runs.
The configuration
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.71.0/lattice-grid.min.css">
<div id="app"></div>
<script type="module">
import React from 'https://esm.sh/react@18';
import { createRoot } from 'https://esm.sh/react-dom@18/client';
import { createGrid, createHeadlessGrid } from 'https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.71.0/lattice-grid.esm.min.js';
import { createLatticeReact } from 'https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.71.0/modules/react.esm.min.js';
import { createChart } from 'https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.71.0/modules/charts.esm.min.js';
import { createDataRouter } from 'https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.71.0/modules/data-router.esm.min.js';
import { MockWebSocket, opsFeed } from 'https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.71.0/modules/mock-socket.esm.min.js';
const { LatticeGrid, LatticeChart, useLatticeRouter, LatticeRouterProvider } =
createLatticeReact({ React, createGrid, createChart, createDataRouter });
const MONEY = { style: 'currency', currency: 'USD', decimals: 0 };
const orderColumns = [
{ field: 'id', title: 'Order', layout: { width: 96 } },
{ field: 'region', title: 'Region', filter: { type: 'set' } },
{ field: 'customer', title: 'Customer' },
{ field: 'value', title: 'Value', type: 'number', format: MONEY, total: 'sum' },
];
const shipmentColumns = [
{ field: 'id', title: 'Shipment', layout: { width: 96 } },
{ field: 'region', title: 'Region', filter: { type: 'set' } },
{ field: 'carrier', title: 'Carrier' },
{ field: 'units', title: 'Units', type: 'number', total: 'sum' },
];
const incidentColumns = [
{ field: 'id', title: 'Incident', layout: { width: 96 } },
{ field: 'region', title: 'Region', filter: { type: 'set' } },
{ field: 'service', title: 'Service' },
{ field: 'severity', title: 'Severity' },
];
function App() {
const router = useLatticeRouter({ key: 'type', rowKey: 'id' });
const [metricsGrid, setMetricsGrid] = React.useState(null);
React.useEffect(() => {
if (!router) return undefined;
// A headless grid holds the metric series; the chart reads it.
const metrics = createHeadlessGrid({ rowKey: 't', columns: [
{ field: 't', type: 'number' }, { field: 'events', type: 'number' },
] });
router.attach(metrics, 'metric', { rowKey: 't' });
setMetricsGrid(metrics);
// link narrows Incidents to the regions of the orders you select,
// re-pushed through the same keyed-diff path.
// (attach() for orders/incidents happens through the route props below;
// link needs both grids, so it is wired once they report ready - see
// the two onGridReady handlers.)
// The mock socket runs it with no backend; one line swaps in a real
// WebSocket.
const socket = new MockWebSocket({ feed: opsFeed({ seed: 7 }), rate: 900, jitter: 300 });
socket.onmessage = (event) => {
const message = JSON.parse(event.data);
if (message.kind === 'snapshot') router.load(message.rows);
else router.apply(message.changes);
};
return () => { socket.close(); metrics.destroy(); };
}, [router]);
const ordersRef = React.useRef(null);
const incidentsRef = React.useRef(null);
const linked = React.useRef(false);
const maybeLink = () => {
if (linked.current || !router || !ordersRef.current || !incidentsRef.current) return;
router.link(ordersRef.current, incidentsRef.current, { from: 'region', to: 'region' });
linked.current = true;
};
return (
<LatticeRouterProvider router={router}>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '14px' }}>
<LatticeGrid
route="order" rowKey="id" selection={{ mode: 'multiple', checkbox: true }}
columns={orderColumns} rows={[]}
onGridReady={(g) => { ordersRef.current = g; maybeLink(); }}
style={{ height: '300px' }}
/>
<LatticeGrid route="shipment" rowKey="id" columns={shipmentColumns} rows={[]} style={{ height: '300px' }} />
<LatticeGrid
route="incident" rowKey="id" columns={incidentColumns} rows={[]}
onGridReady={(g) => { incidentsRef.current = g; maybeLink(); }}
style={{ height: '300px' }}
/>
</div>
{metricsGrid && (
<LatticeChart grid={metricsGrid} type="line" x="t" y="events" title="Throughput per tick" style={{ height: '200px', marginTop: '14px' }} />
)}
</LatticeRouterProvider>
);
}
createRoot(document.getElementById('app')).render(<App />);
</script>