@ministryofjustice/hmpps-digital-prison-reporting-frontend 3.26.1 → 3.26.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dpr/assets/js/all.mjs +31 -15
- package/dpr/components/chart-card/utils.js +89 -42
- package/dpr/components/chart-card/utils.ts +106 -47
- package/dpr/components/chart-card/view.njk +1 -4
- package/dpr/components/chart-tabs/view.njk +10 -5
- package/dpr/types/Charts.ts +33 -7
- package/dpr/types/Dashboards.ts +0 -3
- package/dpr/types/Metrics.ts +3 -3
- package/dpr/utils/dashboardUtils.js +0 -2
- package/dpr/utils/dashboardUtils.ts +0 -2
- package/dpr/utils/metricsUtils.js +1 -3
- package/dpr/utils/metricsUtils.ts +1 -4
- package/package.json +1 -1
- package/package.zip +0 -0
package/dpr/assets/js/all.mjs
CHANGED
|
@@ -1537,9 +1537,8 @@ class ChartVisualisation extends DprClientClass {
|
|
|
1537
1537
|
setHoverValue({ label, value, legend, ctx }) {
|
|
1538
1538
|
if (ctx.tooltipDetailsEl) {
|
|
1539
1539
|
ctx.tooltipDetailsEl.style.display = 'block';
|
|
1540
|
-
ctx.labelElement.innerHTML = `${label}`;
|
|
1540
|
+
ctx.labelElement.innerHTML = `${legend}: ${label}`;
|
|
1541
1541
|
ctx.valueElement.innerHTML = `${value}`;
|
|
1542
|
-
ctx.legendElement.innerHTML = `${legend}`;
|
|
1543
1542
|
}
|
|
1544
1543
|
if (ctx.headlineValuesEl) {
|
|
1545
1544
|
ctx.headlineValuesEl.style.display = 'none';
|
|
@@ -1615,17 +1614,16 @@ class BarChartVisualisation extends ChartVisualisation {
|
|
|
1615
1614
|
const ctx = this;
|
|
1616
1615
|
return {
|
|
1617
1616
|
callbacks: {
|
|
1617
|
+
title(context) {
|
|
1618
|
+
const { label, dataset } = context[0];
|
|
1619
|
+
const { label: establishmentId } = dataset;
|
|
1620
|
+
return `${establishmentId}: ${label}`
|
|
1621
|
+
},
|
|
1618
1622
|
label(context) {
|
|
1619
1623
|
const { label } = context;
|
|
1620
1624
|
const { data, label: legend } = context.dataset;
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
if (!ctx.isPercentage) {
|
|
1624
|
-
value = `${legend}: ${value}`;
|
|
1625
|
-
ctx.setHoverValue({ label, value, legend, ctx });
|
|
1626
|
-
} else {
|
|
1627
|
-
ctx.setHoverValue({ label, value, ctx });
|
|
1628
|
-
}
|
|
1625
|
+
const value = `${data[context.dataIndex]}${ctx.suffix}`;
|
|
1626
|
+
ctx.setHoverValue({ label, value, legend, ctx });
|
|
1629
1627
|
return value
|
|
1630
1628
|
},
|
|
1631
1629
|
},
|
|
@@ -1682,6 +1680,7 @@ class DoughnutChartVisualisation extends ChartVisualisation {
|
|
|
1682
1680
|
|
|
1683
1681
|
initSettings() {
|
|
1684
1682
|
return {
|
|
1683
|
+
options: this.setOptions(),
|
|
1685
1684
|
styling: this.setDatasetStyling(),
|
|
1686
1685
|
datalabels: this.setDataLabels(),
|
|
1687
1686
|
pluginsOptions: this.setPluginsOptions(),
|
|
@@ -1704,6 +1703,13 @@ class DoughnutChartVisualisation extends ChartVisualisation {
|
|
|
1704
1703
|
]
|
|
1705
1704
|
}
|
|
1706
1705
|
|
|
1706
|
+
setOptions() {
|
|
1707
|
+
const cutoutValue = this.chartParams.datasets.length === 1 ? '50%' : '20%';
|
|
1708
|
+
return {
|
|
1709
|
+
cutout: cutoutValue,
|
|
1710
|
+
}
|
|
1711
|
+
}
|
|
1712
|
+
|
|
1707
1713
|
setPluginsOptions() {
|
|
1708
1714
|
return {
|
|
1709
1715
|
legend: {
|
|
@@ -1790,6 +1796,11 @@ class DoughnutChartVisualisation extends ChartVisualisation {
|
|
|
1790
1796
|
const ctx = this;
|
|
1791
1797
|
return {
|
|
1792
1798
|
callbacks: {
|
|
1799
|
+
title(context) {
|
|
1800
|
+
const { label, dataset } = context[0];
|
|
1801
|
+
const { label: establishmentId } = dataset;
|
|
1802
|
+
return `${establishmentId}: ${label}`
|
|
1803
|
+
},
|
|
1793
1804
|
label(context) {
|
|
1794
1805
|
const { label, parsed: value, dataset } = context;
|
|
1795
1806
|
const { label: legend } = dataset;
|
|
@@ -1804,7 +1815,7 @@ class DoughnutChartVisualisation extends ChartVisualisation {
|
|
|
1804
1815
|
ctx.setHoverValue({ label, value: toolipValue, legend, ctx });
|
|
1805
1816
|
} else {
|
|
1806
1817
|
toolipValue = `${toolipValue}`;
|
|
1807
|
-
ctx.setHoverValue({ label, value: toolipValue, ctx });
|
|
1818
|
+
ctx.setHoverValue({ label, value: toolipValue, legend, ctx });
|
|
1808
1819
|
}
|
|
1809
1820
|
|
|
1810
1821
|
return toolipValue
|
|
@@ -1818,13 +1829,18 @@ class DoughnutChartVisualisation extends ChartVisualisation {
|
|
|
1818
1829
|
textAlign: 'center',
|
|
1819
1830
|
color: '#FFF',
|
|
1820
1831
|
display: (context) => {
|
|
1821
|
-
const
|
|
1822
|
-
const
|
|
1832
|
+
const { dataset, dataIndex } = context;
|
|
1833
|
+
const value = dataset.data[dataIndex];
|
|
1834
|
+
const total = dataset.data.reduce((a, c) => a + c, 0);
|
|
1823
1835
|
const percentage = (value / total) * 100;
|
|
1824
1836
|
return percentage > 4
|
|
1825
1837
|
},
|
|
1826
|
-
formatter: (value) => {
|
|
1827
|
-
|
|
1838
|
+
formatter: (value, context) => {
|
|
1839
|
+
const { dataset } = context;
|
|
1840
|
+
const label = `${value}${this.suffix}
|
|
1841
|
+
${dataset.label}`;
|
|
1842
|
+
|
|
1843
|
+
return label
|
|
1828
1844
|
},
|
|
1829
1845
|
labels: {
|
|
1830
1846
|
title: {
|
|
@@ -3,67 +3,114 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const Charts_1 = require("../../types/Charts");
|
|
4
4
|
exports.default = {
|
|
5
5
|
getChartData: ({ definition, metric, }) => {
|
|
6
|
-
const { id, display: title, description,
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
const { id, display: title, description, specification } = definition;
|
|
7
|
+
const values = getValues(specification, metric);
|
|
8
|
+
const chartsValues = getChartsData(values);
|
|
9
|
+
const chartsData = chartsValues.map((value) => {
|
|
10
|
+
return createVisualisationData(value);
|
|
11
|
+
});
|
|
12
|
+
return {
|
|
12
13
|
id,
|
|
13
14
|
title,
|
|
14
15
|
description,
|
|
15
|
-
type,
|
|
16
|
-
unit,
|
|
17
16
|
data: {
|
|
18
|
-
chart:
|
|
19
|
-
|
|
20
|
-
datasets,
|
|
21
|
-
},
|
|
22
|
-
table,
|
|
17
|
+
chart: chartsData,
|
|
18
|
+
table: createTable(values),
|
|
23
19
|
},
|
|
24
20
|
};
|
|
25
|
-
return chartCardData;
|
|
26
21
|
},
|
|
27
22
|
};
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
23
|
+
const getChartsData = (values) => {
|
|
24
|
+
const availableChartTypes = [Charts_1.ChartType.BAR, Charts_1.ChartType.DONUT, Charts_1.ChartType.LINE];
|
|
25
|
+
const chartsValues = [];
|
|
26
|
+
availableChartTypes.forEach((chartType) => {
|
|
27
|
+
const chartData = {
|
|
28
|
+
type: chartType,
|
|
29
|
+
datasets: [],
|
|
30
|
+
};
|
|
31
|
+
values.forEach((value) => {
|
|
32
|
+
const chartValues = value.data.filter((valueData) => {
|
|
33
|
+
var _a;
|
|
34
|
+
return (_a = valueData.chart) === null || _a === void 0 ? void 0 : _a.includes(chartType);
|
|
35
|
+
});
|
|
36
|
+
if (chartValues.length)
|
|
37
|
+
chartData.datasets.push({ data: chartValues, group: value.group });
|
|
38
|
+
});
|
|
39
|
+
if (chartData.datasets.length)
|
|
40
|
+
chartsValues.push(chartData);
|
|
31
41
|
});
|
|
42
|
+
return chartsValues;
|
|
32
43
|
};
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
44
|
+
const getValues = (specification, metric) => {
|
|
45
|
+
return metric.data.map((d) => {
|
|
46
|
+
const groupArray = [];
|
|
47
|
+
let groupValue = '';
|
|
48
|
+
let groupName = '';
|
|
49
|
+
Object.entries(d).forEach((attr) => {
|
|
50
|
+
const specificationData = specification.find((spec) => {
|
|
51
|
+
return spec.name === attr[0];
|
|
52
|
+
});
|
|
53
|
+
if (specificationData.group) {
|
|
54
|
+
groupName = specificationData.display;
|
|
55
|
+
groupValue = `${attr[1]}`;
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
groupArray.push({
|
|
59
|
+
...specificationData,
|
|
60
|
+
value: attr[1],
|
|
61
|
+
});
|
|
62
|
+
}
|
|
41
63
|
});
|
|
42
|
-
|
|
64
|
+
return {
|
|
65
|
+
...(groupName.length && { group: { name: groupName, value: groupValue } }),
|
|
66
|
+
data: groupArray,
|
|
67
|
+
};
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
const createVisualisationData = (value) => {
|
|
71
|
+
const labels = value.datasets[0].data.map((d) => {
|
|
72
|
+
return d.display;
|
|
73
|
+
});
|
|
74
|
+
let unit;
|
|
75
|
+
const datasets = value.datasets.map((d) => {
|
|
76
|
+
const data = d.data.map((v) => +v.value);
|
|
77
|
+
unit = d.data[0].unit;
|
|
78
|
+
const label = d.group ? d.group.value : '';
|
|
79
|
+
return {
|
|
43
80
|
label,
|
|
44
81
|
data,
|
|
45
82
|
total: data.reduce((acc, val) => acc + val, 0),
|
|
46
|
-
}
|
|
83
|
+
};
|
|
47
84
|
});
|
|
48
|
-
|
|
85
|
+
const visData = {
|
|
86
|
+
type: value.type,
|
|
87
|
+
unit,
|
|
88
|
+
data: {
|
|
89
|
+
labels,
|
|
90
|
+
datasets,
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
return visData;
|
|
49
94
|
};
|
|
50
|
-
const createTable = (
|
|
51
|
-
const { specification } = definition;
|
|
95
|
+
const createTable = (values) => {
|
|
52
96
|
const head = [];
|
|
53
|
-
const suffix = setSuffix(unit);
|
|
54
|
-
Object.entries(metric.data[0]).forEach((key) => {
|
|
55
|
-
const name = `${key[0]}`;
|
|
56
|
-
const spec = specification.find((s) => s.name === name);
|
|
57
|
-
const text = spec ? spec.display : name;
|
|
58
|
-
head.push({ text });
|
|
59
|
-
});
|
|
60
97
|
const rows = [];
|
|
61
|
-
//
|
|
62
|
-
|
|
98
|
+
// Head
|
|
99
|
+
if (values[0].group) {
|
|
100
|
+
head.push({ text: values[0].group.name });
|
|
101
|
+
}
|
|
102
|
+
values[0].data.forEach((v) => {
|
|
103
|
+
head.push({ text: v.display });
|
|
104
|
+
});
|
|
105
|
+
// Rows
|
|
106
|
+
values.forEach((v) => {
|
|
63
107
|
const row = [];
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
108
|
+
if (v.group) {
|
|
109
|
+
row.push({ text: v.group.value });
|
|
110
|
+
}
|
|
111
|
+
v.data.forEach((d) => {
|
|
112
|
+
const suffix = setSuffix(d.unit);
|
|
113
|
+
row.push({ text: `${d.value}${suffix}` });
|
|
67
114
|
});
|
|
68
115
|
rows.push(row);
|
|
69
116
|
});
|
|
@@ -1,4 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
ChartCardData,
|
|
3
|
+
ChartUnit,
|
|
4
|
+
MoJTableHead,
|
|
5
|
+
MoJTableRow,
|
|
6
|
+
ChartDataset,
|
|
7
|
+
ChartType,
|
|
8
|
+
ChartData,
|
|
9
|
+
ChartCardValue,
|
|
10
|
+
ChartsData,
|
|
11
|
+
ChartValue,
|
|
12
|
+
ChartGroup,
|
|
13
|
+
} from '../../types/Charts'
|
|
2
14
|
import { MetricsDataResponse, MetricsDefinition, MetricsDefinitionSpecification } from '../../types/Metrics'
|
|
3
15
|
|
|
4
16
|
export default {
|
|
@@ -9,79 +21,126 @@ export default {
|
|
|
9
21
|
definition: MetricsDefinition
|
|
10
22
|
metric: MetricsDataResponse
|
|
11
23
|
}): ChartCardData => {
|
|
12
|
-
const { id, display: title, description,
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
24
|
+
const { id, display: title, description, specification } = definition
|
|
25
|
+
const values = getValues(specification, metric)
|
|
26
|
+
const chartsValues = getChartsData(values)
|
|
27
|
+
const chartsData = chartsValues.map((value: ChartsData) => {
|
|
28
|
+
return createVisualisationData(value)
|
|
29
|
+
})
|
|
18
30
|
|
|
19
|
-
|
|
31
|
+
return {
|
|
20
32
|
id,
|
|
21
33
|
title,
|
|
22
34
|
description,
|
|
23
|
-
type,
|
|
24
|
-
unit,
|
|
25
35
|
data: {
|
|
26
|
-
chart:
|
|
27
|
-
|
|
28
|
-
datasets,
|
|
29
|
-
},
|
|
30
|
-
table,
|
|
36
|
+
chart: chartsData,
|
|
37
|
+
table: createTable(values),
|
|
31
38
|
},
|
|
32
39
|
}
|
|
33
|
-
|
|
34
|
-
return chartCardData
|
|
35
40
|
},
|
|
36
41
|
}
|
|
37
42
|
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
43
|
+
const getChartsData = (values: ChartValue[]) => {
|
|
44
|
+
const availableChartTypes = [ChartType.BAR, ChartType.DONUT, ChartType.LINE]
|
|
45
|
+
|
|
46
|
+
const chartsValues: ChartsData[] = []
|
|
47
|
+
availableChartTypes.forEach((chartType: ChartType) => {
|
|
48
|
+
const chartData = {
|
|
49
|
+
type: chartType,
|
|
50
|
+
datasets: [] as { data: ChartCardValue[]; group: ChartGroup }[],
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
values.forEach((value: ChartValue) => {
|
|
54
|
+
const chartValues = value.data.filter((valueData: ChartCardValue) => {
|
|
55
|
+
return valueData.chart?.includes(chartType)
|
|
56
|
+
})
|
|
57
|
+
if (chartValues.length) chartData.datasets.push({ data: chartValues, group: value.group })
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
if (chartData.datasets.length) chartsValues.push(chartData)
|
|
41
61
|
})
|
|
62
|
+
|
|
63
|
+
return chartsValues
|
|
42
64
|
}
|
|
43
65
|
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
66
|
+
const getValues = (specification: MetricsDefinitionSpecification[], metric: MetricsDataResponse): ChartValue[] => {
|
|
67
|
+
return metric.data.map((d) => {
|
|
68
|
+
const groupArray: ChartCardValue[] = []
|
|
69
|
+
let groupValue = ''
|
|
70
|
+
let groupName = ''
|
|
47
71
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
72
|
+
Object.entries(d).forEach((attr) => {
|
|
73
|
+
const specificationData = specification.find((spec: MetricsDefinitionSpecification) => {
|
|
74
|
+
return spec.name === attr[0]
|
|
75
|
+
})
|
|
76
|
+
if (specificationData.group) {
|
|
77
|
+
groupName = specificationData.display
|
|
78
|
+
groupValue = `${attr[1]}`
|
|
79
|
+
} else {
|
|
80
|
+
groupArray.push({
|
|
81
|
+
...specificationData,
|
|
82
|
+
value: attr[1],
|
|
83
|
+
})
|
|
84
|
+
}
|
|
53
85
|
})
|
|
54
|
-
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
...(groupName.length && { group: { name: groupName, value: groupValue } }),
|
|
89
|
+
data: groupArray,
|
|
90
|
+
}
|
|
91
|
+
})
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const createVisualisationData = (value: ChartsData): ChartData => {
|
|
95
|
+
const labels: string[] = value.datasets[0].data.map((d: ChartCardValue) => {
|
|
96
|
+
return d.display
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
let unit
|
|
100
|
+
const datasets: ChartDataset[] = value.datasets.map((d) => {
|
|
101
|
+
const data = d.data.map((v) => +v.value)
|
|
102
|
+
unit = d.data[0].unit
|
|
103
|
+
const label = d.group ? d.group.value : ''
|
|
104
|
+
return {
|
|
55
105
|
label,
|
|
56
106
|
data,
|
|
57
107
|
total: data.reduce((acc: number, val: number) => acc + val, 0),
|
|
58
|
-
}
|
|
108
|
+
}
|
|
59
109
|
})
|
|
60
110
|
|
|
61
|
-
|
|
111
|
+
const visData: ChartData = {
|
|
112
|
+
type: value.type,
|
|
113
|
+
unit,
|
|
114
|
+
data: {
|
|
115
|
+
labels,
|
|
116
|
+
datasets,
|
|
117
|
+
},
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return visData
|
|
62
121
|
}
|
|
63
122
|
|
|
64
|
-
const createTable = (
|
|
65
|
-
const { specification } = definition
|
|
123
|
+
const createTable = (values: ChartValue[]) => {
|
|
66
124
|
const head: MoJTableHead[] = []
|
|
67
|
-
const
|
|
125
|
+
const rows: MoJTableRow[][] = []
|
|
68
126
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
127
|
+
// Head
|
|
128
|
+
if (values[0].group) {
|
|
129
|
+
head.push({ text: values[0].group.name })
|
|
130
|
+
}
|
|
131
|
+
values[0].data.forEach((v) => {
|
|
132
|
+
head.push({ text: v.display })
|
|
74
133
|
})
|
|
75
134
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
79
|
-
metric.data.forEach((item: any) => {
|
|
135
|
+
// Rows
|
|
136
|
+
values.forEach((v) => {
|
|
80
137
|
const row: MoJTableRow[] = []
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
138
|
+
if (v.group) {
|
|
139
|
+
row.push({ text: v.group.value })
|
|
140
|
+
}
|
|
141
|
+
v.data.forEach((d) => {
|
|
142
|
+
const suffix = setSuffix(d.unit)
|
|
143
|
+
row.push({ text: `${d.value}${suffix}` })
|
|
85
144
|
})
|
|
86
145
|
rows.push(row)
|
|
87
146
|
})
|
|
@@ -7,11 +7,9 @@
|
|
|
7
7
|
{% macro dprChartCard(chart) %}
|
|
8
8
|
{% set id = chart.id %}
|
|
9
9
|
{% set title = chart.title %}
|
|
10
|
-
{% set type = chart.type %}
|
|
11
10
|
{% set data = chart.data %}
|
|
12
11
|
{% set description = chart.description %}
|
|
13
12
|
{% set details = chart.details %}
|
|
14
|
-
{% set unit = chart.unit %}
|
|
15
13
|
|
|
16
14
|
<div class="dpr-chart-card" id="{{ id }}-chart-card">
|
|
17
15
|
<h2 class="govuk-heading-m">{{ title }}</h2>
|
|
@@ -22,7 +20,7 @@
|
|
|
22
20
|
|
|
23
21
|
<div class="chart-tabs-container">
|
|
24
22
|
|
|
25
|
-
{{ dprChartTabs(id,
|
|
23
|
+
{{ dprChartTabs(id, data, unit) }}
|
|
26
24
|
|
|
27
25
|
<div class="chart-tabs-details">
|
|
28
26
|
{% set headlines = details.headlines %}
|
|
@@ -31,7 +29,6 @@
|
|
|
31
29
|
<div id="dpr-{{ id }}-tooltip-details" class="chart-tabs-details__item chart-tabs-details__item--tooltip">
|
|
32
30
|
<h3 id="dpr-{{ id }}-label" class="govuk-heading-s dpr-chart-details-label">init</h3>
|
|
33
31
|
<p id="dpr-{{ id }}-value" class="govuk-body dpr-chart-details-value">init</p>
|
|
34
|
-
<p id="dpr-{{ id }}-legend" class="govuk-body-s dpr-chart-details-legend">init</p>
|
|
35
32
|
</div>
|
|
36
33
|
|
|
37
34
|
<div id="dpr-{{ id }}-headline-values" class="chart-tabs-details__item chart-tabs-details__item--headlines">
|
|
@@ -2,19 +2,24 @@
|
|
|
2
2
|
{% from "govuk/components/tabs/macro.njk" import govukTabs %}
|
|
3
3
|
{% from "govuk/components/table/macro.njk" import govukTable %}
|
|
4
4
|
|
|
5
|
-
{% macro dprChartTabs(id,
|
|
5
|
+
{% macro dprChartTabs(id, data, unit) %}
|
|
6
6
|
{% set chartData = data.chart %}
|
|
7
7
|
{% set tableData = data.table %}
|
|
8
8
|
|
|
9
9
|
{% set tabItems = [] %}
|
|
10
|
-
{% for
|
|
10
|
+
{% for chart in chartData %}
|
|
11
|
+
|
|
12
|
+
{% set type = chart.type %}
|
|
13
|
+
{% set data = chart.data %}
|
|
14
|
+
{% set unit = chart.unit %}
|
|
15
|
+
|
|
11
16
|
{% set chartHtml %}
|
|
12
|
-
{{ dprChart(id,
|
|
17
|
+
{{ dprChart(id, type, data, unit) }}
|
|
13
18
|
{% endset -%}
|
|
14
19
|
|
|
15
20
|
{% set tabContent = {
|
|
16
|
-
label:
|
|
17
|
-
id: id + "_" +
|
|
21
|
+
label: type | capitalize,
|
|
22
|
+
id: id + "_" + type + "_tab",
|
|
18
23
|
panel: {
|
|
19
24
|
html: chartHtml
|
|
20
25
|
}
|
package/dpr/types/Charts.ts
CHANGED
|
@@ -1,22 +1,25 @@
|
|
|
1
1
|
interface Chart {
|
|
2
2
|
id: string
|
|
3
|
-
type: ChartType
|
|
4
|
-
unit: ChartUnit
|
|
5
3
|
data: {
|
|
6
|
-
chart: ChartData
|
|
4
|
+
chart: ChartData[]
|
|
7
5
|
}
|
|
8
6
|
}
|
|
9
7
|
|
|
8
|
+
export interface ChartData {
|
|
9
|
+
type: ChartType
|
|
10
|
+
unit: ChartUnit
|
|
11
|
+
data: ChartDataValues
|
|
12
|
+
}
|
|
13
|
+
|
|
10
14
|
export enum ChartUnit {
|
|
11
15
|
NUMBER = 'number',
|
|
12
16
|
PERCENTAGE = 'percentage',
|
|
13
17
|
}
|
|
14
18
|
|
|
15
19
|
interface ChartTabs extends Omit<Chart, 'type'> {
|
|
16
|
-
type: ChartType[]
|
|
17
20
|
data: {
|
|
18
|
-
chart: ChartData
|
|
19
|
-
table
|
|
21
|
+
chart: ChartData[]
|
|
22
|
+
table?: MoJTable
|
|
20
23
|
}
|
|
21
24
|
}
|
|
22
25
|
|
|
@@ -34,7 +37,7 @@ interface ChartCardDetailsItem {
|
|
|
34
37
|
value: string | number
|
|
35
38
|
}
|
|
36
39
|
|
|
37
|
-
interface
|
|
40
|
+
export interface ChartDataValues {
|
|
38
41
|
labels: string[]
|
|
39
42
|
datasets: ChartDataset[]
|
|
40
43
|
axis?: 'x' | 'y'
|
|
@@ -66,3 +69,26 @@ export interface MoJTableHead {
|
|
|
66
69
|
text?: string
|
|
67
70
|
html?: string
|
|
68
71
|
}
|
|
72
|
+
|
|
73
|
+
export interface ChartCardValue {
|
|
74
|
+
value: string | number
|
|
75
|
+
name: string
|
|
76
|
+
display: string
|
|
77
|
+
unit?: ChartUnit
|
|
78
|
+
chart?: ChartType[]
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface ChartValue {
|
|
82
|
+
group?: ChartGroup
|
|
83
|
+
data: ChartCardValue[]
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface ChartGroup {
|
|
87
|
+
name: string
|
|
88
|
+
value: string
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface ChartsData {
|
|
92
|
+
type: ChartType
|
|
93
|
+
datasets: { data: ChartCardValue[]; group: ChartGroup }[]
|
|
94
|
+
}
|
package/dpr/types/Dashboards.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { ChartType } from './Charts'
|
|
2
|
-
|
|
3
1
|
export interface DashboardDefinition {
|
|
4
2
|
id: string
|
|
5
3
|
name: string
|
|
@@ -9,5 +7,4 @@ export interface DashboardDefinition {
|
|
|
9
7
|
|
|
10
8
|
export interface DashboardMetricDefinition {
|
|
11
9
|
id: string
|
|
12
|
-
visualisationType: ChartType[]
|
|
13
10
|
}
|
package/dpr/types/Metrics.ts
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import { ChartType, ChartUnit } from './Charts'
|
|
2
|
-
import Dict = NodeJS.Dict
|
|
3
2
|
|
|
4
3
|
export interface MetricsDefinition {
|
|
5
4
|
id: string
|
|
6
5
|
name: string
|
|
7
6
|
display: string
|
|
8
7
|
description: string
|
|
9
|
-
visualisationType: ChartType[]
|
|
10
8
|
specification: MetricsDefinitionSpecification[]
|
|
11
9
|
}
|
|
12
10
|
|
|
@@ -14,10 +12,12 @@ export interface MetricsDefinitionSpecification {
|
|
|
14
12
|
name: string
|
|
15
13
|
display: string
|
|
16
14
|
unit?: ChartUnit
|
|
15
|
+
group?: true
|
|
16
|
+
chart?: ChartType[]
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export interface MetricsDataResponse {
|
|
20
20
|
id: string
|
|
21
|
-
data:
|
|
21
|
+
data: { [key: string]: number | string }[]
|
|
22
22
|
updated: string
|
|
23
23
|
}
|
|
@@ -11,10 +11,8 @@ exports.default = {
|
|
|
11
11
|
const metrics = JSON.parse(req.body.metrics);
|
|
12
12
|
const { title, description } = req.body;
|
|
13
13
|
const metricsData = await Promise.all(metrics.map(async (metric) => {
|
|
14
|
-
const { visualisationType: type } = metric;
|
|
15
14
|
return metricsUtils_1.default.getMetricData({
|
|
16
15
|
id: metric.id,
|
|
17
|
-
type,
|
|
18
16
|
req,
|
|
19
17
|
res,
|
|
20
18
|
services,
|
|
@@ -11,10 +11,8 @@ export default {
|
|
|
11
11
|
const { title, description } = req.body
|
|
12
12
|
const metricsData = await Promise.all(
|
|
13
13
|
metrics.map(async (metric: DashboardMetricDefinition) => {
|
|
14
|
-
const { visualisationType: type } = metric
|
|
15
14
|
return MetricsUtils.getMetricData({
|
|
16
15
|
id: metric.id,
|
|
17
|
-
type,
|
|
18
16
|
req,
|
|
19
17
|
res,
|
|
20
18
|
services,
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.default = {
|
|
4
|
-
getMetricData: async ({ id,
|
|
4
|
+
getMetricData: async ({ id, req, res, services, next, }) => {
|
|
5
5
|
var _a;
|
|
6
6
|
const token = ((_a = res.locals.user) === null || _a === void 0 ? void 0 : _a.token) ? res.locals.user.token : 'token';
|
|
7
7
|
const { dpdId } = req.params;
|
|
8
8
|
const { dataProductDefinitionsPath } = req.query;
|
|
9
9
|
const definition = await services.metricService.getDefinition(token, id, dpdId, dataProductDefinitionsPath);
|
|
10
|
-
if (type)
|
|
11
|
-
definition.visualisationType = type;
|
|
12
10
|
const metric = await services.metricService.getMetricData(token, id, dpdId);
|
|
13
11
|
return {
|
|
14
12
|
definition,
|
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
import { AsyncReportUtilsParams } from '../types/AsyncReportUtils'
|
|
2
|
-
import { ChartType } from '../types/Charts'
|
|
3
2
|
import { MetricsDataResponse, MetricsDefinition } from '../types/Metrics'
|
|
4
3
|
|
|
5
4
|
export default {
|
|
6
5
|
getMetricData: async ({
|
|
7
6
|
id,
|
|
8
|
-
type,
|
|
9
7
|
req,
|
|
10
8
|
res,
|
|
11
9
|
services,
|
|
12
10
|
next,
|
|
13
|
-
}: { id: string
|
|
11
|
+
}: { id: string } & AsyncReportUtilsParams): Promise<{
|
|
14
12
|
definition: MetricsDefinition
|
|
15
13
|
metric: MetricsDataResponse
|
|
16
14
|
}> => {
|
|
@@ -19,7 +17,6 @@ export default {
|
|
|
19
17
|
const { dataProductDefinitionsPath } = req.query
|
|
20
18
|
|
|
21
19
|
const definition = await services.metricService.getDefinition(token, id, dpdId, <string>dataProductDefinitionsPath)
|
|
22
|
-
if (type) definition.visualisationType = type
|
|
23
20
|
|
|
24
21
|
const metric = await services.metricService.getMetricData(token, id, dpdId)
|
|
25
22
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ministryofjustice/hmpps-digital-prison-reporting-frontend",
|
|
3
3
|
"description": "The Digital Prison Reporting Frontend contains templates and code to help display data effectively in UI applications.",
|
|
4
|
-
"version": "3.26.
|
|
4
|
+
"version": "3.26.2",
|
|
5
5
|
"main": "dpr/assets/js/all.mjs",
|
|
6
6
|
"sass": "dpr/all.scss",
|
|
7
7
|
"engines": {
|
package/package.zip
CHANGED
|
Binary file
|