@dhis2/analytics 29.2.0 → 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.
Files changed (33) hide show
  1. package/build/cjs/components/PeriodDimension/FixedPeriodFilter.js +5 -10
  2. package/build/cjs/components/PeriodDimension/FixedPeriodSelect.js +3 -1
  3. package/build/cjs/components/PeriodDimension/PeriodDimension.js +15 -5
  4. package/build/cjs/components/PeriodDimension/PeriodTransfer.js +110 -14
  5. package/build/cjs/components/PeriodDimension/RelativePeriodFilter.js +4 -6
  6. package/build/cjs/components/PeriodDimension/__tests__/PeriodDimension.spec.js +19 -6
  7. package/build/cjs/components/PeriodDimension/__tests__/fixedPeriods.spec.js +1 -1
  8. package/build/cjs/components/PeriodDimension/__tests__/utils.spec.js +1 -1
  9. package/build/cjs/components/PeriodDimension/useDataOutputPeriodTypes.js +153 -0
  10. package/build/cjs/components/PeriodDimension/utils/enabledPeriodTypes.js +179 -0
  11. package/build/cjs/components/PeriodDimension/utils/fixedPeriods.js +24 -42
  12. package/build/cjs/components/PeriodDimension/utils/index.js +7 -4
  13. package/build/cjs/index.js +8 -0
  14. package/build/cjs/locales/en/translations.json +8 -3
  15. package/build/cjs/locales/fr/translations.json +20 -20
  16. package/build/cjs/visualizations/util/getFilterText.js +1 -1
  17. package/build/es/components/PeriodDimension/FixedPeriodFilter.js +5 -10
  18. package/build/es/components/PeriodDimension/FixedPeriodSelect.js +4 -2
  19. package/build/es/components/PeriodDimension/PeriodDimension.js +15 -5
  20. package/build/es/components/PeriodDimension/PeriodTransfer.js +115 -19
  21. package/build/es/components/PeriodDimension/RelativePeriodFilter.js +4 -6
  22. package/build/es/components/PeriodDimension/__tests__/PeriodDimension.spec.js +19 -6
  23. package/build/es/components/PeriodDimension/__tests__/fixedPeriods.spec.js +1 -1
  24. package/build/es/components/PeriodDimension/__tests__/utils.spec.js +1 -1
  25. package/build/es/components/PeriodDimension/useDataOutputPeriodTypes.js +147 -0
  26. package/build/es/components/PeriodDimension/utils/enabledPeriodTypes.js +168 -0
  27. package/build/es/components/PeriodDimension/utils/fixedPeriods.js +25 -43
  28. package/build/es/components/PeriodDimension/utils/index.js +6 -3
  29. package/build/es/index.js +1 -0
  30. package/build/es/locales/en/translations.json +8 -3
  31. package/build/es/locales/fr/translations.json +20 -20
  32. package/build/es/visualizations/util/getFilterText.js +1 -1
  33. 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 { getFixedPeriodsOptionsById } from './utils/fixedPeriods.js';
15
- import { MONTHLY, QUARTERLY } from './utils/index.js';
16
- import { getRelativePeriodsOptionsById } from './utils/relativePeriods.js';
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 defaultRelativePeriodType = excludedPeriodTypes.includes(MONTHLY) ? getRelativePeriodsOptionsById(QUARTERLY) : getRelativePeriodsOptionsById(MONTHLY);
50
- const defaultFixedPeriodType = excludedPeriodTypes.includes(MONTHLY) ? getFixedPeriodsOptionsById(QUARTERLY, periodsSettings) : getFixedPeriodsOptionsById(MONTHLY, periodsSettings);
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 [allPeriods, setAllPeriods] = useState(defaultRelativePeriodType.getPeriods());
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
- setAllPeriods(state ? getRelativePeriodsOptionsById(relativeFilter.periodType).getPeriods() : getFixedPeriodsOptionsById(fixedFilter.periodType, periodsSettings).getPeriods(fixedPeriodConfig(Number(fixedFilter.year))));
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: relativeFilter.periodType,
176
+ currentFilter: effectiveRelativeFilterType,
91
177
  onSelectFilter: filter => {
92
178
  setRelativeFilter({
93
179
  periodType: filter
94
180
  });
95
- setAllPeriods(getRelativePeriodsOptionsById(filter).getPeriods());
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
- excludedPeriodTypes: excludedPeriodTypes
185
+ availableOptions: filteredRelativeOptions
99
186
  }) : /*#__PURE__*/React.createElement(FixedPeriodFilter, {
100
- currentPeriodType: fixedFilter.periodType,
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
- excludedPeriodTypes: excludedPeriodTypes
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
- setAllPeriods(getFixedPeriodsOptionsById(filter.periodType, periodsSettings).getPeriods(fixedPeriodConfig(Number(filter.year)), periodsSettings));
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
- excludedPeriodTypes
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
- }, filterPeriodTypesById(getRelativePeriodsOptions(), excludedPeriodTypes).map(option => /*#__PURE__*/React.createElement(SingleSelectOption, {
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
- data: {
11
- userSettings: {
12
- keyUiLocale: 'en'
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', 'FYNOV', 'FYOCT', 'FYJUL', 'FYAPR']);
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', 'FYNOV', 'FYJUL', 'FYAPR']);
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
+ };