@ministryofjustice/hmpps-digital-prison-reporting-frontend 4.15.3 → 4.16.0

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.
Files changed (47) hide show
  1. package/dpr/components/_async/async-filters-form/view.njk +2 -2
  2. package/dpr/components/_catalogue/catalogue-list/utils.test.ts +1 -1
  3. package/dpr/components/_charts/chart/Buckets.js +2 -0
  4. package/dpr/components/_charts/chart/Buckets.js.map +7 -0
  5. package/dpr/components/_charts/chart/Buckets.ts +198 -0
  6. package/dpr/components/_charts/chart/DashboardVisualisation.js +2 -0
  7. package/dpr/components/_charts/chart/DashboardVisualisation.js.map +7 -0
  8. package/dpr/components/_charts/chart/DashboardVisualisation.ts +43 -0
  9. package/dpr/components/_charts/chart/heatmap/HeatmapChart.js +2 -0
  10. package/dpr/components/_charts/chart/heatmap/HeatmapChart.js.map +7 -0
  11. package/dpr/components/_charts/chart/heatmap/HeatmapChart.ts +144 -0
  12. package/dpr/components/_charts/utils.js +1 -1
  13. package/dpr/components/_charts/utils.js.map +3 -3
  14. package/dpr/components/_charts/utils.ts +1 -1
  15. package/dpr/components/_dashboards/dashboard/types.js.map +1 -1
  16. package/dpr/components/_dashboards/dashboard/types.ts +4 -4
  17. package/dpr/components/_dashboards/scorecard/Scorecard.js +2 -0
  18. package/dpr/components/_dashboards/scorecard/Scorecard.js.map +7 -0
  19. package/dpr/components/_dashboards/scorecard/Scorecard.ts +315 -0
  20. package/dpr/components/_dashboards/scorecard/types.js +1 -1
  21. package/dpr/components/_dashboards/scorecard/types.js.map +1 -1
  22. package/dpr/components/_dashboards/scorecard/types.ts +20 -1
  23. package/dpr/components/_dashboards/scorecard/utils.js +1 -1
  24. package/dpr/components/_dashboards/scorecard/utils.js.map +3 -3
  25. package/dpr/components/_dashboards/scorecard/utils.test.ts +8 -437
  26. package/dpr/components/_dashboards/scorecard/utils.ts +2 -265
  27. package/dpr/components/_dashboards/scorecard/view.njk +2 -2
  28. package/dpr/components/_filters/types.d.js.map +1 -1
  29. package/dpr/components/_filters/types.d.ts +1 -0
  30. package/dpr/components/_filters/utils.js +1 -1
  31. package/dpr/components/_filters/utils.js.map +3 -3
  32. package/dpr/components/_filters/utils.ts +17 -12
  33. package/dpr/routes/journeys/request-report/filters/tests.cy.js +1 -1
  34. package/dpr/routes/journeys/request-report/filters/tests.cy.js.map +3 -3
  35. package/dpr/routes/journeys/request-report/filters/tests.cy.ts +40 -0
  36. package/dpr/routes/journeys/view-report/async/dashboard/utils.js +1 -1
  37. package/dpr/routes/journeys/view-report/async/dashboard/utils.js.map +3 -3
  38. package/dpr/routes/journeys/view-report/async/dashboard/utils.ts +3 -2
  39. package/dpr/types/api.d.js.map +1 -1
  40. package/dpr/types/api.d.ts +2 -0
  41. package/dpr/utils/datasetHelper.js +1 -1
  42. package/dpr/utils/datasetHelper.js.map +2 -2
  43. package/dpr/utils/datasetHelper.ts +1 -1
  44. package/package.json +1 -1
  45. package/dpr/components/_charts/chart/heatmap/Heatmap.js +0 -2
  46. package/dpr/components/_charts/chart/heatmap/Heatmap.js.map +0 -7
  47. package/dpr/components/_charts/chart/heatmap/Heatmap.ts +0 -278
