@ministryofjustice/hmpps-digital-prison-reporting-frontend
Version:
The Digital Prison Reporting Frontend contains templates and code to help display data effectively in UI applications.
278 lines (277 loc) • 10.8 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const DataTableBuilder_1 = __importDefault(require("../DataTableBuilder/DataTableBuilder"));
const arrayUtils_1 = require("../arrayUtils");
const SummaryDataTableBuilder_1 = __importDefault(require("../SummaryDataTableBuilder/SummaryDataTableBuilder"));
class SectionedDataTableBuilder extends DataTableBuilder_1.default {
constructor(specification) {
const { fields, sections, template } = specification;
super(fields);
this.sections = sections;
this.template = template;
}
/**
* Creates the section heading strings
*
* @param {Dict<string>[]} data
* @param {FieldDefinition[]} sectionFields
* @return {*} {string[]} array of section headings
*/
createSectionHeadings(data, sectionFields) {
return data
.map((rowData) => ({
description: this.mapSectionDescription(rowData),
sortKey: this.getSortKey(rowData, sectionFields),
}))
.sort(this.sortKeyComparison())
.map((s) => s.description)
.reduce(arrayUtils_1.distinct, []);
}
/**
* Initialise section heading arrays
*
* @private
* @param {string[]} sectionDescriptions
* @return {*}
* @memberof SectionedDataTableBuilder
*/
initSectionData(sectionDescriptions) {
const sectionedData = {};
sectionDescriptions.forEach((sectionDescription) => {
sectionedData[sectionDescription] = [];
});
return sectionedData;
}
/**
* Maps the rows to the correct section
*
* @private
* @param {Array<Dict<string>>} data
* @param {Dict<Cell[][]>} sectionedData
* @return {*}
* @memberof SectionedDataTableBuilder
*/
mapRowsToSection(data, sectionedData) {
return data.reduce((previousValue, rowData) => {
const sectionDescription = this.mapSectionDescription(rowData);
const mappedData = this.mapRow(rowData);
return {
...previousValue,
[sectionDescription]: previousValue[sectionDescription].concat([mappedData]),
};
}, sectionedData);
}
/**
* Maps the rows to the correct section
*
* @private
* @param {Array<Dict<string>>} data
* @param {Dict<Cell[][]>} sectionedData
* @return {*}
* @memberof SectionedDataTableBuilder
*/
mapDataToSection(data, sectionedData) {
return data.reduce((previousValue, rowData) => {
const sectionDescription = this.mapSectionDescription(rowData);
const section = {
...previousValue,
[sectionDescription]: previousValue[sectionDescription].concat([rowData]),
};
return section;
}, sectionedData);
}
/**
* Gets the counts for rows in section
*
* @param {Dict<Cell[][]>} sectionedData
* @param {string} sectionDescription
* @return {*}
* @memberof SectionedDataTableBuilder
*/
getSectionCount(sectionedData, sectionDescription) {
const count = sectionedData[sectionDescription].length;
const countDescription = `${count} result${count === 1 ? '' : 's'}`;
return {
count,
countDescription,
};
}
/**
* Creates the summaries and builds the table with summaries
*
* @private
* @param {string} sectionDescription
* @param {Cell[][]} mappedTableData
* @param {Cell[]} header
* @return {*}
* @memberof SectionedDataTableBuilder
*/
mapSummariesAndCreateTable(sectionDescription, mappedTableData, header) {
let tableContent = [];
let mappedSectionHeaderSummary = [];
let mappedHeaderSummary = [];
let mappedFooterSummary = [];
let mappedSectionFooterSummary = [];
mappedSectionHeaderSummary = this.mapSectionSummaryTables(sectionDescription, 'section-header', this.columns.length);
mappedHeaderSummary = this.mapSectionSummaryRows('table-header', sectionDescription);
mappedFooterSummary = this.mapSectionSummaryRows('table-footer', sectionDescription);
mappedSectionFooterSummary = this.mapSectionSummaryTables(sectionDescription, 'section-footer', this.columns.length);
tableContent = mappedSectionHeaderSummary
.concat(mappedTableData.length > 0 ? [header] : [])
.concat(mappedHeaderSummary)
.concat(mappedTableData)
.concat(mappedFooterSummary)
.concat(mappedSectionFooterSummary);
return tableContent;
}
/**
* Creates the table data
* - if summaries are present, includes the summaries data
*
* @private
* @param {string[]} sectionDescriptions
* @param {Dict<Cell[][]>} sectionedData
* @param {Cell[]} header
* @return {*}
* @memberof SectionedDataTableBuilder
*/
createTableContent(sectionDescriptions, sectionedData, header) {
return sectionDescriptions.flatMap((sectionDescription, index) => {
const { count, countDescription } = this.getSectionCount(sectionedData, sectionDescription);
const mappedTableData = sectionedData[sectionDescription];
let tableContent = [];
if (Object.keys(this.reportSummaries).length) {
tableContent = this.mapSummariesAndCreateTable(sectionDescription, mappedTableData, header);
}
else {
tableContent = tableContent.concat(mappedTableData.length > 0 ? [header] : []).concat(mappedTableData);
}
const sectionHeader = this.createSectionHeader(sectionDescription, index, count, countDescription);
return [...sectionHeader, ...tableContent];
});
}
createSectionHeader(sectionDescription, index, count, countDescription) {
const header = [];
if (index !== 0) {
header.push([
{
classes: 'dpr-section-header-spacer',
colspan: this.columns.length,
text: '',
},
]);
}
header.push([
{
classes: 'dpr-section-header',
colspan: this.columns.length,
html: `<h2 class="govuk-heading-m">${sectionDescription}${count > 0 ? ` <span class='govuk-caption-m'>${countDescription}</span>` : ''}</h2>`,
},
]);
header.push([
{
classes: 'dpr-section-header-spacer-bottom',
colspan: this.columns.length,
text: '',
},
]);
return header;
}
mapSectionSummaryRows(template, sectionDescription) {
if (this.reportSummaries[template]) {
return this.reportSummaries[template].flatMap((reportSummary) => reportSummary.data
.filter((rowData) => this.mapSectionDescription(rowData) === sectionDescription)
.map((rowData) => this.mapRow(rowData, `dpr-report-summary-cell dpr-report-summary-cell-${template}`, reportSummary.fields)));
}
return [];
}
mapSectionSummaryTables(sectionDescription, summaryTemplate, columnsLength) {
const summaries = this.reportSummaries[summaryTemplate];
if (summaries) {
const htmlTables = summaries.map((summary) => {
const data = summary.data.filter((row) => this.mapSectionDescription(row) === sectionDescription);
if (data.length > 0) {
const dataTable = new SummaryDataTableBuilder_1.default(summary, this.sections).buildTable(data);
const htmlTable = this.convertDataTableToHtml(dataTable);
return `<div class='dpr-summary-container'>${htmlTable}</div>`;
}
return '';
});
const summaryContent = htmlTables.join('');
if (summaryContent.length > 0) {
return [
[
{
classes: 'dpr-summary-cell',
colspan: columnsLength,
html: `<div class='dpr-summary-container-group dpr-summary-container-group-${summaryTemplate}'>${summaryContent}</div>`,
},
],
];
}
}
return [];
}
mapSectionDescription(rowData) {
const { sections } = this;
return this.mapNamesToFields(sections)
.map((s) => `${s.display}: ${this.mapCellValue(s, rowData[s.name])}`)
.join(', ');
}
mapSections(data) {
const sectionHeadings = this.initSectionedHeadings(data);
let { sectionedData } = sectionHeadings;
// Maps data to sections
if (this.template !== 'summary-section') {
if (this.template === 'parent-child-section') {
sectionedData = this.mapDataToSection(data, sectionedData);
}
else {
sectionedData = this.mapRowsToSection(data, sectionedData);
}
}
return {
sectionDescriptions: sectionHeadings.sectionDescriptions,
sectionedData,
};
}
initSectionedHeadings(data) {
// Get the section definition data
const sectionFields = this.mapNamesToFields(this.sections);
// create the sectionHeadings
const sectionDescriptions = this.createSectionHeadings(data, sectionFields);
// init empty sections
const sectionedData = this.initSectionData(sectionDescriptions);
return {
sectionDescriptions,
sectionedData,
};
}
/**
* Creates the table rows.
*
* @private
* @param {Array<Dict<string>>} data
* @param {Cell[]} header
* @return {*} {Cell[][]}
* @memberof SectionedDataTableBuilder
*/
mapSectionedData(data, header) {
const { sectionDescriptions, sectionedData } = this.mapSections(data);
// Create the table
const tableContent = this.createTableContent(sectionDescriptions, sectionedData, header);
return tableContent;
}
buildTable(data) {
return {
head: null,
rows: this.mapSectionedData(data, this.mapHeader(true, 'govuk-table__header')),
rowCount: data.length,
colCount: this.columns.length,
};
}
}
exports.default = SectionedDataTableBuilder;