Lattice Grid Buy a licence

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.

Building…
Loading a live grid…

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>