Lattice Grid

blog

Bar Chart for Tickets by Support Queue

Building…
Loading a live grid…

You run a support team, and every morning you open the queue report. Eight queues, three priorities each, twenty-four counts of open tickets. The question you actually have is not in any cell: which queue is falling behind, and is it the urgent tickets or the routine ones that are piling up? A table makes you read your way to that answer. A bar chart shows it before you have read anything.

The example below does it with the rows on screen. The chart is bound to the grid, so it draws exactly the rows the grid is showing. Put a priority in the box, or open the filter on the Queue column, and the bars you do not want leave the chart on the next frame. Clear the filter and they come back.

Why bars and not a pie

A pie is the obvious choice for counts that add up to a whole, and it is the wrong one for comparing queues. The eye has to judge angles and areas, and eight slices that are within a few tickets of each other look the same. Bars share one baseline, so length does the comparing: the longest queue is the longest bar, and stacking the priorities inside each bar shows how much of it is urgent. When the queue names are long, the horizontalBar variant lays the bars on their side so every name gets a full line of room.

The chart is a view of the grid

There is no second data set to keep in step. createChart takes the grid itself, reads its filtered rows, and redraws whenever they change. x names the category column, y the measure, and series splits each bar by priority. stack: true piles the priorities into one bar per queue instead of three side by side. Sort, filter or edit the grid and the chart follows, because a chart of rows you cannot see would be describing a different data set.

The code, in plain JavaScript

import { createGrid } from '@toclocoinc/lattice-grid';
import { createChart } from '@toclocoinc/lattice-grid/modules/charts';
import '@toclocoinc/lattice-grid/css';

const open = {
  'Billing and invoicing': [6, 14, 9],
  'Account access': [11, 18, 7],
  'Integrations and API': [9, 21, 12],
  'Data import and export': [4, 13, 15],
  'Reporting and dashboards': [3, 17, 19],
  'Mobile app': [5, 10, 8],
  'Security and compliance': [8, 6, 2],
  'Onboarding and training': [1, 9, 16],
};
const priorities = ['High', 'Medium', 'Low'];
const rows = Object.keys(open).flatMap((queue, q) =>
  priorities.map((priority, p) => ({
    id: q * 3 + p, queue, priority, open: open[queue][p],
  }))
);

const grid = createGrid(document.querySelector('#grid'), {
  rowKey: 'id',
  columns: [
    { field: 'queue', title: 'Queue', filter: { type: 'set' } },
    { field: 'priority', title: 'Priority', filter: { type: 'set' } },
    { field: 'open', title: 'Open tickets', type: 'number' },
  ],
  rows,
});

createChart({
  grid, container: '#chart', type: 'bar',
  x: 'queue', y: 'open', series: 'priority', stack: true,
  title: 'Open tickets by queue and priority',
});

Give #chart and #grid a height in your page and that is the whole screen.

The same thing in React

The React adapter is a factory: you pass it React and createGrid, and it returns the component. The component forwards a ref whose .grid is the real grid, which is what the chart binds to.

import React, { useEffect, useRef } from 'react';
import { createGrid } from '@toclocoinc/lattice-grid';
import { createChart } from '@toclocoinc/lattice-grid/modules/charts';
import { createLatticeGrid } from '@toclocoinc/lattice-grid/modules/react';
import '@toclocoinc/lattice-grid/css';

const LatticeGrid = createLatticeGrid({ React, createGrid });
const columns = [
  { field: 'queue', title: 'Queue', filter: { type: 'set' } },
  { field: 'priority', title: 'Priority', filter: { type: 'set' } },
  { field: 'open', title: 'Open tickets', type: 'number' },
];

export function QueueLoad({ rows }) {
  const gridRef = useRef(null);
  const chartEl = useRef(null);

  useEffect(() => {
    const chart = createChart({
      grid: gridRef.current.grid, container: chartEl.current, type: 'bar',
      x: 'queue', y: 'open', series: 'priority', stack: true,
      title: 'Open tickets by queue and priority',
    });
    return () => chart.destroy();
  }, []);

  return (
    <>
      <div ref={chartEl} style={{ height: 300 }} />
      <LatticeGrid ref={gridRef} rowKey="id" columns={columns} rows={rows} style={{ height: 300 }} />
    </>
  );
}

Pass it the same twenty-four rows as the first listing, with queue, priority and open on each.

The options used

Option Type What it does
grid Grid Required. The grid to read.
container Element or string Required. Where to draw.
type string 'bar' here. 'horizontalBar' lays the bars on their side.
x / y string Category and measure columns.
series string Splits the measure into one series per distinct value.
stack boolean or 'percent' Stack the series rather than drawing them side by side.
title string Drawn above the plot.

Go further

The charts page lists every chart type the same grid can feed, and the bar, line and pie family demo shows the neighbours of this one side by side. To see how a chart keeps following the grid when you filter by clicking, read how to draw a chart that follows the grid.

Read next

All posts RSS