@dhis2/analytics 24.10.1 → 24.10.3

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/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ ## [24.10.3](https://github.com/dhis2/analytics/compare/v24.10.2...v24.10.3) (2024-03-20)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * show correct date and time in About AO Unit [DHIS2-15825, DHIS2-16365] [24.x] ([#1642](https://github.com/dhis2/analytics/issues/1642)) ([1c9068c](https://github.com/dhis2/analytics/commit/1c9068c9d153798ccbd61f6b916c1ee94ec122f8))
7
+
8
+ ## [24.10.2](https://github.com/dhis2/analytics/compare/v24.10.1...v24.10.2) (2023-10-25)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * avoid undefined in totals DHIS2-14511 v39 ([#1587](https://github.com/dhis2/analytics/issues/1587)) ([9831c17](https://github.com/dhis2/analytics/commit/9831c17f696f763b4ef6125fef9a9999c520f5aa))
14
+
1
15
  ## [24.10.1](https://github.com/dhis2/analytics/compare/v24.10.0...v24.10.1) (2023-04-24)
2
16
 
3
17
 
@@ -81,6 +81,9 @@ const AboutAOUnit = /*#__PURE__*/(0, _react.forwardRef)((_ref3, ref) => {
81
81
  renderId
82
82
  } = _ref3;
83
83
  const [isExpanded, setIsExpanded] = (0, _react.useState)(true);
84
+ const {
85
+ fromServerDate
86
+ } = (0, _appRuntime.useTimeZoneConversion)();
84
87
  const queries = (0, _react.useMemo)(() => getQueries(type), [type]);
85
88
  const {
86
89
  data,
@@ -194,16 +197,16 @@ const AboutAOUnit = /*#__PURE__*/(0, _react.forwardRef)((_ref3, ref) => {
194
197
  }, /*#__PURE__*/_react.default.createElement(_ui.IconClock16, {
195
198
  color: _ui.colors.grey700
196
199
  }), _d2I18n.default.t('Last updated {{time}}', {
197
- time: (0, _moment.default)(data.ao.lastUpdated).fromNow()
200
+ time: (0, _moment.default)(fromServerDate(data.ao.lastUpdated)).fromNow()
198
201
  })), /*#__PURE__*/_react.default.createElement("p", {
199
202
  className: "jsx-".concat(_AboutAOUnitStyle.default.__hash) + " " + "detailLine"
200
203
  }, /*#__PURE__*/_react.default.createElement(_ui.IconUser16, {
201
204
  color: _ui.colors.grey700
202
205
  }), (_data$ao$createdBy = data.ao.createdBy) !== null && _data$ao$createdBy !== void 0 && _data$ao$createdBy.displayName ? _d2I18n.default.t('Created {{time}} by {{author}}', {
203
- time: (0, _moment.default)(data.ao.created).fromNow(),
206
+ time: (0, _moment.default)(fromServerDate(data.ao.created)).fromNow(),
204
207
  author: data.ao.createdBy.displayName
205
208
  }) : _d2I18n.default.t('Created {{time}}', {
206
- time: (0, _moment.default)(data.ao.created).fromNow()
209
+ time: (0, _moment.default)(fromServerDate(data.ao.created)).fromNow()
207
210
  })), /*#__PURE__*/_react.default.createElement("p", {
208
211
  className: "jsx-".concat(_AboutAOUnitStyle.default.__hash) + " " + "detailLine"
209
212
  }, /*#__PURE__*/_react.default.createElement(_ui.IconView16, {
@@ -7,6 +7,8 @@ exports.InterpretationThread = void 0;
7
7
 
8
8
  var _style = _interopRequireDefault(require("styled-jsx/style"));
9
9
 
10
+ var _appRuntime = require("@dhis2/app-runtime");
11
+
10
12
  var _ui = require("@dhis2/ui");
11
13
 
12
14
  var _classnames = _interopRequireDefault(require("classnames"));
@@ -39,6 +41,9 @@ const InterpretationThread = _ref => {
39
41
  onThreadUpdated,
40
42
  downloadMenuComponent: DownloadMenu
41
43
  } = _ref;
44
+ const {
45
+ fromServerDate
46
+ } = (0, _appRuntime.useTimeZoneConversion)();
42
47
  const focusRef = (0, _react.useRef)();
43
48
  (0, _react.useEffect)(() => {
44
49
  if (initialFocus && focusRef.current) {
@@ -57,7 +62,7 @@ const InterpretationThread = _ref => {
57
62
  className: "jsx-615306698" + " " + 'title'
58
63
  }, /*#__PURE__*/_react.default.createElement(_ui.IconClock16, {
59
64
  color: _ui.colors.grey700
60
- }), (0, _moment.default)(interpretation.created).format('LLL')), DownloadMenu && /*#__PURE__*/_react.default.createElement(DownloadMenu, {
65
+ }), (0, _moment.default)(fromServerDate(interpretation.created)).format('LLL')), DownloadMenu && /*#__PURE__*/_react.default.createElement(DownloadMenu, {
61
66
  relativePeriodDate: interpretation.created,
62
67
  className: "jsx-615306698"
63
68
  }), /*#__PURE__*/_react.default.createElement("div", {
@@ -7,6 +7,8 @@ exports.InterpretationList = void 0;
7
7
 
8
8
  var _style = _interopRequireDefault(require("styled-jsx/style"));
9
9
 
10
+ var _appRuntime = require("@dhis2/app-runtime");
11
+
10
12
  var _ui = require("@dhis2/ui");
11
13
 
12
14
  var _moment = _interopRequireDefault(require("moment"));
@@ -43,6 +45,9 @@ const InterpretationList = _ref => {
43
45
  refresh,
44
46
  disabled
45
47
  } = _ref;
48
+ const {
49
+ fromServerDate
50
+ } = (0, _appRuntime.useTimeZoneConversion)();
46
51
  const interpretationsByDate = interpretations.reduce((groupedInterpretations, interpretation) => {
47
52
  const date = interpretation.created.split('T')[0];
48
53
 
@@ -67,7 +72,7 @@ const InterpretationList = _ref => {
67
72
  }), /*#__PURE__*/_react.default.createElement("time", {
68
73
  dateTime: date,
69
74
  className: _style.default.dynamic([["4058400613", [_ui.spacers.dp8, _ui.spacers.dp8, _ui.spacers.dp16, _ui.colors.grey800, _ui.spacers.dp12, _ui.spacers.dp12, _ui.spacers.dp32, _ui.spacers.dp4]]]) + " " + "date-header"
70
- }, (0, _moment.default)(date).format('ll'))), /*#__PURE__*/_react.default.createElement("ol", {
75
+ }, (0, _moment.default)(fromServerDate(date)).format('ll'))), /*#__PURE__*/_react.default.createElement("ol", {
71
76
  className: _style.default.dynamic([["4058400613", [_ui.spacers.dp8, _ui.spacers.dp8, _ui.spacers.dp16, _ui.colors.grey800, _ui.spacers.dp12, _ui.spacers.dp12, _ui.spacers.dp32, _ui.spacers.dp4]]]) + " " + "interpretation-list"
72
77
  }, interpretationsByDate[date].sort(sortByCreatedDateDesc).map(interpretation => /*#__PURE__*/_react.default.createElement(_index.Interpretation, {
73
78
  key: interpretation.id,
@@ -7,6 +7,8 @@ exports.Message = void 0;
7
7
 
8
8
  var _style = _interopRequireDefault(require("styled-jsx/style"));
9
9
 
10
+ var _appRuntime = require("@dhis2/app-runtime");
11
+
10
12
  var _d2UiRichText = require("@dhis2/d2-ui-rich-text");
11
13
 
12
14
  var _ui = require("@dhis2/ui");
@@ -26,24 +28,27 @@ const Message = _ref => {
26
28
  created,
27
29
  username
28
30
  } = _ref;
31
+ const {
32
+ fromServerDate
33
+ } = (0, _appRuntime.useTimeZoneConversion)();
29
34
  return /*#__PURE__*/_react.default.createElement("li", {
30
- className: _style.default.dynamic([["2436588813", [_ui.spacers.dp8, _ui.colors.grey100, _ui.spacers.dp8, _ui.colors.grey900, _ui.colors.grey600, _ui.colors.grey900, _ui.spacers.dp8]]]) + " " + "container"
35
+ className: _style.default.dynamic([["4031345705", [_ui.spacers.dp8, _ui.colors.grey100, _ui.spacers.dp8, _ui.colors.grey900, _ui.colors.grey600, _ui.colors.grey900, _ui.spacers.dp8]]]) + " " + "container"
31
36
  }, /*#__PURE__*/_react.default.createElement("div", {
32
- className: _style.default.dynamic([["2436588813", [_ui.spacers.dp8, _ui.colors.grey100, _ui.spacers.dp8, _ui.colors.grey900, _ui.colors.grey600, _ui.colors.grey900, _ui.spacers.dp8]]]) + " " + "header"
37
+ className: _style.default.dynamic([["4031345705", [_ui.spacers.dp8, _ui.colors.grey100, _ui.spacers.dp8, _ui.colors.grey900, _ui.colors.grey600, _ui.colors.grey900, _ui.spacers.dp8]]]) + " " + "header"
33
38
  }, /*#__PURE__*/_react.default.createElement(_ui.UserAvatar, {
34
39
  name: username,
35
40
  extrasmall: true
36
41
  }), username, /*#__PURE__*/_react.default.createElement("time", {
37
42
  dateTime: created,
38
- className: _style.default.dynamic([["2436588813", [_ui.spacers.dp8, _ui.colors.grey100, _ui.spacers.dp8, _ui.colors.grey900, _ui.colors.grey600, _ui.colors.grey900, _ui.spacers.dp8]]])
39
- }, (0, _moment.default)(created).format('lll'))), /*#__PURE__*/_react.default.createElement("div", {
40
- className: _style.default.dynamic([["2436588813", [_ui.spacers.dp8, _ui.colors.grey100, _ui.spacers.dp8, _ui.colors.grey900, _ui.colors.grey600, _ui.colors.grey900, _ui.spacers.dp8]]]) + " " + "content"
43
+ className: _style.default.dynamic([["4031345705", [_ui.spacers.dp8, _ui.colors.grey100, _ui.spacers.dp8, _ui.colors.grey900, _ui.colors.grey600, _ui.colors.grey900, _ui.spacers.dp8]]])
44
+ }, (0, _moment.default)(fromServerDate(created)).format('lll'))), /*#__PURE__*/_react.default.createElement("div", {
45
+ className: _style.default.dynamic([["4031345705", [_ui.spacers.dp8, _ui.colors.grey100, _ui.spacers.dp8, _ui.colors.grey900, _ui.colors.grey600, _ui.colors.grey900, _ui.spacers.dp8]]]) + " " + "content"
41
46
  }, /*#__PURE__*/_react.default.createElement(_d2UiRichText.Parser, null, text)), /*#__PURE__*/_react.default.createElement("div", {
42
- className: _style.default.dynamic([["2436588813", [_ui.spacers.dp8, _ui.colors.grey100, _ui.spacers.dp8, _ui.colors.grey900, _ui.colors.grey600, _ui.colors.grey900, _ui.spacers.dp8]]]) + " " + "footer"
47
+ className: _style.default.dynamic([["4031345705", [_ui.spacers.dp8, _ui.colors.grey100, _ui.spacers.dp8, _ui.colors.grey900, _ui.colors.grey600, _ui.colors.grey900, _ui.spacers.dp8]]]) + " " + "footer"
43
48
  }, children), /*#__PURE__*/_react.default.createElement(_style.default, {
44
- id: "2436588813",
49
+ id: "4031345705",
45
50
  dynamic: [_ui.spacers.dp8, _ui.colors.grey100, _ui.spacers.dp8, _ui.colors.grey900, _ui.colors.grey600, _ui.colors.grey900, _ui.spacers.dp8]
46
- }, [".container.__jsx-style-dynamic-selector{padding:".concat(_ui.spacers.dp8, ";background-color:").concat(_ui.colors.grey100, ";border-radius:5px;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;gap:").concat(_ui.spacers.dp8, ";}"), ".header.__jsx-style-dynamic-selector{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;gap:6px;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;font-size:13px;line-height:16px;color:".concat(_ui.colors.grey900, ";}"), ".header.__jsx-style-dynamic-selector time.__jsx-style-dynamic-selector{font-size:12px;color:".concat(_ui.colors.grey600, ";}"), ".content.__jsx-style-dynamic-selector{font-size:14px;line-height:19px;color:".concat(_ui.colors.grey900, ";}"), ".content.__jsx-style-dynamic-selector p:first-child{margin:0;}", ".footer.__jsx-style-dynamic-selector{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-align-items:flex-start;-webkit-box-align:flex-start;-ms-flex-align:flex-start;align-items:flex-start;gap:".concat(_ui.spacers.dp8, ";}")]));
51
+ }, [".container.__jsx-style-dynamic-selector{padding:".concat(_ui.spacers.dp8, ";background-color:").concat(_ui.colors.grey100, ";border-radius:5px;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;gap:").concat(_ui.spacers.dp8, ";}"), ".header.__jsx-style-dynamic-selector{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;gap:6px;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;font-size:13px;line-height:16px;color:".concat(_ui.colors.grey900, ";}"), ".header.__jsx-style-dynamic-selector time.__jsx-style-dynamic-selector{font-size:12px;color:".concat(_ui.colors.grey600, ";}"), ".content.__jsx-style-dynamic-selector{font-size:14px;line-height:19px;color:".concat(_ui.colors.grey900, ";word-break:break-word;white-space:pre-line;}"), ".content.__jsx-style-dynamic-selector p:first-child{margin:0;}", ".footer.__jsx-style-dynamic-selector{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-align-items:flex-start;-webkit-box-align:flex-start;-ms-flex-align:flex-start;align-items:flex-start;gap:".concat(_ui.spacers.dp8, ";}")]));
47
52
  };
48
53
 
49
54
  exports.Message = Message;
@@ -17,6 +17,8 @@ var _valueTypes = require("../valueTypes.js");
17
17
 
18
18
  var _AdaptiveClippingController = require("./AdaptiveClippingController.js");
19
19
 
20
+ var _addToTotalIfNumber = require("./addToTotalIfNumber.js");
21
+
20
22
  var _parseValue = require("./parseValue.js");
21
23
 
22
24
  var _pivotTableConstants = require("./pivotTableConstants.js");
@@ -626,10 +628,7 @@ class PivotTableEngine {
626
628
  dataFields.forEach(field => {
627
629
  const headerIndex = this.dimensionLookup.dataHeaders[field];
628
630
  const value = (0, _parseValue.parseValue)(dataRow[headerIndex]);
629
-
630
- if (value && !isNaN(value)) {
631
- totalCell[field] = (totalCell[field] || 0) + value;
632
- }
631
+ totalCell[field] = (0, _addToTotalIfNumber.addToTotalIfNumber)(value, totalCell[field]);
633
632
  });
634
633
  }
635
634
 
@@ -648,10 +647,7 @@ class PivotTableEngine {
648
647
  dataFields.forEach(field => {
649
648
  const headerIndex = this.dimensionLookup.dataHeaders[field];
650
649
  const value = (0, _parseValue.parseValue)(dataRow[headerIndex]);
651
-
652
- if (value && !isNaN(value)) {
653
- percentageTotal[field] = (percentageTotal[field] || 0) + value;
654
- }
650
+ percentageTotal[field] = (0, _addToTotalIfNumber.addToTotalIfNumber)(value, percentageTotal[field]);
655
651
  });
656
652
 
657
653
  if (totals.columnSubtotal) {
@@ -666,10 +662,7 @@ class PivotTableEngine {
666
662
  dataFields.forEach(field => {
667
663
  const headerIndex = this.dimensionLookup.dataHeaders[field];
668
664
  const value = (0, _parseValue.parseValue)(dataRow[headerIndex]);
669
-
670
- if (value && !isNaN(value)) {
671
- percentageTotal[field] = (percentageTotal[field] || 0) + value;
672
- }
665
+ percentageTotal[field] = (0, _addToTotalIfNumber.addToTotalIfNumber)(value, percentageTotal[field]);
673
666
  });
674
667
  }
675
668
 
@@ -685,10 +678,7 @@ class PivotTableEngine {
685
678
  dataFields.forEach(field => {
686
679
  const headerIndex = this.dimensionLookup.dataHeaders[field];
687
680
  const value = (0, _parseValue.parseValue)(dataRow[headerIndex]);
688
-
689
- if (value && !isNaN(value)) {
690
- percentageTotal[field] = (percentageTotal[field] || 0) + value;
691
- }
681
+ percentageTotal[field] = (0, _addToTotalIfNumber.addToTotalIfNumber)(value, percentageTotal[field]);
692
682
  });
693
683
  }
694
684
  }
@@ -705,10 +695,7 @@ class PivotTableEngine {
705
695
  dataFields.forEach(field => {
706
696
  const headerIndex = this.dimensionLookup.dataHeaders[field];
707
697
  const value = (0, _parseValue.parseValue)(dataRow[headerIndex]);
708
-
709
- if (value && !isNaN(value)) {
710
- percentageTotal[field] = (percentageTotal[field] || 0) + value;
711
- }
698
+ percentageTotal[field] = (0, _addToTotalIfNumber.addToTotalIfNumber)(value, percentageTotal[field]);
712
699
  });
713
700
 
714
701
  if (totals.rowSubtotal) {
@@ -723,10 +710,7 @@ class PivotTableEngine {
723
710
  dataFields.forEach(field => {
724
711
  const headerIndex = this.dimensionLookup.dataHeaders[field];
725
712
  const value = (0, _parseValue.parseValue)(dataRow[headerIndex]);
726
-
727
- if (value && !isNaN(value)) {
728
- percentageTotal[field] = (percentageTotal[field] || 0) + value;
729
- }
713
+ percentageTotal[field] = (0, _addToTotalIfNumber.addToTotalIfNumber)(value, percentageTotal[field]);
730
714
  });
731
715
  }
732
716
 
@@ -742,10 +726,7 @@ class PivotTableEngine {
742
726
  dataFields.forEach(field => {
743
727
  const headerIndex = this.dimensionLookup.dataHeaders[field];
744
728
  const value = (0, _parseValue.parseValue)(dataRow[headerIndex]);
745
-
746
- if (value && !isNaN(value)) {
747
- percentageTotal[field] = (percentageTotal[field] || 0) + value;
748
- }
729
+ percentageTotal[field] = (0, _addToTotalIfNumber.addToTotalIfNumber)(value, percentageTotal[field]);
749
730
  });
750
731
  }
751
732
  }
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+
3
+ var _addToTotalIfNumber = require("../addToTotalIfNumber.js");
4
+
5
+ const tests = [{
6
+ testName: 'negative value',
7
+ value: -1,
8
+ total: undefined,
9
+ expected: -1
10
+ }, {
11
+ testName: 'zero value',
12
+ value: 0,
13
+ total: undefined,
14
+ expected: 0
15
+ }, {
16
+ testName: 'positive value',
17
+ value: 1,
18
+ total: undefined,
19
+ expected: 1
20
+ }, {
21
+ testName: 'null value',
22
+ value: null,
23
+ total: undefined,
24
+ expected: undefined
25
+ }, {
26
+ testName: 'undefined value',
27
+ value: undefined,
28
+ total: undefined,
29
+ expected: undefined
30
+ }, {
31
+ testName: 'string value',
32
+ value: 'string',
33
+ total: undefined,
34
+ expected: undefined
35
+ }, {
36
+ testName: 'negative value with existing total',
37
+ value: -1,
38
+ total: 100,
39
+ expected: 99
40
+ }, {
41
+ testName: 'zero value with existing total',
42
+ value: 0,
43
+ total: 100,
44
+ expected: 100
45
+ }, {
46
+ testName: 'positive value with existing total',
47
+ value: 1,
48
+ total: 100,
49
+ expected: 101
50
+ }, {
51
+ testName: 'null value with existing total',
52
+ value: null,
53
+ total: 100,
54
+ expected: 100
55
+ }, {
56
+ testName: 'undefined value with existing total',
57
+ value: undefined,
58
+ total: 100,
59
+ expected: 100
60
+ }, {
61
+ testName: 'string value with existing total',
62
+ value: 'string',
63
+ total: 100,
64
+ expected: 100
65
+ }];
66
+ describe('addToTotalIfNumber', () => {
67
+ tests.forEach(t => {
68
+ it(t.testName, () => {
69
+ expect((0, _addToTotalIfNumber.addToTotalIfNumber)(t.value, t.total)).toEqual(t.expected);
70
+ });
71
+ });
72
+ });
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.addToTotalIfNumber = void 0;
7
+
8
+ const addToTotalIfNumber = (value, total) => typeof value === 'number' && Number.isFinite(value) ? (total !== null && total !== void 0 ? total : 0) + value : total;
9
+
10
+ exports.addToTotalIfNumber = addToTotalIfNumber;
@@ -1,5 +1,5 @@
1
1
  import _JSXStyle from "styled-jsx/style";
2
- import { useDataQuery, useDataMutation } from '@dhis2/app-runtime';
2
+ import { useDataQuery, useDataMutation, useTimeZoneConversion } from '@dhis2/app-runtime';
3
3
  import i18n from '@dhis2/d2-i18n';
4
4
  import { Parser as RichTextParser } from '@dhis2/d2-ui-rich-text';
5
5
  import { Button, CircularLoader, IconChevronDown24, IconChevronUp24, IconClock16, IconShare16, IconSubscribe24, IconSubscribeOff24, IconUser16, IconView16, colors } from '@dhis2/ui';
@@ -56,6 +56,9 @@ const AboutAOUnit = /*#__PURE__*/forwardRef((_ref3, ref) => {
56
56
  renderId
57
57
  } = _ref3;
58
58
  const [isExpanded, setIsExpanded] = useState(true);
59
+ const {
60
+ fromServerDate
61
+ } = useTimeZoneConversion();
59
62
  const queries = useMemo(() => getQueries(type), [type]);
60
63
  const {
61
64
  data,
@@ -169,16 +172,16 @@ const AboutAOUnit = /*#__PURE__*/forwardRef((_ref3, ref) => {
169
172
  }, /*#__PURE__*/React.createElement(IconClock16, {
170
173
  color: colors.grey700
171
174
  }), i18n.t('Last updated {{time}}', {
172
- time: moment(data.ao.lastUpdated).fromNow()
175
+ time: moment(fromServerDate(data.ao.lastUpdated)).fromNow()
173
176
  })), /*#__PURE__*/React.createElement("p", {
174
177
  className: "jsx-".concat(styles.__hash) + " " + "detailLine"
175
178
  }, /*#__PURE__*/React.createElement(IconUser16, {
176
179
  color: colors.grey700
177
180
  }), (_data$ao$createdBy = data.ao.createdBy) !== null && _data$ao$createdBy !== void 0 && _data$ao$createdBy.displayName ? i18n.t('Created {{time}} by {{author}}', {
178
- time: moment(data.ao.created).fromNow(),
181
+ time: moment(fromServerDate(data.ao.created)).fromNow(),
179
182
  author: data.ao.createdBy.displayName
180
183
  }) : i18n.t('Created {{time}}', {
181
- time: moment(data.ao.created).fromNow()
184
+ time: moment(fromServerDate(data.ao.created)).fromNow()
182
185
  })), /*#__PURE__*/React.createElement("p", {
183
186
  className: "jsx-".concat(styles.__hash) + " " + "detailLine"
184
187
  }, /*#__PURE__*/React.createElement(IconView16, {
@@ -1,4 +1,5 @@
1
1
  import _JSXStyle from "styled-jsx/style";
2
+ import { useTimeZoneConversion } from '@dhis2/app-runtime';
2
3
  import { IconClock16, colors } from '@dhis2/ui';
3
4
  import cx from 'classnames';
4
5
  import moment from 'moment';
@@ -18,6 +19,9 @@ const InterpretationThread = _ref => {
18
19
  onThreadUpdated,
19
20
  downloadMenuComponent: DownloadMenu
20
21
  } = _ref;
22
+ const {
23
+ fromServerDate
24
+ } = useTimeZoneConversion();
21
25
  const focusRef = useRef();
22
26
  useEffect(() => {
23
27
  if (initialFocus && focusRef.current) {
@@ -36,7 +40,7 @@ const InterpretationThread = _ref => {
36
40
  className: "jsx-615306698" + " " + 'title'
37
41
  }, /*#__PURE__*/React.createElement(IconClock16, {
38
42
  color: colors.grey700
39
- }), moment(interpretation.created).format('LLL')), DownloadMenu && /*#__PURE__*/React.createElement(DownloadMenu, {
43
+ }), moment(fromServerDate(interpretation.created)).format('LLL')), DownloadMenu && /*#__PURE__*/React.createElement(DownloadMenu, {
40
44
  relativePeriodDate: interpretation.created,
41
45
  className: "jsx-615306698"
42
46
  }), /*#__PURE__*/React.createElement("div", {
@@ -1,4 +1,5 @@
1
1
  import _JSXStyle from "styled-jsx/style";
2
+ import { useTimeZoneConversion } from '@dhis2/app-runtime';
2
3
  import { IconCalendar24, colors, spacers } from '@dhis2/ui';
3
4
  import moment from 'moment';
4
5
  import PropTypes from 'prop-types';
@@ -29,6 +30,9 @@ export const InterpretationList = _ref => {
29
30
  refresh,
30
31
  disabled
31
32
  } = _ref;
33
+ const {
34
+ fromServerDate
35
+ } = useTimeZoneConversion();
32
36
  const interpretationsByDate = interpretations.reduce((groupedInterpretations, interpretation) => {
33
37
  const date = interpretation.created.split('T')[0];
34
38
 
@@ -53,7 +57,7 @@ export const InterpretationList = _ref => {
53
57
  }), /*#__PURE__*/React.createElement("time", {
54
58
  dateTime: date,
55
59
  className: _JSXStyle.dynamic([["4058400613", [spacers.dp8, spacers.dp8, spacers.dp16, colors.grey800, spacers.dp12, spacers.dp12, spacers.dp32, spacers.dp4]]]) + " " + "date-header"
56
- }, moment(date).format('ll'))), /*#__PURE__*/React.createElement("ol", {
60
+ }, moment(fromServerDate(date)).format('ll'))), /*#__PURE__*/React.createElement("ol", {
57
61
  className: _JSXStyle.dynamic([["4058400613", [spacers.dp8, spacers.dp8, spacers.dp16, colors.grey800, spacers.dp12, spacers.dp12, spacers.dp32, spacers.dp4]]]) + " " + "interpretation-list"
58
62
  }, interpretationsByDate[date].sort(sortByCreatedDateDesc).map(interpretation => /*#__PURE__*/React.createElement(Interpretation, {
59
63
  key: interpretation.id,
@@ -1,4 +1,5 @@
1
1
  import _JSXStyle from "styled-jsx/style";
2
+ import { useTimeZoneConversion } from '@dhis2/app-runtime';
2
3
  import { Parser as RichTextParser } from '@dhis2/d2-ui-rich-text';
3
4
  import { UserAvatar, spacers, colors } from '@dhis2/ui';
4
5
  import moment from 'moment';
@@ -12,24 +13,27 @@ const Message = _ref => {
12
13
  created,
13
14
  username
14
15
  } = _ref;
16
+ const {
17
+ fromServerDate
18
+ } = useTimeZoneConversion();
15
19
  return /*#__PURE__*/React.createElement("li", {
16
- className: _JSXStyle.dynamic([["2436588813", [spacers.dp8, colors.grey100, spacers.dp8, colors.grey900, colors.grey600, colors.grey900, spacers.dp8]]]) + " " + "container"
20
+ className: _JSXStyle.dynamic([["4031345705", [spacers.dp8, colors.grey100, spacers.dp8, colors.grey900, colors.grey600, colors.grey900, spacers.dp8]]]) + " " + "container"
17
21
  }, /*#__PURE__*/React.createElement("div", {
18
- className: _JSXStyle.dynamic([["2436588813", [spacers.dp8, colors.grey100, spacers.dp8, colors.grey900, colors.grey600, colors.grey900, spacers.dp8]]]) + " " + "header"
22
+ className: _JSXStyle.dynamic([["4031345705", [spacers.dp8, colors.grey100, spacers.dp8, colors.grey900, colors.grey600, colors.grey900, spacers.dp8]]]) + " " + "header"
19
23
  }, /*#__PURE__*/React.createElement(UserAvatar, {
20
24
  name: username,
21
25
  extrasmall: true
22
26
  }), username, /*#__PURE__*/React.createElement("time", {
23
27
  dateTime: created,
24
- className: _JSXStyle.dynamic([["2436588813", [spacers.dp8, colors.grey100, spacers.dp8, colors.grey900, colors.grey600, colors.grey900, spacers.dp8]]])
25
- }, moment(created).format('lll'))), /*#__PURE__*/React.createElement("div", {
26
- className: _JSXStyle.dynamic([["2436588813", [spacers.dp8, colors.grey100, spacers.dp8, colors.grey900, colors.grey600, colors.grey900, spacers.dp8]]]) + " " + "content"
28
+ className: _JSXStyle.dynamic([["4031345705", [spacers.dp8, colors.grey100, spacers.dp8, colors.grey900, colors.grey600, colors.grey900, spacers.dp8]]])
29
+ }, moment(fromServerDate(created)).format('lll'))), /*#__PURE__*/React.createElement("div", {
30
+ className: _JSXStyle.dynamic([["4031345705", [spacers.dp8, colors.grey100, spacers.dp8, colors.grey900, colors.grey600, colors.grey900, spacers.dp8]]]) + " " + "content"
27
31
  }, /*#__PURE__*/React.createElement(RichTextParser, null, text)), /*#__PURE__*/React.createElement("div", {
28
- className: _JSXStyle.dynamic([["2436588813", [spacers.dp8, colors.grey100, spacers.dp8, colors.grey900, colors.grey600, colors.grey900, spacers.dp8]]]) + " " + "footer"
32
+ className: _JSXStyle.dynamic([["4031345705", [spacers.dp8, colors.grey100, spacers.dp8, colors.grey900, colors.grey600, colors.grey900, spacers.dp8]]]) + " " + "footer"
29
33
  }, children), /*#__PURE__*/React.createElement(_JSXStyle, {
30
- id: "2436588813",
34
+ id: "4031345705",
31
35
  dynamic: [spacers.dp8, colors.grey100, spacers.dp8, colors.grey900, colors.grey600, colors.grey900, spacers.dp8]
32
- }, [".container.__jsx-style-dynamic-selector{padding:".concat(spacers.dp8, ";background-color:").concat(colors.grey100, ";border-radius:5px;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;gap:").concat(spacers.dp8, ";}"), ".header.__jsx-style-dynamic-selector{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;gap:6px;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;font-size:13px;line-height:16px;color:".concat(colors.grey900, ";}"), ".header.__jsx-style-dynamic-selector time.__jsx-style-dynamic-selector{font-size:12px;color:".concat(colors.grey600, ";}"), ".content.__jsx-style-dynamic-selector{font-size:14px;line-height:19px;color:".concat(colors.grey900, ";}"), ".content.__jsx-style-dynamic-selector p:first-child{margin:0;}", ".footer.__jsx-style-dynamic-selector{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-align-items:flex-start;-webkit-box-align:flex-start;-ms-flex-align:flex-start;align-items:flex-start;gap:".concat(spacers.dp8, ";}")]));
36
+ }, [".container.__jsx-style-dynamic-selector{padding:".concat(spacers.dp8, ";background-color:").concat(colors.grey100, ";border-radius:5px;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;gap:").concat(spacers.dp8, ";}"), ".header.__jsx-style-dynamic-selector{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;gap:6px;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;font-size:13px;line-height:16px;color:".concat(colors.grey900, ";}"), ".header.__jsx-style-dynamic-selector time.__jsx-style-dynamic-selector{font-size:12px;color:".concat(colors.grey600, ";}"), ".content.__jsx-style-dynamic-selector{font-size:14px;line-height:19px;color:".concat(colors.grey900, ";word-break:break-word;white-space:pre-line;}"), ".content.__jsx-style-dynamic-selector p:first-child{margin:0;}", ".footer.__jsx-style-dynamic-selector{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-align-items:flex-start;-webkit-box-align:flex-start;-ms-flex-align:flex-start;align-items:flex-start;gap:".concat(spacers.dp8, ";}")]));
33
37
  };
34
38
 
35
39
  Message.propTypes = {
@@ -6,6 +6,7 @@ import { DIMENSION_ID_ORGUNIT } from '../predefinedDimensions.js';
6
6
  import { renderValue } from '../renderValue.js';
7
7
  import { VALUE_TYPE_NUMBER, VALUE_TYPE_TEXT } from '../valueTypes.js';
8
8
  import { AdaptiveClippingController } from './AdaptiveClippingController.js';
9
+ import { addToTotalIfNumber } from './addToTotalIfNumber.js';
9
10
  import { parseValue } from './parseValue.js';
10
11
  import { AGGREGATE_TYPE_NA, AGGREGATE_TYPE_AVERAGE, AGGREGATE_TYPE_SUM, CELL_TYPE_VALUE, CELL_TYPE_TOTAL, CELL_TYPE_SUBTOTAL, SORT_ORDER_ASCENDING, SORT_ORDER_DESCENDING, DISPLAY_DENSITY_PADDING_COMPACT, DISPLAY_DENSITY_PADDING_COMFORTABLE, DISPLAY_DENSITY_OPTION_COMFORTABLE, DISPLAY_DENSITY_OPTION_COMPACT, DISPLAY_DENSITY_OPTION_NORMAL, DISPLAY_DENSITY_PADDING_NORMAL, FONT_SIZE_OPTION_SMALL, FONT_SIZE_SMALL, FONT_SIZE_OPTION_LARGE, FONT_SIZE_LARGE, FONT_SIZE_OPTION_NORMAL, FONT_SIZE_NORMAL, NUMBER_TYPE_COLUMN_PERCENTAGE, NUMBER_TYPE_ROW_PERCENTAGE, NUMBER_TYPE_VALUE } from './pivotTableConstants.js';
11
12
  const dataFields = ['value', 'numerator', 'denominator', 'factor', 'multiplier', 'divisor'];
@@ -609,10 +610,7 @@ export class PivotTableEngine {
609
610
  dataFields.forEach(field => {
610
611
  const headerIndex = this.dimensionLookup.dataHeaders[field];
611
612
  const value = parseValue(dataRow[headerIndex]);
612
-
613
- if (value && !isNaN(value)) {
614
- totalCell[field] = (totalCell[field] || 0) + value;
615
- }
613
+ totalCell[field] = addToTotalIfNumber(value, totalCell[field]);
616
614
  });
617
615
  }
618
616
 
@@ -631,10 +629,7 @@ export class PivotTableEngine {
631
629
  dataFields.forEach(field => {
632
630
  const headerIndex = this.dimensionLookup.dataHeaders[field];
633
631
  const value = parseValue(dataRow[headerIndex]);
634
-
635
- if (value && !isNaN(value)) {
636
- percentageTotal[field] = (percentageTotal[field] || 0) + value;
637
- }
632
+ percentageTotal[field] = addToTotalIfNumber(value, percentageTotal[field]);
638
633
  });
639
634
 
640
635
  if (totals.columnSubtotal) {
@@ -649,10 +644,7 @@ export class PivotTableEngine {
649
644
  dataFields.forEach(field => {
650
645
  const headerIndex = this.dimensionLookup.dataHeaders[field];
651
646
  const value = parseValue(dataRow[headerIndex]);
652
-
653
- if (value && !isNaN(value)) {
654
- percentageTotal[field] = (percentageTotal[field] || 0) + value;
655
- }
647
+ percentageTotal[field] = addToTotalIfNumber(value, percentageTotal[field]);
656
648
  });
657
649
  }
658
650
 
@@ -668,10 +660,7 @@ export class PivotTableEngine {
668
660
  dataFields.forEach(field => {
669
661
  const headerIndex = this.dimensionLookup.dataHeaders[field];
670
662
  const value = parseValue(dataRow[headerIndex]);
671
-
672
- if (value && !isNaN(value)) {
673
- percentageTotal[field] = (percentageTotal[field] || 0) + value;
674
- }
663
+ percentageTotal[field] = addToTotalIfNumber(value, percentageTotal[field]);
675
664
  });
676
665
  }
677
666
  }
@@ -688,10 +677,7 @@ export class PivotTableEngine {
688
677
  dataFields.forEach(field => {
689
678
  const headerIndex = this.dimensionLookup.dataHeaders[field];
690
679
  const value = parseValue(dataRow[headerIndex]);
691
-
692
- if (value && !isNaN(value)) {
693
- percentageTotal[field] = (percentageTotal[field] || 0) + value;
694
- }
680
+ percentageTotal[field] = addToTotalIfNumber(value, percentageTotal[field]);
695
681
  });
696
682
 
697
683
  if (totals.rowSubtotal) {
@@ -706,10 +692,7 @@ export class PivotTableEngine {
706
692
  dataFields.forEach(field => {
707
693
  const headerIndex = this.dimensionLookup.dataHeaders[field];
708
694
  const value = parseValue(dataRow[headerIndex]);
709
-
710
- if (value && !isNaN(value)) {
711
- percentageTotal[field] = (percentageTotal[field] || 0) + value;
712
- }
695
+ percentageTotal[field] = addToTotalIfNumber(value, percentageTotal[field]);
713
696
  });
714
697
  }
715
698
 
@@ -725,10 +708,7 @@ export class PivotTableEngine {
725
708
  dataFields.forEach(field => {
726
709
  const headerIndex = this.dimensionLookup.dataHeaders[field];
727
710
  const value = parseValue(dataRow[headerIndex]);
728
-
729
- if (value && !isNaN(value)) {
730
- percentageTotal[field] = (percentageTotal[field] || 0) + value;
731
- }
711
+ percentageTotal[field] = addToTotalIfNumber(value, percentageTotal[field]);
732
712
  });
733
713
  }
734
714
  }
@@ -0,0 +1,69 @@
1
+ import { addToTotalIfNumber } from '../addToTotalIfNumber.js';
2
+ const tests = [{
3
+ testName: 'negative value',
4
+ value: -1,
5
+ total: undefined,
6
+ expected: -1
7
+ }, {
8
+ testName: 'zero value',
9
+ value: 0,
10
+ total: undefined,
11
+ expected: 0
12
+ }, {
13
+ testName: 'positive value',
14
+ value: 1,
15
+ total: undefined,
16
+ expected: 1
17
+ }, {
18
+ testName: 'null value',
19
+ value: null,
20
+ total: undefined,
21
+ expected: undefined
22
+ }, {
23
+ testName: 'undefined value',
24
+ value: undefined,
25
+ total: undefined,
26
+ expected: undefined
27
+ }, {
28
+ testName: 'string value',
29
+ value: 'string',
30
+ total: undefined,
31
+ expected: undefined
32
+ }, {
33
+ testName: 'negative value with existing total',
34
+ value: -1,
35
+ total: 100,
36
+ expected: 99
37
+ }, {
38
+ testName: 'zero value with existing total',
39
+ value: 0,
40
+ total: 100,
41
+ expected: 100
42
+ }, {
43
+ testName: 'positive value with existing total',
44
+ value: 1,
45
+ total: 100,
46
+ expected: 101
47
+ }, {
48
+ testName: 'null value with existing total',
49
+ value: null,
50
+ total: 100,
51
+ expected: 100
52
+ }, {
53
+ testName: 'undefined value with existing total',
54
+ value: undefined,
55
+ total: 100,
56
+ expected: 100
57
+ }, {
58
+ testName: 'string value with existing total',
59
+ value: 'string',
60
+ total: 100,
61
+ expected: 100
62
+ }];
63
+ describe('addToTotalIfNumber', () => {
64
+ tests.forEach(t => {
65
+ it(t.testName, () => {
66
+ expect(addToTotalIfNumber(t.value, t.total)).toEqual(t.expected);
67
+ });
68
+ });
69
+ });
@@ -0,0 +1 @@
1
+ export const addToTotalIfNumber = (value, total) => typeof value === 'number' && Number.isFinite(value) ? (total !== null && total !== void 0 ? total : 0) + value : total;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dhis2/analytics",
3
- "version": "24.10.1",
3
+ "version": "24.10.3",
4
4
  "main": "./build/cjs/index.js",
5
5
  "module": "./build/es/index.js",
6
6
  "exports": {