@ministryofjustice/hmpps-digital-prison-reporting-frontend
Version:
The Digital Prison Reporting Frontend contains templates and code to help display data effectively in UI applications.
325 lines (322 loc) • 11.2 kB
JavaScript
import { FilterType } from '../components/_filters/filter-input/enum.js';
import { resolveDateRangeDefaults, appendDateRangeValue } from '../components/_inputs/date-range/utils.js';
import { resolveGranularDateRangeDefaults, appendGranularDateRangeValue } from '../components/_inputs/granular-date-range/utils.js';
import { appendMultiSelectValues } from '../components/_inputs/multi-select/utils.js';
import { visibleColumns, mandatoryColumns } from '../components/_reports/report-heading/report-columns/report-columns-form/utils.js';
import { ReportType } from '../types/UserReports.js';
import logger from './logger.js';
import { getQueryParamAsString } from './queryMappers.js';
/**
* Gets the definition and fields by reportType
*
* @param {ReportType} reportType
* @param {Services} services
* @param {string} token
* @param {string} reportId
* @param {string} id
* @param {string} [definitionsPath]
*/
const getDefinitionByType = async (reportType, services, token, reportId, id, definitionsPath, queryData) => {
let definition;
let fields;
if (reportType === ReportType.REPORT) {
definition = await services.reportingService.getDefinition(token, reportId, id, definitionsPath, queryData);
fields = getFields(definition);
}
else {
definition = await services.dashboardService.getDefinition(token, reportId, id, definitionsPath, queryData);
fields = getDashboardFields(definition);
}
if (!definition) {
throw Error(`Unable to retrieve definition for ${reportType} with id:${id} & reportId:${reportId}`);
}
return {
definition,
fields,
};
};
/**
* Given a field id, gets its filter object
*
* @param {components['schemas']['FieldDefinition'][]} fields
* @param {string} fieldId
* @return {*} {(components['schemas']['FilterDefinition'] | undefined)}
*/
const getFilter = (fields, fieldId) => {
return fields.find(f => f.name === fieldId)?.filter;
};
/**
* Gets the current varaint definition
*
* @param {components['schemas']['ReportDefinitionSummary'][]} definitions
* @param {string} reportId
* @param {string} variantId
* @return {*} {(components['schemas']['VariantDefinition'] | undefined)}
*/
const getCurrentVariantDefinition = (definitions, reportId, variantId) => {
if (!definitions) {
logger.info('No definitions');
return undefined;
}
let variantDef;
const reportDef = definitions.find((report) => report.id === reportId);
if (reportDef) {
// TODO: this needs fixing, we should never be using `as unknown as`, but the types are very mixed up here
variantDef = reportDef.variants.find(variant => variantId === variant.id);
return variantDef;
}
return undefined;
};
/**
* Gets the display name of a field
*
* @param {(components['schemas']['FieldDefinition'][] | components['schemas']['SummaryField'][])} fields
* @param {string} fieldId
* @return {*}
*/
const getFieldDisplayName = (fields, fieldId) => {
const ids = fieldId.split('.');
const field = fields.find(f => {
return f.name === ids[0];
});
if (field) {
return ids[1] ? `${field.display} ${ids[1]}` : field.display;
}
return fieldId;
};
/**
* Gets a single field by its id/name
*
* @param {components['schemas']['FieldDefinition'][]} fields
* @param {string} fieldId
* @return {*}
*/
const getField = (fields, fieldId) => {
return fields.find(field => {
return field.name === fieldId;
});
};
/**
* Get all fields from a definition
*
* @param {components['schemas']['SingleVariantReportDefinition']} definition
* @return {*} {components['schemas']['FieldDefinition'][]}
*/
const getFields = (definition) => {
return definition.variant.specification?.fields || [];
};
/**
* Get all fields from a definition
*
* @param {components['schemas']['SingleVariantReportDefinition']} definition
* @return {*} {components['schemas']['FieldDefinition'][]}
*/
const getDashboardFields = (definition) => {
return definition.filterFields || [];
};
/**
* Gets only the fields that have filters, from fields
*
* @param {components['schemas']['FieldDefinition'][]} fields
* @return {*} {FieldWithFilter[]}
*/
const getFieldsWithFilters = (fields) => {
return fields.filter((f) => !!f.filter);
};
/**
* Gets the template type
*
* @param {components['schemas']['SingleVariantReportDefinition']} definition
* @return {*} {Template}
*/
const getTemplate = (definition) => {
return definition.variant.specification?.template || 'list';
};
/**
* Gets the filters from a variant
*
* @param {components['schemas']['FieldDefinition'][]} fields
* @return {*} {components['schemas']['FilterDefinition'][]}
*/
const getFilters = (fields) => {
const filters = fields
.filter((field) => field.filter)
.map((field) => field.filter);
return filters.length ? filters : [];
};
/**
* Given an array of string names, will return the relative field types
*
* @param {string[]} names
* @param {components['schemas']['FieldDefinition'][]} fields
* @return {*} {components['schemas']['FieldDefinition'][]}
*/
const getFieldsByName = (names, fields) => {
return names.map(s => fields.find(field => field.name === s)).filter(field => field !== undefined);
};
/**
* Validates the definition
*
* @param {components['schemas']['SingleVariantReportDefinition']} definition
* @return {*}
*/
const validateDefinition = (definition) => {
const { variant } = definition;
return validateVariant(variant, definition.id);
};
/**
* Validates that the variant has a `specification`
*
* @param {components['schemas']['VariantDefinition']} variant
* @return {*}
*/
const validateVariant = (variant, defId) => {
const { specification } = variant;
if (!specification) {
logger.error(`No specification found for definition ID: ${defId} variant ID: ${variant.id}`);
throw new Error(`No specification found for definition ID: ${defId} variant ID: ${variant.id}`);
}
return { variant, specification };
};
/**
* Determines whether there are any interactive filters
* defined in the definition
*
* @param {components['schemas']['FieldDefinition'][]} fields
* @return {*}
*/
const hasInteractiveFilters = (fields) => {
const filters = getFilters(fields);
return filters.some(filter => filter.interactive);
};
const getReportSummary = (reportId, reportingService, token, definitionPath) => reportingService.getDefinitionSummary(token, reportId, definitionPath);
/**
* Creates a the default filters querystring
*
* @param {components['schemas']['FieldDefinition'][]} fields
* @return {*} {DefaultFilterQueryStrings}
*/
const getDefaultFiltersQueryString = (fields) => {
const defaultParams = new URLSearchParams();
const interactiveParams = new URLSearchParams();
const fieldsWithFilters = getFieldsWithFilters(fields);
fieldsWithFilters.forEach(field => {
const { interactive = false } = field.filter;
appendFilterDefaults(interactive ? interactiveParams : defaultParams, field);
});
return {
defaultFiltersSearch: buildQueryString(defaultParams),
interactiveDefaultFiltersSearch: buildQueryString(interactiveParams),
};
};
/**
* Sets the default sort query string
*
* @param {components['schemas']['FieldDefinition'][]} fields
* @return {*}
*/
const getDefaultSortQueryString = (fields) => {
const defaultSortField = fields.find(field => field.defaultsort);
if (!defaultSortField) {
return '';
}
const defaultSortParams = new URLSearchParams();
const isAsc = (defaultSortField.sortDirection ?? 'asc') === 'asc';
defaultSortParams.set('sortColumn', defaultSortField.name);
defaultSortParams.set('sortedAsc', String(isAsc));
return buildQueryString(defaultSortParams);
};
/**
* Builds the querystring
*
* @param {URLSearchParams} params
* @return {*} {string}
*/
const buildQueryString = (params) => {
const qs = params.toString();
return qs || '';
};
/**
* Appends the filter value to a query string
*
* @param {URLSearchParams} params
* @param {FieldWithFilter} field
*/
const appendFilterDefaults = (params, field) => {
const { name, filter } = field;
const { type, defaultValue, defaultQuickFilterValue } = filter;
if (!defaultValue && !defaultQuickFilterValue)
return;
switch (type.toLowerCase()) {
case FilterType.radio.toLowerCase():
case FilterType.select.toLowerCase():
case FilterType.text.toLowerCase():
case FilterType.date.toLowerCase():
case FilterType.autocomplete.toLowerCase(): {
if (!defaultValue)
break;
params.append(`filters.${name}`, defaultValue);
break;
}
case FilterType.autocompleteMulti.toLowerCase():
case FilterType.multiselect.toLowerCase(): {
if (!defaultValue)
break;
appendMultiSelectValues(params, name, defaultValue);
break;
}
case FilterType.dateRange.toLowerCase(): {
const resolved = resolveDateRangeDefaults(filter);
if (!resolved)
break;
appendDateRangeValue(params, name, resolved);
break;
}
case FilterType.granularDateRange.toLowerCase(): {
const resolved = resolveGranularDateRangeDefaults(filter);
if (!resolved)
break;
appendGranularDateRangeValue(params, name, resolved);
break;
}
}
};
/**
* Get the default columns from the DPD
* and creates a querystring
*
* @param {components['schemas']['FieldDefinition'][]} fields
* @return {*}
*/
const getDefaultColumnsQueryString = (fields) => {
const visibleCols = visibleColumns(fields);
const mandatoryCols = mandatoryColumns(fields);
const defaultColumns = Array.from(new Set([...mandatoryCols, ...visibleCols]));
const params = new URLSearchParams();
defaultColumns.forEach(column => {
params.append('columns', column);
});
return buildQueryString(params);
};
/**
* Get the definition path from the query
*
* @param {Record<string, unknown>} query
*/
const getDefinitionsPath = (query) => getQueryParamAsString(query, 'dataProductDefinitionsPath');
var DefinitionUtils = {
getCurrentVariantDefinition,
getFieldDisplayName,
getField,
getFields,
getTemplate,
getFilter,
getFilters,
getReportSummary,
hasInteractiveFilters,
getDefaultFiltersQueryString,
getDefaultColumnsQueryString,
getDefinitionByType,
};
export { DefinitionUtils as default, getCurrentVariantDefinition, getDashboardFields, getDefaultColumnsQueryString, getDefaultFiltersQueryString, getDefaultSortQueryString, getDefinitionByType, getDefinitionsPath, getField, getFieldDisplayName, getFields, getFieldsByName, getFieldsWithFilters, getFilter, getFilters, getReportSummary, getTemplate, hasInteractiveFilters, validateDefinition, validateVariant };
//# sourceMappingURL=definitionUtils.js.map