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.

120 lines (117 loc) 5.1 kB
'use strict'; /** * Returns whether the keys array contains optional ones * * @param {components['schemas']['DashboardVisualisationColumnDefinition'][]} keys * @return {*} {boolean} */ const hasOptionalKeys = (keys) => { return Boolean(keys?.some(key => key.optional)); }; /** * Filters dashboard data rows based on the most complete valid set of column IDs. * * Workflow: * 1. Generate all fallback key combinations using getKeyVariations(). * 2. Use getKeyIds() to determine the "best" set of required keys — meaning: * the largest set of IDs for which *at least one* row has valid data * (non-empty, non-null, non-undefined). * 3. Filter the entire dataset to keep only rows that have valid values * for all selected required keys. * * @param {DashboardDataResponse[]} dashboardData The full dataset to filter. * @param {components['schemas']['DashboardVisualisationColumnDefinition'][]} keys Column definitions used to derive key combinations. * @returns {DashboardDataResponse[]} Rows that satisfy the selected required-key validity rules. */ const filterRowsByKeys = (dashboardData, keys) => { // 1. Build fallback key variations const colIdVariations = getKeyVariations(keys); // 2. Select the most complete variation supported by at least one valid row const validHeadIds = getKeyIds(dashboardData, colIdVariations); /** * Checks whether a single row contains valid values for all required keys. */ const isRowValid = (row) => Object.keys(row) .map(fieldId => { const value = row[fieldId].raw; // Only validate fields that are part of the selected key set return validHeadIds.includes(fieldId) ? value !== '' && value !== null && value !== undefined : true; }) .every(Boolean); // 3. Keep only fully-valid rows return dashboardData.filter(isRowValid); }; /** * Generate all valid column ID variations based on trailing optional keys. * * Behaviour rules: * 1. Begin with the full list of column IDs. * 2. Iterate backward through the keys: * - Add the current remaining IDs as a variation. * - If the current key is optional, remove its ID from the end. * - If the current key is NOT optional, stop removing afterward. * 3. If *all* keys are optional, append an empty list. * * @param {components['schemas']['DashboardVisualisationColumnDefinition'][]} keys The visualisation column definitions. * @returns {string[][]} An array of ID combinations representing valid fallback sets. */ const getKeyVariations = (keys) => { const colIds = keys.map(k => k.id); const allOptional = keys.every(k => k.optional); const reversedKeys = keys.slice().reverse(); /** * Reduce through reversed keys, accumulating variations and trimming * optional keys from the end. */ const state = reversedKeys.reduce((acc, key) => { // Add the current remaining IDs as a variation. acc.variations.push([...acc.remaining]); // Drop last ID if the key was optional. acc.remaining = key.optional ? acc.remaining.slice(0, -1) : acc.remaining; return acc; }, { remaining: [...colIds], variations: [], }); // Append empty variation only when all keys are optional if (allOptional) { state.variations.push([]); } return state.variations; }; /** * Given dashboard data and a list of column-ID variations (from getKeyVariations), * this function returns the *first* ID set for which there exists at least one * row where all referenced fields contain valid (non-empty, non-null) raw values. * * If *none* of the variations produce any valid rows, the function returns the * *last* variation tested. * * @param {DashboardDataResponse[]} dashboardData Array of dataset rows to validate. * @param {string[][]} colIdVariations Variations of column IDs, ordered from most strict → least strict. * @returns {string[]} The first ID set that has at least one fully-valid row, or the last set. */ const getKeyIds = (dashboardData, colIdVariations) => { // Precompute field lists for each row (performance optimisation) const rowFieldLists = dashboardData.map(row => Object.keys(row)); /** * Check whether a row satisfies all required IDs. */ const isRowValid = (row, rowFields, ids) => ids.every(id => { if (!rowFields.includes(id)) return false; const value = row[id].raw; return value !== '' && value !== null && value !== undefined; }); /** * Find the first variation with at least one valid row. */ const match = colIdVariations.find(ids => dashboardData.some((row, index) => isRowValid(row, rowFieldLists[index], ids))); // If none match, return the final variation. return match ?? colIdVariations[colIdVariations.length - 1]; }; exports.filterRowsByKeys = filterRowsByKeys; exports.getKeyIds = getKeyIds; exports.getKeyVariations = getKeyVariations; exports.hasOptionalKeys = hasOptionalKeys; //# sourceMappingURL=VisualisationOptionalKeysHelper.js.map