@dhis2/analytics 29.2.1 → 29.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/cjs/components/PeriodDimension/FixedPeriodFilter.js +5 -10
- package/build/cjs/components/PeriodDimension/FixedPeriodSelect.js +3 -1
- package/build/cjs/components/PeriodDimension/PeriodDimension.js +15 -5
- package/build/cjs/components/PeriodDimension/PeriodTransfer.js +110 -14
- package/build/cjs/components/PeriodDimension/RelativePeriodFilter.js +4 -6
- package/build/cjs/components/PeriodDimension/__tests__/PeriodDimension.spec.js +19 -6
- package/build/cjs/components/PeriodDimension/__tests__/fixedPeriods.spec.js +1 -1
- package/build/cjs/components/PeriodDimension/__tests__/utils.spec.js +1 -1
- package/build/cjs/components/PeriodDimension/useDataOutputPeriodTypes.js +153 -0
- package/build/cjs/components/PeriodDimension/utils/enabledPeriodTypes.js +179 -0
- package/build/cjs/components/PeriodDimension/utils/fixedPeriods.js +24 -42
- package/build/cjs/components/PeriodDimension/utils/index.js +7 -4
- package/build/cjs/index.js +8 -0
- package/build/cjs/locales/en/translations.json +8 -3
- package/build/cjs/visualizations/util/getFilterText.js +1 -1
- package/build/es/components/PeriodDimension/FixedPeriodFilter.js +5 -10
- package/build/es/components/PeriodDimension/FixedPeriodSelect.js +4 -2
- package/build/es/components/PeriodDimension/PeriodDimension.js +15 -5
- package/build/es/components/PeriodDimension/PeriodTransfer.js +115 -19
- package/build/es/components/PeriodDimension/RelativePeriodFilter.js +4 -6
- package/build/es/components/PeriodDimension/__tests__/PeriodDimension.spec.js +19 -6
- package/build/es/components/PeriodDimension/__tests__/fixedPeriods.spec.js +1 -1
- package/build/es/components/PeriodDimension/__tests__/utils.spec.js +1 -1
- package/build/es/components/PeriodDimension/useDataOutputPeriodTypes.js +147 -0
- package/build/es/components/PeriodDimension/utils/enabledPeriodTypes.js +168 -0
- package/build/es/components/PeriodDimension/utils/fixedPeriods.js +25 -43
- package/build/es/components/PeriodDimension/utils/index.js +6 -3
- package/build/es/index.js +1 -0
- package/build/es/locales/en/translations.json +8 -3
- package/build/es/visualizations/util/getFilterText.js +1 -1
- package/package.json +1 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import _JSXStyle from "styled-jsx/style";
|
|
2
2
|
function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
|
|
3
3
|
import { getNowInCalendar } from '@dhis2/multi-calendar-dates';
|
|
4
|
-
import { IconInfo16, TabBar, Tab, Transfer } from '@dhis2/ui';
|
|
4
|
+
import { IconInfo16, NoticeBox, TabBar, Tab, Transfer } from '@dhis2/ui';
|
|
5
5
|
import PropTypes from 'prop-types';
|
|
6
|
-
import React, { useState } from 'react';
|
|
6
|
+
import React, { useRef, useState, useMemo } from 'react';
|
|
7
7
|
import PeriodIcon from '../../assets/DimensionItemIcons/PeriodIcon.js'; //TODO: Reimplement the icon.js
|
|
8
8
|
import i18n from '../../locales/index.js';
|
|
9
9
|
import { TRANSFER_HEIGHT, TRANSFER_OPTIONS_WIDTH, TRANSFER_SELECTED_WIDTH } from '../../modules/dimensionSelectorHelper.js';
|
|
@@ -11,9 +11,10 @@ import styles from '../styles/DimensionSelector.style.js';
|
|
|
11
11
|
import { TransferOption } from '../TransferOption.js';
|
|
12
12
|
import FixedPeriodFilter from './FixedPeriodFilter.js';
|
|
13
13
|
import RelativePeriodFilter from './RelativePeriodFilter.js';
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
14
|
+
import { applyDisplayLabelOverrides, applyFixedPeriodTypeDisplayLabels, filterEnabledFixedPeriodTypes, filterEnabledRelativePeriodTypes } from './utils/enabledPeriodTypes.js';
|
|
15
|
+
import { getFixedPeriodsOptions } from './utils/fixedPeriods.js';
|
|
16
|
+
import { MONTHLY, QUARTERLY, filterPeriodTypesById } from './utils/index.js';
|
|
17
|
+
import { getRelativePeriodsOptions } from './utils/relativePeriods.js';
|
|
17
18
|
const RightHeader = ({
|
|
18
19
|
infoBoxMessage
|
|
19
20
|
}) => /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("p", {
|
|
@@ -44,10 +45,52 @@ const PeriodTransfer = ({
|
|
|
44
45
|
excludedPeriodTypes = EXCLUDED_PERIOD_TYPES_PROP_DEFAULT,
|
|
45
46
|
periodsSettings = PERIODS_SETTINGS_PROP_DEFAULT,
|
|
46
47
|
infoBoxMessage,
|
|
47
|
-
height = TRANSFER_HEIGHT
|
|
48
|
+
height = TRANSFER_HEIGHT,
|
|
49
|
+
enabledPeriodTypesData = null,
|
|
50
|
+
supportsEnabledPeriodTypes = false
|
|
48
51
|
}) => {
|
|
49
|
-
const
|
|
50
|
-
|
|
52
|
+
const {
|
|
53
|
+
filteredFixedOptions,
|
|
54
|
+
filteredRelativeOptions
|
|
55
|
+
} = useMemo(() => {
|
|
56
|
+
if (supportsEnabledPeriodTypes && enabledPeriodTypesData) {
|
|
57
|
+
const {
|
|
58
|
+
enabledTypes,
|
|
59
|
+
financialYearStart,
|
|
60
|
+
financialYearDisplayLabel,
|
|
61
|
+
weeklyDisplayLabel,
|
|
62
|
+
metaData
|
|
63
|
+
} = enabledPeriodTypesData;
|
|
64
|
+
const filteredFixed = applyFixedPeriodTypeDisplayLabels(filterEnabledFixedPeriodTypes(getFixedPeriodsOptions(periodsSettings), enabledTypes), enabledTypes);
|
|
65
|
+
const filteredRelative = applyDisplayLabelOverrides(filterEnabledRelativePeriodTypes(getRelativePeriodsOptions(), enabledTypes, financialYearStart), {
|
|
66
|
+
financialYearDisplayLabel,
|
|
67
|
+
weeklyDisplayLabel,
|
|
68
|
+
metaData
|
|
69
|
+
});
|
|
70
|
+
return {
|
|
71
|
+
filteredFixedOptions: filteredFixed,
|
|
72
|
+
filteredRelativeOptions: filteredRelative
|
|
73
|
+
};
|
|
74
|
+
} else {
|
|
75
|
+
const allFixed = getFixedPeriodsOptions(periodsSettings);
|
|
76
|
+
const allRelative = getRelativePeriodsOptions();
|
|
77
|
+
return {
|
|
78
|
+
filteredFixedOptions: filterPeriodTypesById(allFixed, excludedPeriodTypes),
|
|
79
|
+
filteredRelativeOptions: filterPeriodTypesById(allRelative, excludedPeriodTypes)
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}, [supportsEnabledPeriodTypes, enabledPeriodTypesData, excludedPeriodTypes, periodsSettings]);
|
|
83
|
+
const analysisRelativePeriod = enabledPeriodTypesData === null || enabledPeriodTypesData === void 0 ? void 0 : enabledPeriodTypesData.analysisRelativePeriod;
|
|
84
|
+
const defaultRelativePeriodType = (() => {
|
|
85
|
+
if (analysisRelativePeriod) {
|
|
86
|
+
const match = filteredRelativeOptions.find(opt => opt.getPeriods().some(p => p.id === analysisRelativePeriod));
|
|
87
|
+
if (match) {
|
|
88
|
+
return match;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return filteredRelativeOptions.find(opt => opt.id === MONTHLY) || filteredRelativeOptions.find(opt => opt.id === QUARTERLY) || filteredRelativeOptions[0];
|
|
92
|
+
})();
|
|
93
|
+
const defaultFixedPeriodType = filteredFixedOptions.find(opt => opt.id === MONTHLY) || filteredFixedOptions.find(opt => opt.id === QUARTERLY) || filteredFixedOptions[0];
|
|
51
94
|
const now = getNowInCalendar(periodsSettings.calendar);
|
|
52
95
|
// use ".eraYear" rather than ".year" because in Ethiopian calendar, eraYear is what our users expect to see (for other calendars, it doesn't matter)
|
|
53
96
|
// there is still a pending decision in Temporal regarding which era to use by default: https://github.com/js-temporal/temporal-polyfill/blob/9350ee7dd0d29f329fc097debf923a517c32f813/lib/calendar.ts#L1964
|
|
@@ -57,15 +100,52 @@ const PeriodTransfer = ({
|
|
|
57
100
|
filterFuturePeriods: false,
|
|
58
101
|
reversePeriods: false
|
|
59
102
|
});
|
|
60
|
-
const [
|
|
103
|
+
const [userPeriods, setUserPeriods] = useState(null);
|
|
61
104
|
const [isRelative, setIsRelative] = useState(true);
|
|
62
105
|
const [relativeFilter, setRelativeFilter] = useState({
|
|
63
|
-
periodType: defaultRelativePeriodType.id
|
|
106
|
+
periodType: (defaultRelativePeriodType === null || defaultRelativePeriodType === void 0 ? void 0 : defaultRelativePeriodType.id) || ''
|
|
64
107
|
});
|
|
65
108
|
const [fixedFilter, setFixedFilter] = useState({
|
|
66
|
-
periodType: defaultFixedPeriodType.id,
|
|
109
|
+
periodType: (defaultFixedPeriodType === null || defaultFixedPeriodType === void 0 ? void 0 : defaultFixedPeriodType.id) || '',
|
|
67
110
|
year: defaultFixedPeriodYear.toString()
|
|
68
111
|
});
|
|
112
|
+
const effectiveRelativeFilterType = filteredRelativeOptions.some(opt => opt.id === relativeFilter.periodType) ? relativeFilter.periodType : (defaultRelativePeriodType === null || defaultRelativePeriodType === void 0 ? void 0 : defaultRelativePeriodType.id) || '';
|
|
113
|
+
const effectiveFixedFilterType = filteredFixedOptions.some(opt => opt.id === fixedFilter.periodType) ? fixedFilter.periodType : (defaultFixedPeriodType === null || defaultFixedPeriodType === void 0 ? void 0 : defaultFixedPeriodType.id) || '';
|
|
114
|
+
const prevEffectiveRelativeRef = useRef(effectiveRelativeFilterType);
|
|
115
|
+
const prevEffectiveFixedRef = useRef(effectiveFixedFilterType);
|
|
116
|
+
if (prevEffectiveRelativeRef.current !== effectiveRelativeFilterType) {
|
|
117
|
+
prevEffectiveRelativeRef.current = effectiveRelativeFilterType;
|
|
118
|
+
if (relativeFilter.periodType !== effectiveRelativeFilterType) {
|
|
119
|
+
setRelativeFilter({
|
|
120
|
+
periodType: effectiveRelativeFilterType
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
if (isRelative) {
|
|
124
|
+
setUserPeriods(null);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (prevEffectiveFixedRef.current !== effectiveFixedFilterType) {
|
|
128
|
+
prevEffectiveFixedRef.current = effectiveFixedFilterType;
|
|
129
|
+
if (fixedFilter.periodType !== effectiveFixedFilterType) {
|
|
130
|
+
setFixedFilter(prev => ({
|
|
131
|
+
...prev,
|
|
132
|
+
periodType: effectiveFixedFilterType
|
|
133
|
+
}));
|
|
134
|
+
}
|
|
135
|
+
if (!isRelative) {
|
|
136
|
+
setUserPeriods(null);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
const derivedPeriods = useMemo(() => {
|
|
140
|
+
if (isRelative) {
|
|
141
|
+
const opt = filteredRelativeOptions.find(o => o.id === effectiveRelativeFilterType);
|
|
142
|
+
return (opt === null || opt === void 0 ? void 0 : opt.getPeriods()) || [];
|
|
143
|
+
} else {
|
|
144
|
+
const opt = filteredFixedOptions.find(o => o.id === effectiveFixedFilterType);
|
|
145
|
+
return (opt === null || opt === void 0 ? void 0 : opt.getPeriods(fixedPeriodConfig(Number(fixedFilter.year)))) || [];
|
|
146
|
+
}
|
|
147
|
+
}, [isRelative, effectiveRelativeFilterType, effectiveFixedFilterType, filteredRelativeOptions, filteredFixedOptions, fixedFilter.year]);
|
|
148
|
+
const allPeriods = userPeriods !== null && userPeriods !== void 0 ? userPeriods : derivedPeriods;
|
|
69
149
|
const isActive = value => {
|
|
70
150
|
const item = selectedItems.find(item => item.id === value);
|
|
71
151
|
return !item || item.isActive;
|
|
@@ -73,9 +153,15 @@ const PeriodTransfer = ({
|
|
|
73
153
|
const onIsRelativeClick = state => {
|
|
74
154
|
if (state !== isRelative) {
|
|
75
155
|
setIsRelative(state);
|
|
76
|
-
|
|
156
|
+
setUserPeriods(null);
|
|
77
157
|
}
|
|
78
158
|
};
|
|
159
|
+
if (enabledPeriodTypesData !== null && enabledPeriodTypesData !== void 0 && enabledPeriodTypesData.noEnabledTypes) {
|
|
160
|
+
return /*#__PURE__*/React.createElement(NoticeBox, {
|
|
161
|
+
warning: true,
|
|
162
|
+
title: i18n.t('No period types available')
|
|
163
|
+
}, i18n.t('No period types are enabled in the system. Please contact your system administrator.'));
|
|
164
|
+
}
|
|
79
165
|
const renderLeftHeader = () => /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(TabBar, null, /*#__PURE__*/React.createElement(Tab, {
|
|
80
166
|
selected: isRelative,
|
|
81
167
|
onClick: () => onIsRelativeClick(true),
|
|
@@ -87,17 +173,18 @@ const PeriodTransfer = ({
|
|
|
87
173
|
}, i18n.t('Fixed periods'))), /*#__PURE__*/React.createElement("div", {
|
|
88
174
|
className: `jsx-${styles.__hash}` + " " + "filterContainer"
|
|
89
175
|
}, isRelative ? /*#__PURE__*/React.createElement(RelativePeriodFilter, {
|
|
90
|
-
currentFilter:
|
|
176
|
+
currentFilter: effectiveRelativeFilterType,
|
|
91
177
|
onSelectFilter: filter => {
|
|
92
178
|
setRelativeFilter({
|
|
93
179
|
periodType: filter
|
|
94
180
|
});
|
|
95
|
-
|
|
181
|
+
const selectedOption = filteredRelativeOptions.find(opt => opt.id === filter);
|
|
182
|
+
setUserPeriods((selectedOption === null || selectedOption === void 0 ? void 0 : selectedOption.getPeriods()) || []);
|
|
96
183
|
},
|
|
97
184
|
dataTest: `${dataTest}-relative-period-filter`,
|
|
98
|
-
|
|
185
|
+
availableOptions: filteredRelativeOptions
|
|
99
186
|
}) : /*#__PURE__*/React.createElement(FixedPeriodFilter, {
|
|
100
|
-
currentPeriodType:
|
|
187
|
+
currentPeriodType: effectiveFixedFilterType,
|
|
101
188
|
currentYear: fixedFilter.year,
|
|
102
189
|
onSelectPeriodType: periodType => {
|
|
103
190
|
onSelectFixedPeriods({
|
|
@@ -112,14 +199,15 @@ const PeriodTransfer = ({
|
|
|
112
199
|
});
|
|
113
200
|
},
|
|
114
201
|
dataTest: `${dataTest}-fixed-period-filter`,
|
|
115
|
-
|
|
202
|
+
availableOptions: filteredFixedOptions
|
|
116
203
|
})), /*#__PURE__*/React.createElement(_JSXStyle, {
|
|
117
204
|
id: styles.__hash
|
|
118
205
|
}, styles));
|
|
119
206
|
const onSelectFixedPeriods = filter => {
|
|
120
207
|
setFixedFilter(filter);
|
|
121
208
|
if (filter.year.match(/[0-9]{4}/)) {
|
|
122
|
-
|
|
209
|
+
const selectedOption = filteredFixedOptions.find(opt => opt.id === filter.periodType);
|
|
210
|
+
setUserPeriods((selectedOption === null || selectedOption === void 0 ? void 0 : selectedOption.getPeriods(fixedPeriodConfig(Number(filter.year)))) || []);
|
|
123
211
|
}
|
|
124
212
|
};
|
|
125
213
|
const renderEmptySelection = () => /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("p", {
|
|
@@ -174,6 +262,13 @@ const PeriodTransfer = ({
|
|
|
174
262
|
PeriodTransfer.propTypes = {
|
|
175
263
|
onSelect: PropTypes.func.isRequired,
|
|
176
264
|
dataTest: PropTypes.string,
|
|
265
|
+
enabledPeriodTypesData: PropTypes.shape({
|
|
266
|
+
analysisRelativePeriod: PropTypes.string,
|
|
267
|
+
enabledTypes: PropTypes.array,
|
|
268
|
+
financialYearDisplayLabel: PropTypes.string,
|
|
269
|
+
financialYearStart: PropTypes.string,
|
|
270
|
+
noEnabledTypes: PropTypes.bool
|
|
271
|
+
}),
|
|
177
272
|
excludedPeriodTypes: PropTypes.arrayOf(PropTypes.string),
|
|
178
273
|
height: PropTypes.string,
|
|
179
274
|
infoBoxMessage: PropTypes.string,
|
|
@@ -186,6 +281,7 @@ PeriodTransfer.propTypes = {
|
|
|
186
281
|
id: PropTypes.string,
|
|
187
282
|
isActive: PropTypes.bool,
|
|
188
283
|
name: PropTypes.string
|
|
189
|
-
}))
|
|
284
|
+
})),
|
|
285
|
+
supportsEnabledPeriodTypes: PropTypes.bool
|
|
190
286
|
};
|
|
191
287
|
export default PeriodTransfer;
|
|
@@ -4,13 +4,11 @@ import PropTypes from 'prop-types';
|
|
|
4
4
|
import React from 'react';
|
|
5
5
|
import i18n from '../../locales/index.js';
|
|
6
6
|
import styles from './styles/PeriodFilter.style.js';
|
|
7
|
-
import { filterPeriodTypesById } from './utils/index.js';
|
|
8
|
-
import { getRelativePeriodsOptions } from './utils/relativePeriods.js';
|
|
9
7
|
const RelativePeriodFilter = ({
|
|
10
8
|
currentFilter,
|
|
11
9
|
onSelectFilter,
|
|
12
10
|
dataTest,
|
|
13
|
-
|
|
11
|
+
availableOptions
|
|
14
12
|
}) => /*#__PURE__*/React.createElement("div", {
|
|
15
13
|
"data-test": dataTest,
|
|
16
14
|
className: `jsx-${styles.__hash}` + " " + "leftSection"
|
|
@@ -23,7 +21,7 @@ const RelativePeriodFilter = ({
|
|
|
23
21
|
selected: currentFilter,
|
|
24
22
|
className: "filterElement",
|
|
25
23
|
dataTest: `${dataTest}-period-type`
|
|
26
|
-
},
|
|
24
|
+
}, availableOptions.map(option => /*#__PURE__*/React.createElement(SingleSelectOption, {
|
|
27
25
|
key: option.id,
|
|
28
26
|
value: option.id,
|
|
29
27
|
label: option.name,
|
|
@@ -32,9 +30,9 @@ const RelativePeriodFilter = ({
|
|
|
32
30
|
id: styles.__hash
|
|
33
31
|
}, styles));
|
|
34
32
|
RelativePeriodFilter.propTypes = {
|
|
33
|
+
availableOptions: PropTypes.array.isRequired,
|
|
35
34
|
currentFilter: PropTypes.string.isRequired,
|
|
36
35
|
onSelectFilter: PropTypes.func.isRequired,
|
|
37
|
-
dataTest: PropTypes.string
|
|
38
|
-
excludedPeriodTypes: PropTypes.arrayOf(PropTypes.string)
|
|
36
|
+
dataTest: PropTypes.string
|
|
39
37
|
};
|
|
40
38
|
export default RelativePeriodFilter;
|
|
@@ -4,14 +4,27 @@ import React from 'react';
|
|
|
4
4
|
import PeriodDimension from '../PeriodDimension.js';
|
|
5
5
|
jest.mock('@dhis2/app-runtime', () => ({
|
|
6
6
|
useConfig: () => ({
|
|
7
|
-
systemInfo: {}
|
|
7
|
+
systemInfo: {},
|
|
8
|
+
serverVersion: {
|
|
9
|
+
minor: 42
|
|
10
|
+
}
|
|
8
11
|
}),
|
|
9
|
-
useDataQuery: () =>
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
useDataQuery: jest.fn().mockImplementation((_query, options) => {
|
|
13
|
+
if (options !== null && options !== void 0 && options.lazy) {
|
|
14
|
+
return {
|
|
15
|
+
data: null,
|
|
16
|
+
error: undefined,
|
|
17
|
+
loading: false,
|
|
18
|
+
refetch: jest.fn()
|
|
19
|
+
};
|
|
14
20
|
}
|
|
21
|
+
return {
|
|
22
|
+
data: {
|
|
23
|
+
userSettings: {
|
|
24
|
+
keyUiLocale: 'en'
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
};
|
|
15
28
|
})
|
|
16
29
|
}));
|
|
17
30
|
global.ResizeObserver = jest.fn().mockImplementation(() => ({
|
|
@@ -6,7 +6,7 @@ describe('fixedPeriods utils', () => {
|
|
|
6
6
|
describe('getOptions', () => {
|
|
7
7
|
it('should return a list of available period ranges', () => {
|
|
8
8
|
const periodIds = getFixedPeriodsOptions().map(option => option.id);
|
|
9
|
-
expect(periodIds).toEqual(['DAILY', 'WEEKLY', 'WEEKLYWED', 'WEEKLYTHU', 'WEEKLYSAT', 'WEEKLYSUN', 'BIWEEKLY', 'MONTHLY', 'BIMONTHLY', 'QUARTERLY', 'SIXMONTHLY', 'SIXMONTHLYAPR', 'YEARLY', '
|
|
9
|
+
expect(periodIds).toEqual(['DAILY', 'WEEKLY', 'WEEKLYWED', 'WEEKLYTHU', 'WEEKLYSAT', 'WEEKLYSUN', 'BIWEEKLY', 'MONTHLY', 'BIMONTHLY', 'QUARTERLY', 'SIXMONTHLY', 'SIXMONTHLYAPR', 'YEARLY', 'FYFEB', 'FYAPR', 'FYJUL', 'FYAUG', 'FYSEP', 'FYOCT', 'FYNOV']);
|
|
10
10
|
});
|
|
11
11
|
});
|
|
12
12
|
describe('Daily period generator', () => {
|
|
@@ -6,7 +6,7 @@ describe('utils', () => {
|
|
|
6
6
|
it('should filter fixed periods', () => {
|
|
7
7
|
const excludedPeriodTypes = ['DAILY', 'WEEKLY', 'WEEKLYSAT', 'MONTHLY', 'FYOCT'];
|
|
8
8
|
const periodIds = filterPeriodTypesById(getFixedPeriodsOptions(), excludedPeriodTypes).map(option => option.id);
|
|
9
|
-
expect(periodIds).toEqual(['WEEKLYWED', 'WEEKLYTHU', 'WEEKLYSUN', 'BIWEEKLY', 'BIMONTHLY', 'QUARTERLY', 'SIXMONTHLY', 'SIXMONTHLYAPR', 'YEARLY', '
|
|
9
|
+
expect(periodIds).toEqual(['WEEKLYWED', 'WEEKLYTHU', 'WEEKLYSUN', 'BIWEEKLY', 'BIMONTHLY', 'QUARTERLY', 'SIXMONTHLY', 'SIXMONTHLYAPR', 'YEARLY', 'FYFEB', 'FYAPR', 'FYJUL', 'FYAUG', 'FYSEP', 'FYNOV']);
|
|
10
10
|
});
|
|
11
11
|
it('should filter relative periods', () => {
|
|
12
12
|
const excludedPeriodTypes = ['MONTHLY', 'BIMONTHLY'];
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { useConfig, useDataQuery } from '@dhis2/app-runtime';
|
|
2
|
+
import { useEffect, useMemo } from 'react';
|
|
3
|
+
const v43Query = {
|
|
4
|
+
enabledPeriodTypes: {
|
|
5
|
+
resource: 'configuration/dataOutputPeriodTypes'
|
|
6
|
+
},
|
|
7
|
+
// v43-only: analyticsFinancialYearStart is removed in v44
|
|
8
|
+
financialYearStart: {
|
|
9
|
+
resource: 'systemSettings/analyticsFinancialYearStart'
|
|
10
|
+
},
|
|
11
|
+
analysisRelativePeriod: {
|
|
12
|
+
resource: 'systemSettings/keyAnalysisRelativePeriod'
|
|
13
|
+
},
|
|
14
|
+
// v43-only: analyticsWeeklyStart is removed in v44
|
|
15
|
+
weeklyStart: {
|
|
16
|
+
resource: 'systemSettings/analyticsWeeklyStart'
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
// v43-only: analyticsFinancialYearStart is removed in v44
|
|
21
|
+
const FY_SETTING_TO_SERVER_PT = {
|
|
22
|
+
FINANCIAL_YEAR_APRIL: 'FinancialApril',
|
|
23
|
+
FINANCIAL_YEAR_JULY: 'FinancialJuly',
|
|
24
|
+
FINANCIAL_YEAR_SEPTEMBER: 'FinancialSep',
|
|
25
|
+
FINANCIAL_YEAR_OCTOBER: 'FinancialOct',
|
|
26
|
+
FINANCIAL_YEAR_NOVEMBER: 'FinancialNov'
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// v43-only: analyticsWeeklyStart is removed in v44
|
|
30
|
+
const WEEKLY_START_TO_SERVER_PT = {
|
|
31
|
+
WEEKLY: 'Weekly',
|
|
32
|
+
WEEKLY_WEDNESDAY: 'WeeklyWednesday',
|
|
33
|
+
WEEKLY_THURSDAY: 'WeeklyThursday',
|
|
34
|
+
WEEKLY_FRIDAY: 'WeeklyFriday',
|
|
35
|
+
WEEKLY_SATURDAY: 'WeeklySaturday',
|
|
36
|
+
WEEKLY_SUNDAY: 'WeeklySunday'
|
|
37
|
+
};
|
|
38
|
+
const useDataOutputPeriodTypes = () => {
|
|
39
|
+
const {
|
|
40
|
+
serverVersion
|
|
41
|
+
} = useConfig();
|
|
42
|
+
const supportsEnabledPeriodTypes = serverVersion.minor >= 43;
|
|
43
|
+
const {
|
|
44
|
+
data: v43Data,
|
|
45
|
+
error: v43Error,
|
|
46
|
+
refetch: v43Refetch
|
|
47
|
+
} = useDataQuery(v43Query, {
|
|
48
|
+
lazy: true
|
|
49
|
+
});
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
if (supportsEnabledPeriodTypes) {
|
|
52
|
+
v43Refetch();
|
|
53
|
+
}
|
|
54
|
+
}, [supportsEnabledPeriodTypes, v43Refetch]);
|
|
55
|
+
const enabledPeriodTypesData = useMemo(() => {
|
|
56
|
+
var _v43Data$financialYea, _v43Data$weeklyStart, _v43Data$analysisRela;
|
|
57
|
+
if (!supportsEnabledPeriodTypes) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
if (v43Error || !(v43Data !== null && v43Data !== void 0 && v43Data.enabledPeriodTypes)) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
const enabledTypes = v43Data.enabledPeriodTypes;
|
|
64
|
+
if (!enabledTypes || enabledTypes.length === 0) {
|
|
65
|
+
return {
|
|
66
|
+
enabledTypes: [],
|
|
67
|
+
financialYearStart: null,
|
|
68
|
+
analysisRelativePeriod: null,
|
|
69
|
+
noEnabledTypes: true
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// v43-only: financial year logic goes away in v44
|
|
74
|
+
let financialYearStart = null;
|
|
75
|
+
let financialYearDisplayLabel = null;
|
|
76
|
+
if ((_v43Data$financialYea = v43Data.financialYearStart) !== null && _v43Data$financialYea !== void 0 && _v43Data$financialYea.analyticsFinancialYearStart) {
|
|
77
|
+
const fyStartValue = v43Data.financialYearStart.analyticsFinancialYearStart;
|
|
78
|
+
const mappedFyPt = FY_SETTING_TO_SERVER_PT[fyStartValue];
|
|
79
|
+
const matchingPt = enabledTypes.find(pt => pt.name === mappedFyPt);
|
|
80
|
+
if (matchingPt) {
|
|
81
|
+
financialYearStart = fyStartValue;
|
|
82
|
+
if (matchingPt.displayLabel) {
|
|
83
|
+
financialYearDisplayLabel = matchingPt.displayLabel;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// v43-only: weekly start logic goes away in v44
|
|
89
|
+
let weeklyDisplayLabel = null;
|
|
90
|
+
if ((_v43Data$weeklyStart = v43Data.weeklyStart) !== null && _v43Data$weeklyStart !== void 0 && _v43Data$weeklyStart.analyticsWeeklyStart) {
|
|
91
|
+
const weeklyStartValue = v43Data.weeklyStart.analyticsWeeklyStart;
|
|
92
|
+
const mappedWeeklyPt = WEEKLY_START_TO_SERVER_PT[weeklyStartValue];
|
|
93
|
+
const matchingWeeklyPt = enabledTypes.find(pt => pt.name === mappedWeeklyPt);
|
|
94
|
+
if (matchingWeeklyPt !== null && matchingWeeklyPt !== void 0 && matchingWeeklyPt.displayLabel) {
|
|
95
|
+
weeklyDisplayLabel = matchingWeeklyPt.displayLabel;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const analysisRelativePeriod = ((_v43Data$analysisRela = v43Data.analysisRelativePeriod) === null || _v43Data$analysisRela === void 0 ? void 0 : _v43Data$analysisRela.keyAnalysisRelativePeriod) || null;
|
|
99
|
+
const metaData = {
|
|
100
|
+
...(financialYearDisplayLabel && {
|
|
101
|
+
THIS_FINANCIAL_YEAR: {
|
|
102
|
+
name: `This ${financialYearDisplayLabel}`
|
|
103
|
+
},
|
|
104
|
+
LAST_FINANCIAL_YEAR: {
|
|
105
|
+
name: `Last ${financialYearDisplayLabel}`
|
|
106
|
+
},
|
|
107
|
+
LAST_5_FINANCIAL_YEARS: {
|
|
108
|
+
name: `Last 5 ${financialYearDisplayLabel}`
|
|
109
|
+
}
|
|
110
|
+
}),
|
|
111
|
+
...(weeklyDisplayLabel && {
|
|
112
|
+
THIS_WEEK: {
|
|
113
|
+
name: `This ${weeklyDisplayLabel}`
|
|
114
|
+
},
|
|
115
|
+
LAST_WEEK: {
|
|
116
|
+
name: `Last ${weeklyDisplayLabel}`
|
|
117
|
+
},
|
|
118
|
+
LAST_4_WEEKS: {
|
|
119
|
+
name: `Last 4 ${weeklyDisplayLabel}`
|
|
120
|
+
},
|
|
121
|
+
LAST_12_WEEKS: {
|
|
122
|
+
name: `Last 12 ${weeklyDisplayLabel}`
|
|
123
|
+
},
|
|
124
|
+
LAST_52_WEEKS: {
|
|
125
|
+
name: `Last 52 ${weeklyDisplayLabel}`
|
|
126
|
+
},
|
|
127
|
+
WEEKS_THIS_YEAR: {
|
|
128
|
+
name: `${weeklyDisplayLabel} this year`
|
|
129
|
+
}
|
|
130
|
+
})
|
|
131
|
+
};
|
|
132
|
+
return {
|
|
133
|
+
enabledTypes,
|
|
134
|
+
financialYearStart,
|
|
135
|
+
financialYearDisplayLabel,
|
|
136
|
+
weeklyDisplayLabel,
|
|
137
|
+
analysisRelativePeriod,
|
|
138
|
+
metaData,
|
|
139
|
+
noEnabledTypes: false
|
|
140
|
+
};
|
|
141
|
+
}, [supportsEnabledPeriodTypes, v43Data, v43Error]);
|
|
142
|
+
return {
|
|
143
|
+
supportsEnabledPeriodTypes,
|
|
144
|
+
enabledPeriodTypesData
|
|
145
|
+
};
|
|
146
|
+
};
|
|
147
|
+
export { useDataOutputPeriodTypes };
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
// Mapping from server period type names to multi-calendar-dates constants
|
|
2
|
+
export const SERVER_PT_TO_MULTI_CALENDAR_PT = {
|
|
3
|
+
Daily: 'DAILY',
|
|
4
|
+
Weekly: 'WEEKLY',
|
|
5
|
+
WeeklyWednesday: 'WEEKLYWED',
|
|
6
|
+
WeeklyThursday: 'WEEKLYTHU',
|
|
7
|
+
WeeklyFriday: 'WEEKLYFRI',
|
|
8
|
+
WeeklySaturday: 'WEEKLYSAT',
|
|
9
|
+
WeeklySunday: 'WEEKLYSUN',
|
|
10
|
+
BiWeekly: 'BIWEEKLY',
|
|
11
|
+
Monthly: 'MONTHLY',
|
|
12
|
+
BiMonthly: 'BIMONTHLY',
|
|
13
|
+
Quarterly: 'QUARTERLY',
|
|
14
|
+
QuarterlyNov: 'QUARTERLYNOV',
|
|
15
|
+
SixMonthly: 'SIXMONTHLY',
|
|
16
|
+
SixMonthlyApril: 'SIXMONTHLYAPR',
|
|
17
|
+
SixMonthlyNov: 'SIXMONTHLYNOV',
|
|
18
|
+
Yearly: 'YEARLY',
|
|
19
|
+
FinancialFeb: 'FYFEB',
|
|
20
|
+
FinancialApril: 'FYAPR',
|
|
21
|
+
FinancialJuly: 'FYJUL',
|
|
22
|
+
FinancialAug: 'FYAUG',
|
|
23
|
+
FinancialSep: 'FYSEP',
|
|
24
|
+
FinancialOct: 'FYOCT',
|
|
25
|
+
FinancialNov: 'FYNOV'
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// Mapping from relative period categories to their corresponding fixed period types
|
|
29
|
+
export const RP_CATEGORY_TO_FP_DEPENDENCIES = {
|
|
30
|
+
DAILY: ['Daily'],
|
|
31
|
+
WEEKLY: ['Weekly', 'WeeklyWednesday', 'WeeklyThursday', 'WeeklyFriday', 'WeeklySaturday', 'WeeklySunday'],
|
|
32
|
+
BIWEEKLY: ['BiWeekly'],
|
|
33
|
+
MONTHLY: ['Monthly'],
|
|
34
|
+
BIMONTHLY: ['BiMonthly'],
|
|
35
|
+
QUARTERLY: ['Quarterly', 'QuarterlyNov'],
|
|
36
|
+
SIXMONTHLY: ['SixMonthly', 'SixMonthlyApril', 'SixMonthlyNov'],
|
|
37
|
+
YEARLY: ['Yearly']
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Filter fixed period types based on enabled server period types
|
|
42
|
+
* @param {Array} allFixedPeriodOptions - All available fixed period options
|
|
43
|
+
* @param {Array} enabledServerPeriodTypes - Enabled period types from server
|
|
44
|
+
* @returns {Array} Filtered fixed period options
|
|
45
|
+
*/
|
|
46
|
+
export const filterEnabledFixedPeriodTypes = (allFixedPeriodOptions, enabledServerPeriodTypes) => {
|
|
47
|
+
if (!enabledServerPeriodTypes || enabledServerPeriodTypes.length === 0) {
|
|
48
|
+
return [];
|
|
49
|
+
}
|
|
50
|
+
const enabledServerPtNames = new Set(enabledServerPeriodTypes.map(pt => pt.name));
|
|
51
|
+
const enabledMultiCalendarPts = new Set();
|
|
52
|
+
|
|
53
|
+
// Map server PT names to multi-calendar-dates constants
|
|
54
|
+
enabledServerPtNames.forEach(serverPtName => {
|
|
55
|
+
const multiCalendarPt = SERVER_PT_TO_MULTI_CALENDAR_PT[serverPtName];
|
|
56
|
+
if (multiCalendarPt) {
|
|
57
|
+
enabledMultiCalendarPts.add(multiCalendarPt);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Filter fixed period options to only include enabled ones
|
|
62
|
+
return allFixedPeriodOptions.filter(option => enabledMultiCalendarPts.has(option.id));
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Apply displayLabel overrides to fixed period type names
|
|
67
|
+
* v43-only: in v44 the API provides these names directly
|
|
68
|
+
*/
|
|
69
|
+
export const applyFixedPeriodTypeDisplayLabels = (filteredFixedOptions, enabledServerPeriodTypes) => {
|
|
70
|
+
if (!enabledServerPeriodTypes) {
|
|
71
|
+
return filteredFixedOptions;
|
|
72
|
+
}
|
|
73
|
+
const displayLabelMap = new Map();
|
|
74
|
+
enabledServerPeriodTypes.forEach(pt => {
|
|
75
|
+
if (pt.displayLabel) {
|
|
76
|
+
const multiCalendarPt = SERVER_PT_TO_MULTI_CALENDAR_PT[pt.name];
|
|
77
|
+
if (multiCalendarPt) {
|
|
78
|
+
displayLabelMap.set(multiCalendarPt, pt.displayLabel);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
if (displayLabelMap.size === 0) {
|
|
83
|
+
return filteredFixedOptions;
|
|
84
|
+
}
|
|
85
|
+
return filteredFixedOptions.map(option => {
|
|
86
|
+
const displayLabel = displayLabelMap.get(option.id);
|
|
87
|
+
return displayLabel ? {
|
|
88
|
+
...option,
|
|
89
|
+
name: displayLabel
|
|
90
|
+
} : option;
|
|
91
|
+
});
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Filter relative period categories based on enabled server period types
|
|
96
|
+
* @param {Array} allRelativePeriodOptions - All available relative period options
|
|
97
|
+
* @param {Array} enabledServerPeriodTypes - Enabled period types from server
|
|
98
|
+
* @param {string|null} financialYearStart - Financial year start setting (if enabled)
|
|
99
|
+
* @returns {Array} Filtered relative period options
|
|
100
|
+
*/
|
|
101
|
+
/**
|
|
102
|
+
* Apply metaData name overrides to a list of periods
|
|
103
|
+
* v43-only: in v44 the API provides these names directly
|
|
104
|
+
*/
|
|
105
|
+
export const applyPeriodNameOverrides = (periods, metaData) => {
|
|
106
|
+
if (!metaData) {
|
|
107
|
+
return periods;
|
|
108
|
+
}
|
|
109
|
+
return periods.map(period => metaData[period.id] ? {
|
|
110
|
+
...period,
|
|
111
|
+
name: metaData[period.id].name
|
|
112
|
+
} : period);
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Apply display label overrides to relative period options
|
|
117
|
+
* v43-only: in v44 the API provides these names directly
|
|
118
|
+
*/
|
|
119
|
+
export const applyDisplayLabelOverrides = (filteredRelativeOptions, {
|
|
120
|
+
financialYearDisplayLabel,
|
|
121
|
+
weeklyDisplayLabel,
|
|
122
|
+
metaData
|
|
123
|
+
}) => {
|
|
124
|
+
const overrides = {};
|
|
125
|
+
if (financialYearDisplayLabel) {
|
|
126
|
+
overrides['FINANCIAL'] = {
|
|
127
|
+
name: financialYearDisplayLabel
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
if (weeklyDisplayLabel) {
|
|
131
|
+
overrides['WEEKLY'] = {
|
|
132
|
+
name: weeklyDisplayLabel
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
if (Object.keys(overrides).length === 0) {
|
|
136
|
+
return filteredRelativeOptions;
|
|
137
|
+
}
|
|
138
|
+
return filteredRelativeOptions.map(option => {
|
|
139
|
+
const override = overrides[option.id];
|
|
140
|
+
if (!override) {
|
|
141
|
+
return option;
|
|
142
|
+
}
|
|
143
|
+
return {
|
|
144
|
+
...option,
|
|
145
|
+
name: override.name,
|
|
146
|
+
getPeriods: () => applyPeriodNameOverrides(option.getPeriods(), metaData)
|
|
147
|
+
};
|
|
148
|
+
});
|
|
149
|
+
};
|
|
150
|
+
export const filterEnabledRelativePeriodTypes = (allRelativePeriodOptions, enabledServerPeriodTypes, financialYearStart = null) => {
|
|
151
|
+
if (!enabledServerPeriodTypes || enabledServerPeriodTypes.length === 0) {
|
|
152
|
+
return [];
|
|
153
|
+
}
|
|
154
|
+
const enabledServerPtNames = new Set(enabledServerPeriodTypes.map(pt => pt.name));
|
|
155
|
+
return allRelativePeriodOptions.filter(option => {
|
|
156
|
+
// Special handling for financial years
|
|
157
|
+
if (option.id === 'FINANCIAL') {
|
|
158
|
+
return financialYearStart !== null;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Check if any of the required FP dependencies are enabled
|
|
162
|
+
const requiredFpTypes = RP_CATEGORY_TO_FP_DEPENDENCIES[option.id];
|
|
163
|
+
if (!requiredFpTypes) {
|
|
164
|
+
return true; // Show if no dependency mapping (shouldn't happen)
|
|
165
|
+
}
|
|
166
|
+
return requiredFpTypes.some(fpType => enabledServerPtNames.has(fpType));
|
|
167
|
+
});
|
|
168
|
+
};
|