Bar Chart for Tickets by Support Queue
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.