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.

474 lines (469 loc) 19.4 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var dayjs = require('dayjs'); var customParseFormat = require('../../../../_virtual/customParseFormat.js'); var utils = require('../start-end-date/utils.js'); var types = require('./types.js'); dayjs.extend(customParseFormat); const hasPartialStartEnd = (granularity, startDate, endDate) => { let partialStart; let partialEnd; switch (granularity) { case types.Granularity.DAILY: partialStart = false; partialEnd = false; break; case types.Granularity.MONTHLY: { const startOfMonth = dayjs(startDate).startOf('month'); const endOfMonth = dayjs(endDate).endOf('month'); partialStart = !startOfMonth.isSame(startDate, 'day'); partialEnd = !endOfMonth.isSame(endDate, 'day'); } break; case types.Granularity.ANNUALLY: { const startOfYear = dayjs(startDate).startOf('year'); const endOfYear = dayjs(endDate).endOf('year'); partialStart = !startOfYear.isSame(startDate, 'day'); partialEnd = !endOfYear.isSame(endDate, 'day'); } break; default: partialStart = false; partialEnd = false; break; } return { start: partialStart, end: partialEnd, }; }; const getQuickFilterOptions = () => { const options = [ { value: types.QuickFilters.NONE, text: 'None' }, { value: types.QuickFilters.TODAY, text: 'Today' }, { value: types.QuickFilters.PAST, text: 'Past:', disabled: true }, { value: types.QuickFilters.YESTERDAY, text: 'Yesterday' }, { value: types.QuickFilters.LAST_SEVEN_DAYS, text: 'Last 7 days' }, { value: types.QuickFilters.LAST_THIRTY_DAYS, text: 'Last 30 days' }, { value: types.QuickFilters.LAST_MONTH, text: 'Last month' }, { value: types.QuickFilters.LAST_FULL_MONTH, text: 'Last full month' }, { value: types.QuickFilters.LAST_NINETY_DAYS, text: 'Last 90 days' }, { value: types.QuickFilters.LAST_THREE_MONTHS, text: 'Last 3 months' }, { value: types.QuickFilters.LAST_FULL_THREE_MONTHS, text: 'Last full 3 months' }, { value: types.QuickFilters.LAST_SIX_MONTHS, text: 'Last 6 months' }, { value: types.QuickFilters.LAST_FULL_SIX_MONTHS, text: 'Last full 6 months' }, { value: types.QuickFilters.LAST_YEAR, text: 'Last year' }, { value: types.QuickFilters.LAST_FULL_YEAR, text: 'Last full year' }, { value: types.QuickFilters.FUTURE, text: 'Future:', disabled: true }, { value: types.QuickFilters.TOMORROW, text: 'Tomorrow' }, { value: types.QuickFilters.NEXT_SEVEN_DAYS, text: 'Next 7 days' }, { value: types.QuickFilters.NEXT_THIRTY_DAYS, text: 'Next 30 days' }, { value: types.QuickFilters.NEXT_MONTH, text: 'Next month' }, { value: types.QuickFilters.NEXT_FULL_MONTH, text: 'Next full month' }, { value: types.QuickFilters.NEXT_NINETY_DAYS, text: 'Next 90 days' }, { value: types.QuickFilters.NEXT_THREE_MONTHS, text: 'Next 3 months' }, { value: types.QuickFilters.NEXT_FULL_THREE_MONTHS, text: 'Next full 3 months' }, { value: types.QuickFilters.NEXT_SIX_MONTHS, text: 'Next 6 months' }, { value: types.QuickFilters.NEXT_FULL_SIX_MONTHS, text: 'Next full 6 months' }, { value: types.QuickFilters.NEXT_YEAR, text: 'Next year' }, { value: types.QuickFilters.NEXT_FULL_YEAR, text: 'Next full year' }, ]; return options; }; const getGranularityOptions = () => { const options = [ { value: types.Granularity.HOURLY, text: 'Hourly' }, { value: types.Granularity.DAILY, text: 'Daily' }, // { value: Granularity.WEEKLY, text: 'Weekly' }, { value: types.Granularity.MONTHLY, text: 'Monthly' }, // { value: Granularity.QUARTERLY, text: 'Quarterly' }, { value: types.Granularity.ANNUALLY, text: 'Annually' }, ]; return options; }; const setGranularityValue = (quickFilterValue, granularityValue) => { let granularity = granularityValue; const invalidDailyValues = ['annually', 'monthly']; const invalidMonthlyValues = ['annually']; if (quickFilterValue.includes('month') && invalidMonthlyValues.includes(quickFilterValue)) { granularity = types.Granularity.MONTHLY; } if (quickFilterValue.includes('day') && invalidDailyValues.includes(quickFilterValue)) { granularity = types.Granularity.DAILY; } return granularity; }; const setDateRangeFromQuickFilterValue = (value) => { let startDate; let endDate; let granularity; switch (value) { case types.QuickFilters.TODAY: endDate = dayjs(); startDate = dayjs(); granularity = types.Granularity.DAILY; break; case types.QuickFilters.YESTERDAY: endDate = dayjs().subtract(1, 'day'); startDate = dayjs().subtract(1, 'day'); granularity = types.Granularity.DAILY; break; case types.QuickFilters.LAST_SEVEN_DAYS: endDate = dayjs(); startDate = endDate.subtract(7, 'day').add(1, 'day'); granularity = types.Granularity.DAILY; break; case types.QuickFilters.LAST_THIRTY_DAYS: endDate = dayjs(); startDate = endDate.subtract(30, 'day').add(1, 'day'); granularity = types.Granularity.DAILY; break; case types.QuickFilters.LAST_MONTH: endDate = dayjs(); startDate = endDate.subtract(1, 'month').add(1, 'day'); granularity = types.Granularity.MONTHLY; break; case types.QuickFilters.LAST_FULL_MONTH: endDate = dayjs().subtract(1, 'month').endOf('month'); startDate = endDate.startOf('month'); granularity = types.Granularity.MONTHLY; break; case types.QuickFilters.LAST_NINETY_DAYS: endDate = dayjs(); startDate = endDate.subtract(90, 'day').add(1, 'day'); granularity = types.Granularity.DAILY; break; case types.QuickFilters.LAST_THREE_MONTHS: endDate = dayjs(); startDate = endDate.subtract(3, 'month').add(1, 'day'); granularity = types.Granularity.MONTHLY; break; case types.QuickFilters.LAST_FULL_THREE_MONTHS: endDate = dayjs().subtract(1, 'month').endOf('month'); startDate = endDate.subtract(2, 'month').startOf('month'); granularity = types.Granularity.MONTHLY; break; case types.QuickFilters.LAST_SIX_MONTHS: endDate = dayjs(); startDate = endDate.subtract(6, 'month').add(1, 'day'); granularity = types.Granularity.MONTHLY; break; case types.QuickFilters.LAST_FULL_SIX_MONTHS: endDate = dayjs().subtract(1, 'month').endOf('month'); startDate = endDate.subtract(6, 'month').startOf('month'); granularity = types.Granularity.MONTHLY; break; case types.QuickFilters.LAST_YEAR: endDate = dayjs(); startDate = endDate.subtract(1, 'year').add(1, 'day'); granularity = types.Granularity.ANNUALLY; break; case types.QuickFilters.LAST_FULL_YEAR: endDate = dayjs().subtract(1, 'year').endOf('year'); startDate = endDate.startOf('year'); granularity = types.Granularity.ANNUALLY; break; case types.QuickFilters.TOMORROW: endDate = dayjs().add(1, 'day'); startDate = dayjs().add(1, 'day'); granularity = types.Granularity.DAILY; break; case types.QuickFilters.NEXT_SEVEN_DAYS: startDate = dayjs(); endDate = dayjs().add(7, 'day').subtract(1, 'day'); granularity = types.Granularity.DAILY; break; case types.QuickFilters.NEXT_THIRTY_DAYS: startDate = dayjs(); endDate = dayjs().add(30, 'day').subtract(1, 'day'); granularity = types.Granularity.DAILY; break; case types.QuickFilters.NEXT_MONTH: startDate = dayjs(); endDate = dayjs().add(1, 'month').subtract(1, 'day'); granularity = types.Granularity.MONTHLY; break; case types.QuickFilters.NEXT_FULL_MONTH: startDate = dayjs().add(1, 'month').startOf('month'); endDate = startDate.endOf('month'); granularity = types.Granularity.MONTHLY; break; case types.QuickFilters.NEXT_NINETY_DAYS: startDate = dayjs(); endDate = dayjs().add(90, 'day').subtract(1, 'day'); granularity = types.Granularity.DAILY; break; case types.QuickFilters.NEXT_THREE_MONTHS: startDate = dayjs(); endDate = dayjs().add(3, 'month').subtract(1, 'day'); granularity = types.Granularity.MONTHLY; break; case types.QuickFilters.NEXT_FULL_THREE_MONTHS: startDate = dayjs().add(1, 'month').startOf('month'); endDate = startDate.add(2, 'month').endOf('month'); granularity = types.Granularity.MONTHLY; break; case types.QuickFilters.NEXT_YEAR: startDate = dayjs(); endDate = dayjs().add(1, 'year').subtract(1, 'day'); granularity = types.Granularity.ANNUALLY; break; case types.QuickFilters.NEXT_FULL_YEAR: startDate = dayjs().add(1, 'year').startOf('year'); endDate = startDate.endOf('year'); granularity = types.Granularity.ANNUALLY; break; default: endDate = dayjs(); startDate = dayjs(); granularity = types.Granularity.DAILY; break; } return { start: startDate.format('YYYY-MM-DD').toString(), end: endDate.format('YYYY-MM-DD').toString(), granularity, }; }; const getOptionDisplayValue = (value, options) => { const item = options.find(opt => { return opt.value === value; }); return item?.text || value; }; const setValueFromRequest = (filter, req, prefix) => { const { preventDefault } = req.query; const quickFilter = req.query[`${prefix}${filter.name}.quick-filter`]; let granularity = req.query[`${prefix}${filter.name}.granularity`]; let start; let end; if (quickFilter && quickFilter !== types.QuickFilters.NONE) { ({ start, end } = setDateRangeFromQuickFilterValue(quickFilter)); granularity = setGranularityValue(quickFilter, granularity); } else { granularity = req.query[`${prefix}${filter.name}.granularity`]; start = req.query[`${prefix}${filter.name}.start`]; end = req.query[`${prefix}${filter.name}.end`]; } const defaultStart = preventDefault ? null : filter.value?.start; const defaultEnd = preventDefault ? null : filter.value?.end; const defaultGranularity = preventDefault ? types.Granularity.DAILY : filter.value?.granularity.value; const defaultQuickFilter = preventDefault ? 'none' : filter.value?.quickFilter?.value; const granularityOptions = getGranularityOptions(); const quickFilterOptions = getQuickFilterOptions(); const startDate = start || defaultStart || filter.min || ''; const endDate = end || defaultEnd || filter.max || ''; const granularityValue = granularity || defaultGranularity || types.Granularity.NONE; const quickFilterValue = quickFilter || defaultQuickFilter || types.QuickFilters.NONE; const value = { start: startDate, end: endDate, granularity: { value: granularityValue, display: getOptionDisplayValue(granularityValue, granularityOptions), }, quickFilter: { value: quickFilterValue, display: getOptionDisplayValue(quickFilterValue, quickFilterOptions), }, partialDate: { ...hasPartialStartEnd(granularityValue, startDate, endDate), }, }; return value; }; const setDefaultValue = (req, name) => { const dateRangeName = name.split('.')[0]; const granularDateRangeDefaults = Object.keys(req.body) .filter(key => key.includes(dateRangeName)) .map(key => { return { name: key, value: req.body[key] }; }); let granularDateRangeValue = { start: '', end: '', granularity: types.Granularity.NONE, quickFilter: types.QuickFilters.NONE, }; granularDateRangeDefaults.forEach(dateRangeDefault => { if (dateRangeDefault.name.includes('start')) { granularDateRangeValue.start = dateRangeDefault.value; } if (dateRangeDefault.name.includes('end')) { granularDateRangeValue.end = dateRangeDefault.value; } if (dateRangeDefault.name.includes('granularity')) { granularDateRangeValue.granularity = dateRangeDefault.value; } if (dateRangeDefault.name.includes('granularity')) { granularDateRangeValue.quickFilter = dateRangeDefault.value; } }); granularDateRangeValue = granularDateRangeValue.start !== '' && granularDateRangeValue.end !== '' && granularDateRangeValue.granularity && granularDateRangeValue.quickFilter ? granularDateRangeValue : ''; return { value: granularDateRangeValue, name: dateRangeName }; }; const setFilterValueFromDefault = (defaultValue, filter) => { const { granularityOptions, quickFilterOptions } = filter; const { granularity, quickFilter, start, end } = defaultValue.value; const value = { start: start ? dayjs(start, 'D/M/YYYY').format('YYYY-MM-DD').toString() : '', end: end ? dayjs(end, 'D/M/YYYY').format('YYYY-MM-DD').toString() : '', granularity: { value: granularity, display: granularityOptions?.find(o => o.value === granularity)?.text || granularity, }, quickFilter: { value: quickFilter, display: quickFilterOptions?.find(o => o.value === quickFilter)?.text || quickFilter, }, }; return { ...filter, value, }; }; const getFilterFromDefinition = (filter, filterData) => { let value; const quickFilterValue = filter.defaultQuickFilterValue; const granularityOptions = getGranularityOptions(); const quickFilterOptions = getQuickFilterOptions(); if (quickFilterValue) { const { start, end, granularity } = setDateRangeFromQuickFilterValue(quickFilterValue); value = { start, end, granularity: { value: granularity, display: getOptionDisplayValue(granularity, granularityOptions), }, quickFilter: { value: quickFilterValue, display: getOptionDisplayValue(quickFilterValue, quickFilterOptions), }, partialDate: { ...hasPartialStartEnd(granularity, start, end), }, }; } else { value = utils.default.getStartAndEndValueFromDefinition(filter); const granularityValue = value.granularity ? value.granularity.value : filter.defaultGranularity || types.Granularity.DAILY; value = { ...value, granularity: { value: granularityValue, display: getOptionDisplayValue(granularityValue, granularityOptions), }, quickFilter: { value: types.QuickFilters.NONE, display: 'None', }, partialDate: { ...hasPartialStartEnd(granularityValue, value.start, value.end), }, }; } return { ...filterData, min: filter.min, max: filter.max, value, quickFilterOptions: getQuickFilterOptions(), granularityOptions: getGranularityOptions(), }; }; /** * Creates and appends the granular daterange query string * to an existing URLSearchParams * * @param {URLSearchParams} params * @param {string} fieldName * @param {string} defaultValue */ const appendGranularDateRangeValue = (params, fieldName, options) => { const { start, end, granularity, quickFilter } = options; if (quickFilter) { params.append(`filters.${fieldName}.quick-filter`, quickFilter); } if (granularity) { params.append(`filters.${fieldName}.granularity`, granularity); } if (start) { params.append(`filters.${fieldName}.start`, start); } if (end) { params.append(`filters.${fieldName}.end`, end); } }; /** * Resolves default values for a `granulardaterange` filter definition. * * Applies precedence rules: * 1. `defaultQuickFilterValue` (derives start, end, and granularity) * 2. `defaultGranularity` combined with `defaultValue` (explicit range) * 3. `defaultGranularity` only * * Returns plain resolved values suitable for query construction, or * `undefined` if the definition does not specify any usable defaults. * * @param {components['schemas']['FilterDefinition']} filter * @return {*} {(GranularDateRangeDefaults | undefined)} */ const resolveGranularDateRangeDefaults = (filter) => { // Case 1: quick filter wins if (filter.defaultQuickFilterValue) { const { start, end, granularity } = setDateRangeFromQuickFilterValue(filter.defaultQuickFilterValue); return { quickFilter: filter.defaultQuickFilterValue, granularity, ...(start !== undefined ? { start } : {}), ...(end !== undefined ? { end } : {}), }; } // Case 2: granularity + explicit default range if (filter.defaultGranularity && filter.defaultValue) { const [start, end] = filter.defaultValue .split(' - ') .map(v => v.trim()) .filter(Boolean); return { granularity: filter.defaultGranularity, ...(start !== undefined ? { start } : {}), ...(end !== undefined ? { end } : {}), }; } // Case 3: granularity only if (filter.defaultGranularity) { return { granularity: filter.defaultGranularity, }; } return undefined; }; var GranularDateRangeInputUtils = { getFilterFromDefinition, setValueFromRequest, setDefaultValue, setFilterValueFromDefault, appendGranularDateRangeValue, resolveGranularDateRangeDefaults, }; exports.appendGranularDateRangeValue = appendGranularDateRangeValue; exports.default = GranularDateRangeInputUtils; exports.getFilterFromDefinition = getFilterFromDefinition; exports.resolveGranularDateRangeDefaults = resolveGranularDateRangeDefaults; exports.setDefaultValue = setDefaultValue; exports.setFilterValueFromDefault = setFilterValueFromDefault; exports.setValueFromRequest = setValueFromRequest; //# sourceMappingURL=utils.js.map