@ministryofjustice/hmpps-digital-prison-reporting-frontend
Version:
The Digital Prison Reporting Frontend contains templates and code to help display data effectively in UI applications.
309 lines (274 loc) • 9.36 kB
text/typescript
import { DashboardDataResponse } from '../../../types/Metrics'
import {
DashboardUIVisualisation,
DashboardVisualisation,
DashboardVisualisationColumn,
DashboardVisualisationColumns,
DashboardVisualisationType,
ScorecardVisualisationColumn,
} from '../dashboard/types'
import { Scorecard, ScorecardGroup, ScorecardTrend } from './types'
import DatasetHelper from '../../../utils/datasetHelper'
const createScorecards = (
scorecardDefinition: DashboardVisualisation,
rawData: DashboardDataResponse[],
): ScorecardGroup[] => {
const { columns } = scorecardDefinition
const { measures, keys } = columns
const dataset = getDataset(scorecardDefinition, rawData)
const { earliest, latest, earliestTs, latestTs } = dataset
const scorecardFromListValueColumn = (<ScorecardVisualisationColumn[]>measures).find((col) => col.displayValue)
const groupKey = DatasetHelper.getGroupKey(keys, latest)
let scorecardGroup: ScorecardGroup[]
if (!scorecardFromListValueColumn) {
scorecardGroup = createScorecardGroupFromColumns(columns, earliest, earliestTs, latest, latestTs, groupKey)
} else if (groupKey) {
scorecardGroup = createScorecardGroupFromListWithGroups(columns, earliest, earliestTs, latest, latestTs, groupKey)
} else {
scorecardGroup = createScorecardGroupFromList(columns, earliest, earliestTs, latest, latestTs)
}
return scorecardGroup
}
const getDataset = (scorecardDefinition: DashboardVisualisation, rawData: DashboardDataResponse[]) => {
const latestData = DatasetHelper.getLastestDataset(rawData)
const latestDataSetRows = DatasetHelper.getDatasetRows(scorecardDefinition, latestData)
const latestTs = latestDataSetRows[0]?.ts?.raw
const latestFiltered = DatasetHelper.filterRowsByDisplayColumns(scorecardDefinition, latestDataSetRows, true)
const earliestData = DatasetHelper.getEarliestDataset(rawData)
const earliestDataSetRows = DatasetHelper.getDatasetRows(scorecardDefinition, earliestData)
const earliestTs = earliestDataSetRows[0]?.ts?.raw
const earliestfiltered = DatasetHelper.filterRowsByDisplayColumns(scorecardDefinition, earliestDataSetRows, true)
return {
earliest: earliestfiltered,
earliestTs,
latest: latestFiltered,
latestTs,
}
}
const createScorecardData = ({
title,
value,
rag,
valueFor,
valueFrom,
prevVal,
groupTitle,
}: {
title: string
value: string | number
rag?: number
valueFor: string
valueFrom: string
prevVal: string | number
groupTitle?: string
}) => {
return {
title,
value,
...(rag && { rag: getRag(rag) }),
valueFor,
trend: createTrend(valueFor, valueFrom, value, prevVal),
...(groupTitle && {
group: groupTitle,
}),
}
}
const createScorecard = (scorecardDefinition: DashboardVisualisation, rawData: DashboardDataResponse[]) => {
const { columns } = scorecardDefinition
const { measures } = columns
const displayColumn = measures[0]
const { id: valueColumnId, display } = displayColumn
const dataset = getDataset(scorecardDefinition, rawData)
const { earliest, latest, earliestTs, latestTs } = dataset
const scorecordArr: Scorecard[] = latest.map((datasetRow: DashboardDataResponse, index: number) => {
const title = display
const { rag, raw: value } = datasetRow[valueColumnId]
const prevVal = earliest[index][valueColumnId].raw
const valueFor = `${latestTs}`
const valueFrom = `${earliestTs}`
return createScorecardData({
title,
value,
rag,
prevVal,
valueFor,
valueFrom,
})
})
return scorecordArr[0]
}
const createScorecardGroupFromList = (
columns: DashboardVisualisationColumns,
earliest: DashboardDataResponse[],
earliestTs: string | number,
latest: DashboardDataResponse[],
latestTs: string | number,
): ScorecardGroup[] => {
const { measures } = columns
const valueColumn = (<ScorecardVisualisationColumn[]>measures).find((col) => col.displayValue)
const displayColumn = measures.find((col) => {
return col.display || col.display === ''
})
return [
{
title: '',
scorecards: latest.map((datasetRow: DashboardDataResponse, index: number) => {
const title = displayColumn
? `${displayColumn.display} ${datasetRow[displayColumn.id].raw}`
: displayColumn.display
const { rag, raw: value } = datasetRow[valueColumn.id]
const prevVal = earliest[index][valueColumn.id].raw
const valueFor = `${latestTs}`
const valueFrom = `${earliestTs}`
return createScorecardData({
title,
value,
rag,
prevVal,
valueFor,
valueFrom,
})
}),
},
]
}
const createScorecardGroupFromColumns = (
columns: DashboardVisualisationColumns,
earliest: DashboardDataResponse[],
earliestTs: string | number,
latest: DashboardDataResponse[],
latestTs: string | number,
groupKey: DashboardVisualisationColumn,
) => {
const { measures } = columns
return latest.map((row, rowIndex) => {
const groupTitle = `${row[groupKey?.id]?.raw}` || ''
return {
title: groupKey.display ? `${groupKey.display}: ${groupTitle}` : groupTitle,
scorecards: Object.keys(row)
.filter((colId) => colId !== groupKey?.id)
.map((colId) => {
const comparisonRow = earliest[rowIndex]
const measure = measures.find((m) => m.id === colId)
const title = measure?.display || colId
const value = +row[colId].raw
const rag = +row[colId].rag
const prevVal = comparisonRow[colId]?.raw
const valueFor = `${latestTs}`
const valueFrom = `${earliestTs}`
return createScorecardData({
title,
value,
rag,
prevVal,
valueFor,
valueFrom,
})
}),
}
})
}
const createScorecardGroupFromListWithGroups = (
columns: DashboardVisualisationColumns,
earliest: DashboardDataResponse[],
earliestTs: string | number,
latest: DashboardDataResponse[],
latestTs: string | number,
groupKey: DashboardVisualisationColumn,
) => {
const { measures } = columns
const latestGroupedByKey = DatasetHelper.groupRowsByKey(latest, groupKey.id)
const earliestGroupedByKey = DatasetHelper.groupRowsByKey(earliest, groupKey.id)
const valueColumn = (<ScorecardVisualisationColumn[]>measures).find((col) => col.displayValue)
const displayColumn = measures.find((col) => {
return col.display || col.display === ''
})
const scorecardGroup = latestGroupedByKey.map((group, groupIndex) => {
const groupTitle = `${group[0][groupKey?.id]?.raw}` || ''
return {
title: groupKey.display ? `${groupKey.display}: ${groupTitle}` : groupTitle,
scorecards: group.map((row, rowIndex) => {
const title = displayColumn ? `${displayColumn.display} ${row[displayColumn.id].raw}` : displayColumn.display
const value = +row[valueColumn.id].raw
const rag = +row[valueColumn.id].rag
const valueFor = `${latestTs}`
const valueFrom = `${earliestTs}`
const comparisonRow = earliestGroupedByKey[groupIndex][rowIndex]
const prevVal = comparisonRow[valueColumn.id]?.raw
return createScorecardData({
title,
value,
rag,
prevVal,
valueFor,
valueFrom,
})
}),
}
})
return scorecardGroup
}
const getRag = (ragScore: number) => {
const ragColors = ['green', 'yellow', 'red']
return {
score: ragScore,
color: ragColors[ragScore],
}
}
const createTrend = (
valueFor: string,
valueFrom: string,
latestValue: string | number,
earliestValue: string | number,
): ScorecardTrend | undefined => {
let trendData
if (valueFrom !== valueFor) {
const value = +latestValue - +earliestValue
const direction = Math.sign(value)
trendData = {
direction,
value: Math.abs(value),
from: valueFrom,
}
}
return trendData
}
const mergeScorecardsIntoGroup = (visualisations: DashboardUIVisualisation[]) => {
const groupedScorecardIndexes: number[][] = visualisations
// get scorecard indexes
.reduce((acc: number[], vis: DashboardUIVisualisation, i: number) => {
if (vis.type === DashboardVisualisationType.SCORECARD) acc.push(i)
return acc
}, [])
// group adjacent indexes
.reduce((r, n) => {
const lastSubArray = r[r.length - 1]
if (!lastSubArray || lastSubArray[lastSubArray.length - 1] !== n - 1) r.push([])
r[r.length - 1].push(n)
return r
}, [])
groupedScorecardIndexes.reverse().forEach((group: number[]) => {
const spliceAtIndex = group[0]
const scorecardGroup: Scorecard[] = group
.map((scIndex: number) => {
return visualisations[scIndex].data as Scorecard
})
.filter((scorecard: Scorecard) => !!scorecard)
while (group.length) {
visualisations.splice(group.pop(), 1)
}
if (scorecardGroup.length) {
visualisations.splice(spliceAtIndex, 0, {
id: `${spliceAtIndex}`,
type: DashboardVisualisationType.SCORECARD_GROUP,
data: [{ scorecards: scorecardGroup }],
})
}
})
return visualisations
}
export default {
createScorecard,
createScorecards,
mergeScorecardsIntoGroup,
}