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.

284 lines (281 loc) 11 kB
import parseUrl from 'parseurl'; import { hasInteractiveFilters, getTemplate } from '../../utils/definitionUtils.js'; import PaginationUtils from './report-page/report-template/report-pagination/utils.js'; import TotalsUtils from './report-page/report-template/report-totals/utils.js'; import ReportTemplateUtils from './report-page/report-template/utils.js'; import ColumnUtils from './report-heading/report-columns/report-columns-form/utils.js'; import { ReportType, LoadType } from '../../types/UserReports.js'; import { ReportQuery } from '../../types/ReportQuery.js'; import localsHelper from '../../utils/localsHelper.js'; import { ErrorHandler } from '../../utils/ErrorHandler/ErrorHandler.js'; import logger from '../../utils/logger.js'; import DataPresentation from '../_dashboards/DataPresentation.js'; class Report extends DataPresentation { variant; specification; reportData; childData = []; summariesData = []; columns; count; pagination; totals; dataTable; constructor(services, res, req, definition, loadType, requestData) { super(services, res, req, definition, loadType, ReportType.REPORT, requestData); this.setSpecification(); } setSpecification = () => { this.variant = this.definition.variant; const { specification } = this.variant; if (!specification) { throw new Error('No specification in definition'); } this.specification = specification; }; build = async () => { // General report data const reportMeta = this.buildReportMeta(ReportType.REPORT); this.buildReportDetails(); // Columns and filters this.buildColumns(); await this.buildFilters(); await this.buildSavedDefaultsConfig(); this.buildAppliedFilters(); // Get the data this.buildReportQuery(); await this.getData(); if (this.expired) { return { expired: this.expired, }; } // Template & page furniture this.buildTable(); await this.buildActions(ReportType.REPORT, this.reportDetails.schedule); this.buildPagination(); return { renderData: { columns: this.columns, filterData: this.filterData, appliedFilters: this.appliedFilters, ...this.savedDefaultsConfig, count: this.count, ...reportMeta, ...this.actions, ...this.reportDetails, ...(this.pagination && { pagination: this.pagination }), ...(this.extractedRequestData && this.extractedRequestData), totals: this.totals, dataTable: this.dataTable, fields: this.fields, }, }; }; /** * Gets all the report data * */ getData = async () => { try { await this.getReportData(); if (this.loadType === LoadType.ASYNC) { await this.getSummariesData(); await this.setChildData(); } await this.getCount(); } catch (error) { const dprError = new ErrorHandler(error).formatError(); if (dprError.status === 404) { this.expired = true; } else { throw error; } } }; /** * Gets the report data */ getReportData = async () => { this.reportData = this.loadType === LoadType.SYNC ? await this.getSyncData() : await this.getAysncData(); }; getAysncData = async () => { const reportQueryRecord = this.reportQuery.toRecordWithFilterPrefix(true); return this.services.reportingService.getAsyncReport(this.token, this.reportId, this.id, this.tableId, reportQueryRecord); }; getSyncData = async () => { const { resourceName } = this.variant; const listWithWarnings = await this.services.reportingService.getListWithWarnings(resourceName, this.token, this.reportQuery); return listWithWarnings.data; }; /** * Gets the summaries data for summary tables * NOTE: Only available for Async * * @return {*} */ getSummariesData = async () => { const { definitionsPath: dataProductDefinitionsPath } = localsHelper.getValues(this.res); this.summariesData = !this.variant.summaries ? [] : await Promise.all(this.variant.summaries.map(async (summary) => { const summaryReport = await this.services.reportingService.getAsyncSummaryReport(this.token, this.reportId, this.id, this.tableId, summary.id, { dataProductDefinitionsPath, }); return { ...summary, data: summaryReport, }; })); logger.info('SUMMARY_SORT_BUG', JSON.stringify({ apiSummaries: this.summariesData })); }; /** * Gets the child date for a parent child report * NOTE: Only available for Async * */ setChildData = async () => { // Get the child data, if applicable const { childVariants } = this.variant; this.childData = !childVariants ? [] : await this.getChildData(childVariants, this.services, this.token, this.req, this.res, this.requestData); }; getChildData = async (childVariants, services, token, req, res, requestData) => { const { definitionsPath: dataProductDefinitionsPath } = localsHelper.getValues(res); const { reportId } = req.params; const childExecutionData = requestData?.childExecutionData; if (!childExecutionData) { throw new Error('getChildData: No execution data found for child variants'); } return Promise.all(childVariants.map(async (childVariant) => { const { specification } = childVariant; if (!specification) { throw new Error('getChildData: No specification found in child variant definition'); } const query = new ReportQuery({ fields: specification?.fields || [], template: 'parent-child', queryParams: req.query, definitionsPath: dataProductDefinitionsPath, }).toRecordWithFilterPrefix(true); const childData = childExecutionData.find(e => e.variantId === childVariant.id); if (!childData || !childData.tableId) { throw new Error('getChildData: No matching child execution data found'); } const { tableId: childTableId } = childData; const childReport = await services.reportingService.getAsyncReport(token, reportId, childVariant.id, childTableId, query); return { id: childVariant.id, data: childReport, }; })); }; /** * Builds the report table * */ buildTable = () => { this.dataTable = ReportTemplateUtils.createReportTemplateData(this.definition, this.columns, this.reportData, this.childData, this.summariesData, this.reportQuery); }; /** * Gets the relevant report details from the report definition * */ buildReportDetails = () => { const { name: reportName, description: reportDescription } = this.definition; // TODO: remove casting `VariantDefinitionWithSchedule` type when type includes "schedule" const { classification, printable, name, description, schedule } = this.variant; const { template, fields } = this.specification; this.reportDetails = { reportName, name, description: description || reportDescription || '', classification: classification || '', printable: Boolean(printable), template, specification: this.specification, // TODO: check if needed ??? fields, // TODO: check if needed??? schedule, }; }; /** * Build columns * */ buildColumns = () => { this.columns = ColumnUtils.getColumns(this.specification, this.req); }; /** * Builds the pagination config for a report */ buildPagination = () => { const url = parseUrl(this.req); if (!url) { throw new Error('Unable to set url data from request'); } let pagination; let totals; if (this.reportDetails.template === 'list') { pagination = PaginationUtils.getPaginationData(url, this.count, this.req); const { pageSize, currentPage, totalRows } = pagination; totals = TotalsUtils.getTotals(pageSize, currentPage, totalRows, this.dataTable.rowCount); } if (pagination) { this.pagination = pagination; } if (totals) { this.totals = totals; } }; /** * Gets the row count * * @return {*} */ getCount = async () => { if (this.loadType === LoadType.SYNC) { this.count = await this.services.reportingService.getCount(this.variant.resourceName, this.token, this.reportQuery); } else { this.count = !this.variant.interactive || !hasInteractiveFilters(this.fields ?? []) ? await this.services.reportingService.getAsyncCount(this.token, this.tableId) : await this.services.reportingService.getAsyncInteractiveCount(this.token, this.tableId, this.reportId, this.id, this.reportQuery); } }; /** * Builds the report query * */ buildReportQuery = () => { const { definitionsPath } = localsHelper.getValues(this.res); const template = getTemplate(this.definition); // Sort const sortColumn = this.req.query?.['sortColumn'] || this.requestData?.sortBy?.data?.['sortColumn']; const sortedAsc = this.req.query?.['sortedAsc'] || this.requestData?.sortBy?.data?.['sortedAsc']; // Pagination const selectedPage = this.req.query?.['selectedPage']; const pageSize = this.req.query?.['pageSize']; // Filters query const filtersQuery = this.getCurrentQuery(); const queryParams = { ...(sortColumn && { sortColumn }), ...(sortedAsc && { sortedAsc }), ...filtersQuery, ...(pageSize && { pageSize }), ...(selectedPage && { selectedPage }), ...(this.columns && { columns: this.columns.value }), }; this.reportQuery = new ReportQuery({ fields: this.fields ?? [], template, queryParams, definitionsPath, }); }; } export { Report as default }; //# sourceMappingURL=Report.js.map