UNPKG

@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
"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;