Select Git revision
StaffGraph.tsx 6.53 KiB
import React, { useEffect, useMemo, useState } from 'react';
import { Bar } from 'react-chartjs-2';
import { Chart as ChartJS, CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend } from 'chart.js';
import { NrenStaff, FilterSelection } from "../Schema";
import { createNRENStaffDataset, getYearsAndNrens, loadDataWithFilterSelectionFallback } from "../helpers/dataconversion";
import DataPage from '../components/DataPage';
import Filter from "../components/graphing/Filter"
import {ExportType, Sections} from '../helpers/constants';
import WithLegend from '../components/WithLegend';
import htmlLegendPlugin from '../plugins/HTMLLegendPlugin';
import {Row} from "react-bootstrap";
import DownloadDataButton from "../components/DownloadDataButton";
ChartJS.register(
CategoryScale,
LinearScale,
BarElement,
Title,
Tooltip,
Legend
);
const chartOptions = {
maintainAspectRatio: false,
animation: {
duration: 0
},
plugins: {
htmlLegend: {
// ID of the container(s) to put the legend in
containerIDs: ['legendtop', 'legendbottom'],
},
legend: {
display: false,
},
tooltip: {
callbacks: {
label: function (tooltipItem) {
let label = tooltipItem.dataset.label || '';
if (tooltipItem.parsed.x !== null) {
label += `: ${tooltipItem.parsed.x}%`
}
return label;
}
},
},
},
scales: {
x: {
position: 'top' as const,
stacked: true,
ticks: {
callback: (value: string | number, index: number) => {
return `${index * 10}%`;
},
},
},
x2: {
ticks: {
callback: (value: string | number) => {
if (typeof value === 'number') {
return `${value}%`;
}
return value;
},
},
grid: {
drawOnChartArea: false
},
afterDataLimits: function (axis) {
const indices = Object.keys(ChartJS.instances)
// initial values should be far outside possible range
let max = -999999
let min = 999999
for (const index of indices) {
if (ChartJS.instances[index] && axis.chart.scales.x2) {
min = Math.min(ChartJS.instances[index].scales.x.min, min);
max = Math.max(ChartJS.instances[index].scales.x.max, max);
}
}
axis.chart.scales.x2.options.min = min;
axis.chart.scales.x2.options.max = max;
axis.chart.scales.x2.min = min;
axis.chart.scales.x2.max = max;
},
},
y: {
stacked: true,
ticks: {
autoSkip: false,
},
},
},
indexAxis: "y" as const
};
interface inputProps {
filterSelection: FilterSelection
setFilterSelection: React.Dispatch<React.SetStateAction<FilterSelection>>
roles?: boolean
}
function StaffGraph({ filterSelection, setFilterSelection, roles = false }: inputProps) {
const [staffData, setStaffData] = useState<NrenStaff[]>([]);
const { years, nrens } = useMemo(
() => getYearsAndNrens(staffData),
[staffData]
);
const nrenStaffDataset = createNRENStaffDataset(staffData, roles, filterSelection.selectedYears[0]);
nrenStaffDataset.datasets.forEach(dataset => {
dataset.hidden = !filterSelection.selectedYears.includes(parseInt(dataset.stack));
});
// remove the datapoints and labels for the nrens that aren't selected
// unfortunately we cannot just hide them because graph.js doesn't want
// to create a stack from a single dataset
nrenStaffDataset.datasets.forEach(dataset => {
dataset.data = dataset.data.filter((e, i) => {
return filterSelection.selectedNrens.includes(nrenStaffDataset.labels[i]);
});
});
nrenStaffDataset.labels = nrenStaffDataset.labels.filter((e) => filterSelection.selectedNrens.includes(e));
useEffect(() => {
loadDataWithFilterSelectionFallback('/api/staff/', setStaffData, setFilterSelection);
}, [setFilterSelection]);
const filterNode = <Filter
max1year
filterOptions={{ availableYears: [...years], availableNrens: [...nrens.values()] }}
filterSelection={filterSelection}
setFilterSelection={setFilterSelection}
/>
const numNrens = filterSelection.selectedNrens.length;
const heightPerBar = 1.5; // every added bar should give this much additional height
// set a minimum height of 20rem
const height = Math.max(numNrens * heightPerBar, 20);
const title = roles
? "Roles of NREN employees"
: "Types of employment for NRENs";
const description = roles
? "The graph shows the roles of NREN employees. On hovering over the graph will give the percentage of employees in that role. This graph can be used to compare, selecting multiple NRENs to see the fluctuation of roles over selected year and with other NRENs."
: "The graph shows the types of employment for NREN employees. On hovering over the graphs will give the percentage of employees in that type of employment. This graph can be used to compare, selecting multiple NRENs to see the fluctuation of types of employment over selected year and with other NRENs.";
const filename = roles ? "roles_of_nren_employees" : "types_of_employment_for_nrens";
return (
<DataPage title={title}
description={description}
category={Sections.Organisation} filter={filterNode}>
<>
<Row>
<DownloadDataButton data={staffData} filename={filename} exportType={ExportType.CSV}/>
<DownloadDataButton data={staffData} filename={filename} exportType={ExportType.EXCEL}/>
</Row>
<WithLegend>
<div className="chart-container" style={{'height': `${height}rem`}}>
<Bar
data={nrenStaffDataset}
options={chartOptions}
plugins={[htmlLegendPlugin]}
/>
</div>
</WithLegend>
</>
</DataPage>
);
}
export default StaffGraph;