@ministryofjustice/hmpps-digital-prison-reporting-frontend
Version:
The Digital Prison Reporting Frontend contains templates and code to help display data effectively in UI applications.
172 lines (169 loc) • 6 kB
JavaScript
import { DataTableBuilder } from '../../DataTableBuilder/DataTableBuilder.js';
import { SectionedDataHelper } from '../SectionedDataHelper/SectionedDataHelper.js';
import { TemplateBuilder } from '../TemplateBuilder.js';
class ParentChildDataBuilder extends TemplateBuilder {
childColumns = [];
childVariants = [];
childData = [];
joinFields = [];
childFields = [];
parentChildDatasets = [];
dataTableBuilder;
constructor(variant, defId) {
super(variant, defId);
this.childVariants = this.variant.childVariants || [];
}
getChildVariant(childId) {
return this.childVariants.find(cv => cv.id === childId);
}
/**
* Exracts the relevant data from the child variant definition
* to generate the child table
*
* @param {string} childId
* @return { fields: components['schemas']['FieldDefinition'][], joinFields: string[], columns: string[], name: string }
* @memberof ParentChildDataBuilder
*/
getChildVariantDefinitionData(childId) {
const childVariant = this.getChildVariant(childId);
let fields = [];
let joinFields = [];
let columns = [];
let name = 'Child report';
if (childVariant && childVariant.specification) {
fields = childVariant.specification.fields;
joinFields = childVariant && childVariant.specification ? childVariant.joinFields : [];
columns = fields.filter(f => f.visible).map(f => f.name);
name = childVariant.name;
}
return {
fields,
joinFields,
columns,
name,
};
}
/**
* Maps the parent child rows.
* Splits into sections that will become the parent table
*
* @return {*} {GroupedParentChildDataset[]}
* @memberof ParentChildDataBuilder
*/
mergeParentChildAndGroup(parentData) {
const groups = [];
let pendingParents = [];
parentData.forEach(parentRow => {
// Build a dataset for this parent
const dataset = {
parent: parentRow,
children: [],
};
// Match against each child dataset
this.childData.forEach(child => {
const { joinFields } = this.getChildVariantDefinitionData(child.id);
const matchedChildren = [];
// Compare join fields
child.data.forEach(childRow => {
const isMatch = joinFields.every(col => {
return parentRow[col] === childRow[col];
});
if (isMatch)
matchedChildren.push(childRow);
});
if (matchedChildren.length) {
dataset.children.push({
id: child.id,
data: matchedChildren,
});
}
});
// Always collect the parent
pendingParents.push(dataset.parent);
// If dataset has children → close the group
if (dataset.children.length > 0) {
groups.push({
parent: pendingParents,
children: dataset.children,
});
pendingParents = [];
}
});
// If leftover parents exist, make a final group (empty children)
if (pendingParents.length > 0) {
groups.push({
parent: pendingParents,
children: [],
});
}
return groups;
}
/**
* Maps the parent child data to a table data
*
* @param {GroupedParentChildDataset[]} parentChildGroups
* @return {*} {ParentChildData[]}
* @memberof ParentChildDataBuilder
*/
mapToTableData(parentChildGroups) {
const parentTableBuilder = new DataTableBuilder(this.fields);
return parentChildGroups.map((group) => {
const parentTable = parentTableBuilder.withNoHeaderOptions(this.columns).buildTable(group.parent);
const children = this.mapChildDataToTableData(group);
return {
parent: parentTable,
children,
};
});
}
/**
* Maps the grouped child data to table data
*
* @param {GroupedParentChildDataset} group
* @return {*}
* @memberof ParentChildDataBuilder
*/
mapChildDataToTableData(group) {
return group.children.map(child => {
const { fields: childFields, columns: childColumns, name } = this.getChildVariantDefinitionData(child.id);
const childTableBuilder = new DataTableBuilder(childFields);
const childTable = childTableBuilder.withNoHeaderOptions(childColumns).buildTable(child.data);
return {
title: name,
...childTable,
};
});
}
withChildData(childData) {
this.childData = childData;
return this;
}
withChildColumns(childColumns) {
this.childColumns = childColumns;
return this;
}
build() {
const sectionData = new SectionedDataHelper()
.withSections(this.sections)
.withData(this.data)
.withFields(this.fields)
.withReportQuery(this.reportQuery)
.build();
const { sections } = sectionData;
const mappedSections = sections.map((section) => {
const groups = this.mergeParentChildAndGroup(section.data);
const table = this.mapToTableData(groups);
return {
...section,
summaries: [],
data: table,
};
});
return {
rowCount: this.data.length,
sections: mappedSections,
};
}
}
export { ParentChildDataBuilder };
//# sourceMappingURL=ParentChildDataBuilder.js.map