@@ -1,278 +0,0 @@
1
- /* eslint-disable prefer-destructuring */
2
- import dayjs from 'dayjs'
3
- import { withAlphaHex } from 'with-alpha-hex'
4
- import { Granularity } from '../../../_inputs/granular-date-range/types'
5
- import { DashboardDataResponse } from '../../../../types/Metrics'
6
- import {
7
- DashboardVisualisation,
8
- DashboardVisualisationColumns,
9
- DashboardVisualisationType,
10
- MatrixDashboardVisualisationBucket,
11
- MatrixDashboardVisualisationOptions,
12
- } from '../../../_dashboards/dashboard/types'
13
- import { ChartData, ChartType, MatrixChartData, UnitType } from '../../../../types/Charts'
14
- import DatasetHelper from '../../../../utils/datasetHelper'
15
-
16
- class HeatmapChart {
17
- private baseColour = '#1d70b8'
18
-
19
- private ragColours: string[] = ['#00703c', '#ffdd00', '#d4351c']
20
-
21
- private buckets: MatrixDashboardVisualisationBucket[] = []
22
-
23
- private bucketCount: number
24
-
25
- private onlyBucketColoursDefined: boolean
26
-
27
- private granularity: Granularity
28
-
29
- private responseData: DashboardDataResponse[]
30
-
31
- private data: MatrixChartData[] = []
32
-
33
- private definition: DashboardVisualisation
34
-
35
- private columns: DashboardVisualisationColumns
36
-
37
- private unit: UnitType
38
-
39
- private type: ChartType
40
-
41
- private dayDateFormat = 'DD/MM/YYYY'
42
-
43
- private hasRag = false
44
-
45
- private valueKey: string
46
-
47
- private options: MatrixDashboardVisualisationOptions = {}
48
-
49
- private label: string
50
-
51
- constructor(responseData: DashboardDataResponse[], granularity: Granularity, definition: DashboardVisualisation) {
52
- this.granularity = granularity
53
- this.initFromDefinition(definition)
54
- this.initFromResponseData(responseData)
55
- this.initBuckets()
56
- }
57
-
58
- private initFromResponseData = (responseData: DashboardDataResponse[]) => {
59
- this.hasRag = responseData[0][this.valueKey].rag !== undefined
60
- this.responseData = responseData
61
- }
62
-
63
- private initFromDefinition = (definition: DashboardVisualisation) => {
64
- this.definition = definition
65
- this.columns = definition.columns
66
- this.unit = this.columns.measures[0].unit ? this.columns.measures[0].unit : undefined
67
- this.type = this.definition.type.split('-')[0] as ChartType
68
-
69
- this.options = <MatrixDashboardVisualisationOptions>definition.options
70
- this.baseColour = this.options.baseColour || this.baseColour
71
- this.valueKey = this.columns.measures[1].id
72
- this.label = this.columns.measures[1].display
73
- this.onlyBucketColoursDefined = this.options?.buckets?.every(
74
- (bucket) => !bucket.max && !bucket.min && bucket.hexColour !== undefined,
75
- )
76
- }
77
-
78
- private initBuckets = () => {
79
- const { buckets } = this.options
80
- this.setBucketCount()
81
- this.initBucketColours()
82
- if (buckets) {
83
- if (this.onlyBucketColoursDefined) {
84
- this.initAutomaticThresholdBucket()
85
- } else {
86
- this.initCustomThresholdBuckets()
87
- }
88
- } else if (!this.hasRag) this.initAutomaticThresholdBucket()
89
- }
90
-
91
- private initCustomThresholdBuckets = () => {
92
- this.buckets = this.options.buckets.map((bucket, i) => {
93
- return {
94
- ...this.buckets[i],
95
- ...bucket,
96
- }
97
- })
98
- }
99
-
100
- /**
101
- * Initialises the bucket thresholds by defining the range between the min and max
102
- * and dividing into 3 equal parts
103
- */
104
- private initAutomaticThresholdBucket = () => {
105
- const { min, max, bucketSize } = this.setAutomaticThresholdSize()
106
- let maxValue = 0
107
- this.buckets = this.buckets.map((bucket, i) => {
108
- let minValue = min
109
- if (i !== 0) minValue = maxValue + 1
110
- maxValue = bucketSize * (i + 1)
111
- if (i === this.buckets.length - 1) maxValue = max
112
-
113
- return {
114
- hexColour: this.options?.buckets ? this.options.buckets[i]?.hexColour : bucket.hexColour,
115
- min: minValue,
116
- max: maxValue,
117
- }
118
- })
119
- }
120
-
121
- private initBucketColours = () => {
122
- const { useRagColours } = this.options
123
- if (useRagColours && this.bucketCount === 3) {
124
- this.buckets = Array.from(new Array(this.bucketCount)).map((d, i) => {
125
- return {
126
- hexColour: this.ragColours[i],
127
- }
128
- })
129
- } else {
130
- const alphaDivision = 1 / this.bucketCount
131
- this.buckets = Array.from(new Array(this.bucketCount)).map((d, i) => {
132
- const division = alphaDivision * (i + 1)
133
- return {
134
- hexColour: withAlphaHex(this.baseColour, division),
135
- }
136
- })
137
- }
138
- }
139
-
140
- private setAutomaticThresholdSize = () => {
141
- const values = this.responseData.map((resData) => Number(resData[this.valueKey].raw))
142
- const min = Math.min(...values)
143
- const max = Math.max(...values)
144
- const bucketSize = Math.ceil((max - min) / this.bucketCount)
145
-
146
- return {
147
- min,
148
- max,
149
- bucketSize,
150
- }
151
- }
152
-
153
- private setBucketCount = () => {
154
- const { buckets } = this.options
155
- if (this.hasRag) {
156
- this.bucketCount =
157
- Math.max(...this.responseData.map((resData: DashboardDataResponse) => resData[this.valueKey].rag)) + 1
158
- } else if (buckets) {
159
- this.bucketCount = buckets.length
160
- } else {
161
- this.bucketCount = 3
162
- }
163
- }
164
-
165
- private validateDefinition = () => {
166
- const { id, columns, type } = this.definition
167
- const errors = []
168
-
169
- // Validate measures
170
- if (columns.measures.length !== 2) {
171
- errors.push(`Measures should only have 2 columns defined. Only found ${columns.measures.length}`)
172
- } else if (type === DashboardVisualisationType.MATRIX_TIMESERIES) {
173
- if (columns.measures[0].id !== 'ts') {
174
- errors.push(`measure at index 0 has incorrect ID. Expected ID to be "ts". Found "${columns.measures[0].id}"`)
175
- }
176
- }
177
-
178
- // Throw the error
179
- if (errors.length) {
180
- const message = `Validation: Visualisaton definition: ID: ${id}, type: ${type}, errors: ${errors.join(',')}`
181
- throw new Error(message)
182
- }
183
- }
184
-
185
- private initTimeseriesData = () => {
186
- const timeBlockData = DatasetHelper.groupRowsByTimestamp(this.responseData)
187
-
188
- this.data = timeBlockData.map((tsData) => {
189
- const { raw, rag } = tsData[0][this.valueKey]
190
- const tsRaw = tsData[0].ts.raw
191
-
192
- const v = Number(raw)
193
- const r = rag !== undefined ? Number(tsData[0][this.valueKey].rag) : undefined
194
- let x
195
- let y
196
-
197
- switch (this.granularity) {
198
- case 'hourly':
199
- break
200
- case 'weekly':
201
- x = dayjs(tsRaw, this.dayDateFormat).format('ddd')
202
- y = dayjs(tsRaw, this.dayDateFormat).week()
203
- break
204
- case 'daily':
205
- x = dayjs(tsRaw, this.dayDateFormat).format('MMM YY')
206
- y = dayjs(tsRaw, this.dayDateFormat).format('D')
207
- break
208
- case 'monthly':
209
- {
210
- const ts = (<string>tsRaw).split(' ')
211
- x = ts[1]
212
- y = ts[0]
213
- }
214
- break
215
- case 'annually':
216
- x = 'year'
217
- y = <string>tsRaw
218
- break
219
- default:
220
- x = dayjs(tsRaw, this.dayDateFormat).format('MMM YY')
221
- y = dayjs(tsRaw, this.dayDateFormat).format('D')
222
- break
223
- }
224
- return { y, x, v, r }
225
- })
226
- }
227
-
228
- private binDataIntoBuckets = () => {
229
- this.data = this.data.map((d) => {
230
- const { v, r } = d
231
- let c
232
- if (r !== undefined) {
233
- c = this.buckets[r].hexColour
234
- } else {
235
- this.buckets.forEach((bucket) => {
236
- const { min, max } = bucket
237
- // First bucket
238
- if (!min && max && v <= max) {
239
- c = bucket.hexColour
240
- }
241
- // middle buckets
242
- if (min && v >= bucket.min && max && v <= bucket.max) {
243
- c = bucket.hexColour
244
- }
245
- // last bucket
246
- if (min && !max && v >= min) {
247
- c = bucket.hexColour
248
- }
249
- })
250
- }
251
-
252
- return { ...d, c }
253
- })
254
- }
255
-
256
- build = (): ChartData => {
257
- this.validateDefinition()
258
- this.initTimeseriesData()
259
- this.binDataIntoBuckets()
260
-
261
- return {
262
- type: this.type,
263
- unit: this.unit,
264
- timeseries: true,
265
- data: {
266
- datasets: [
267
- {
268
- label: this.label,
269
- data: this.data,
270
- },
271
- ],
272
- },
273
- }
274
- }
275
- }
276
-
277
- export { HeatmapChart }
278
- export default HeatmapChart