@dhis2/analytics 28.1.2 → 29.0.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 (59) hide show
  1. package/build/cjs/__demo__/InterpretationsUnit.stories.js +9 -6
  2. package/build/cjs/__fixtures__/interpretationsMockData.js +204 -0
  3. package/build/cjs/components/Interpretations/DashboardItemInterpretations/DashboardInterpretationThread.js +56 -0
  4. package/build/cjs/components/Interpretations/DashboardItemInterpretations/DashboardItemInterpretations.js +54 -0
  5. package/build/cjs/components/Interpretations/DashboardItemInterpretations/index.js +12 -0
  6. package/build/cjs/components/Interpretations/InterpretationModal/Comment.js +12 -17
  7. package/build/cjs/components/Interpretations/InterpretationModal/CommentAddForm.js +20 -34
  8. package/build/cjs/components/Interpretations/InterpretationModal/CommentDeleteButton.js +11 -36
  9. package/build/cjs/components/Interpretations/InterpretationModal/CommentUpdateForm.js +11 -27
  10. package/build/cjs/components/Interpretations/InterpretationModal/InterpretationModal.js +11 -68
  11. package/build/cjs/components/Interpretations/InterpretationModal/InterpretationThread.js +11 -24
  12. package/build/cjs/components/Interpretations/InterpretationsProvider/InterpretationsManager.js +275 -0
  13. package/build/cjs/components/Interpretations/InterpretationsProvider/InterpretationsProvider.js +28 -0
  14. package/build/cjs/components/Interpretations/InterpretationsProvider/__tests__/groupInterpretationIdsByDate.spec.js +37 -0
  15. package/build/cjs/components/Interpretations/InterpretationsProvider/__tests__/hooks.spec.js +565 -0
  16. package/build/cjs/components/Interpretations/InterpretationsProvider/groupInterpretationIdsByDate.js +16 -0
  17. package/build/cjs/components/Interpretations/InterpretationsProvider/hooks.js +278 -0
  18. package/build/cjs/components/Interpretations/InterpretationsProvider/index.js +12 -0
  19. package/build/cjs/components/Interpretations/InterpretationsUnit/InterpretationForm.js +25 -30
  20. package/build/cjs/components/Interpretations/InterpretationsUnit/InterpretationList.js +8 -38
  21. package/build/cjs/components/Interpretations/InterpretationsUnit/InterpretationsUnit.js +22 -73
  22. package/build/cjs/components/Interpretations/common/Interpretation/Interpretation.js +20 -34
  23. package/build/cjs/components/Interpretations/common/Interpretation/InterpretationDeleteButton.js +10 -12
  24. package/build/cjs/components/Interpretations/common/Interpretation/InterpretationUpdateForm.js +13 -24
  25. package/build/cjs/components/Interpretations/common/Message/MessageEditorContainer.js +3 -3
  26. package/build/cjs/index.js +72 -63
  27. package/build/cjs/locales/en/translations.json +10 -1
  28. package/build/cjs/modules/pivotTable/getHeaderForDisplay.js +1 -1
  29. package/build/es/__demo__/InterpretationsUnit.stories.js +9 -6
  30. package/build/es/__fixtures__/interpretationsMockData.js +198 -0
  31. package/build/es/components/Interpretations/DashboardItemInterpretations/DashboardInterpretationThread.js +48 -0
  32. package/build/es/components/Interpretations/DashboardItemInterpretations/DashboardItemInterpretations.js +45 -0
  33. package/build/es/components/Interpretations/DashboardItemInterpretations/index.js +1 -0
  34. package/build/es/components/Interpretations/InterpretationModal/Comment.js +14 -19
  35. package/build/es/components/Interpretations/InterpretationModal/CommentAddForm.js +21 -35
  36. package/build/es/components/Interpretations/InterpretationModal/CommentDeleteButton.js +11 -35
  37. package/build/es/components/Interpretations/InterpretationModal/CommentUpdateForm.js +12 -28
  38. package/build/es/components/Interpretations/InterpretationModal/InterpretationModal.js +12 -69
  39. package/build/es/components/Interpretations/InterpretationModal/InterpretationThread.js +11 -24
  40. package/build/es/components/Interpretations/InterpretationsProvider/InterpretationsManager.js +268 -0
  41. package/build/es/components/Interpretations/InterpretationsProvider/InterpretationsProvider.js +19 -0
  42. package/build/es/components/Interpretations/InterpretationsProvider/__tests__/groupInterpretationIdsByDate.spec.js +35 -0
  43. package/build/es/components/Interpretations/InterpretationsProvider/__tests__/hooks.spec.js +561 -0
  44. package/build/es/components/Interpretations/InterpretationsProvider/groupInterpretationIdsByDate.js +9 -0
  45. package/build/es/components/Interpretations/InterpretationsProvider/hooks.js +258 -0
  46. package/build/es/components/Interpretations/InterpretationsProvider/index.js +1 -0
  47. package/build/es/components/Interpretations/InterpretationsUnit/InterpretationForm.js +26 -31
  48. package/build/es/components/Interpretations/InterpretationsUnit/InterpretationList.js +8 -38
  49. package/build/es/components/Interpretations/InterpretationsUnit/InterpretationsUnit.js +23 -75
  50. package/build/es/components/Interpretations/common/Interpretation/Interpretation.js +21 -35
  51. package/build/es/components/Interpretations/common/Interpretation/InterpretationDeleteButton.js +11 -13
  52. package/build/es/components/Interpretations/common/Interpretation/InterpretationUpdateForm.js +14 -25
  53. package/build/es/components/Interpretations/common/Message/MessageEditorContainer.js +3 -3
  54. package/build/es/index.js +3 -1
  55. package/build/es/locales/en/translations.json +10 -1
  56. package/build/es/modules/pivotTable/getHeaderForDisplay.js +1 -1
  57. package/package.json +1 -1
  58. package/build/cjs/components/Interpretations/common/Interpretation/useLike.js +0 -56
  59. package/build/es/components/Interpretations/common/Interpretation/useLike.js +0 -50
@@ -5,46 +5,35 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.CommentUpdateForm = void 0;
7
7
  var _style = _interopRequireDefault(require("styled-jsx/style"));
8
- var _appRuntime = require("@dhis2/app-runtime");
9
8
  var _d2I18n = _interopRequireDefault(require("@dhis2/d2-i18n"));
10
9
  var _ui = require("@dhis2/ui");
11
10
  var _propTypes = _interopRequireDefault(require("prop-types"));
12
11
  var _react = _interopRequireWildcard(require("react"));
13
12
  var _index = require("../../RichText/index.js");
14
13
  var _index2 = require("../common/index.js");
14
+ var _hooks = require("../InterpretationsProvider/hooks.js");
15
15
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
16
16
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
17
17
  const CommentUpdateForm = ({
18
- interpretationId,
19
- commentId,
20
- currentUser,
18
+ id,
21
19
  text,
22
- close,
23
20
  onComplete
24
21
  }) => {
22
+ const currentUser = (0, _hooks.useInterpretationsCurrentUser)();
25
23
  const [commentText, setCommentText] = (0, _react.useState)(text || '');
26
- const updateMutationRef = (0, _react.useRef)({
27
- resource: `interpretations/${interpretationId}/comments/${commentId}`,
28
- type: 'update',
29
- partial: false,
30
- data: ({
31
- commentText
32
- }) => commentText
33
- });
34
24
  const [update, {
35
25
  loading,
36
26
  error
37
- }] = (0, _appRuntime.useDataMutation)(updateMutationRef.current, {
38
- onComplete: () => {
39
- onComplete();
40
- close();
41
- }
27
+ }] = (0, _hooks.useUpdateCommentForActiveInterpretation)({
28
+ id,
29
+ text: commentText,
30
+ onComplete
42
31
  });
43
32
  const errorText = error ? _d2I18n.default.t('Could not update comment') : '';
44
33
  return /*#__PURE__*/_react.default.createElement("div", {
45
34
  className: _style.default.dynamic([["2690082310", [_ui.spacers.dp8, _ui.spacers.dp8, _ui.colors.grey100]]]) + " " + "message"
46
35
  }, /*#__PURE__*/_react.default.createElement(_index2.MessageEditorContainer, {
47
- currentUser: currentUser
36
+ currentUserName: currentUser.name
48
37
  }, /*#__PURE__*/_react.default.createElement(_index.RichTextEditor, {
49
38
  inputPlaceholder: _d2I18n.default.t('Enter comment text'),
50
39
  onChange: setCommentText,
@@ -55,14 +44,12 @@ const CommentUpdateForm = ({
55
44
  loading: loading,
56
45
  primary: true,
57
46
  small: true,
58
- onClick: () => update({
59
- commentText
60
- })
47
+ onClick: update
61
48
  }, _d2I18n.default.t('Update')), /*#__PURE__*/_react.default.createElement(_ui.Button, {
62
49
  disabled: loading,
63
50
  secondary: true,
64
51
  small: true,
65
- onClick: close
52
+ onClick: onComplete
66
53
  }, _d2I18n.default.t('Cancel')))), /*#__PURE__*/_react.default.createElement(_style.default, {
67
54
  id: "2690082310",
68
55
  dynamic: [_ui.spacers.dp8, _ui.spacers.dp8, _ui.colors.grey100]
@@ -70,10 +57,7 @@ const CommentUpdateForm = ({
70
57
  };
71
58
  exports.CommentUpdateForm = CommentUpdateForm;
72
59
  CommentUpdateForm.propTypes = {
73
- close: _propTypes.default.func.isRequired,
74
- commentId: _propTypes.default.string.isRequired,
75
- currentUser: _propTypes.default.object.isRequired,
76
- interpretationId: _propTypes.default.string.isRequired,
60
+ id: _propTypes.default.string.isRequired,
77
61
  onComplete: _propTypes.default.func.isRequired,
78
62
  text: _propTypes.default.string
79
63
  };
@@ -5,12 +5,12 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.InterpretationModal = void 0;
7
7
  var _style = _interopRequireDefault(require("styled-jsx/style"));
8
- var _appRuntime = require("@dhis2/app-runtime");
9
8
  var _d2I18n = _interopRequireDefault(require("@dhis2/d2-i18n"));
10
9
  var _ui = require("@dhis2/ui");
11
10
  var _classnames = _interopRequireDefault(require("classnames"));
12
11
  var _propTypes = _interopRequireDefault(require("prop-types"));
13
12
  var _react = _interopRequireWildcard(require("react"));
13
+ var _hooks = require("../InterpretationsProvider/hooks.js");
14
14
  var _InterpretationThread = require("./InterpretationThread.js");
15
15
  var _useModalContentWidth = require("./useModalContentWidth.js");
16
16
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
@@ -30,25 +30,12 @@ function getModalContentCSS(width) {
30
30
  className: _style.default.dynamic([["2099285089", [width]]])
31
31
  };
32
32
  }
33
- const query = {
34
- interpretation: {
35
- resource: 'interpretations',
36
- id: ({
37
- id
38
- }) => id,
39
- params: {
40
- fields: ['access[write,manage]', 'id', 'text', 'created', 'createdBy[id,displayName]', 'likes', 'likedBy', 'comments[id,text,created,createdBy[id,displayName]]']
41
- }
42
- }
43
- };
44
33
  const InterpretationModal = ({
45
- currentUser,
46
34
  isVisualizationLoading,
47
35
  visualization,
48
36
  onResponsesReceived,
49
37
  downloadMenuComponent,
50
38
  onClose,
51
- onInterpretationUpdate,
52
39
  interpretationId,
53
40
  initialFocus,
54
41
  pluginComponent: VisualizationPlugin
@@ -56,53 +43,14 @@ const InterpretationModal = ({
56
43
  var _currentUser$settings;
57
44
  const modalContentWidth = (0, _useModalContentWidth.useModalContentWidth)();
58
45
  const modalContentCSS = getModalContentCSS(modalContentWidth);
59
- const [isDirty, setIsDirty] = (0, _react.useState)(false);
46
+ const currentUser = (0, _hooks.useInterpretationsCurrentUser)();
60
47
  const {
61
- data,
62
- error,
48
+ data: interpretation,
63
49
  loading,
64
- fetching,
65
- refetch
66
- } = (0, _appRuntime.useDataQuery)(query, {
67
- lazy: true
68
- });
69
- const interpretation = data === null || data === void 0 ? void 0 : data.interpretation;
50
+ error
51
+ } = (0, _hooks.useActiveInterpretation)(interpretationId);
70
52
  const shouldRenderModalContent = !error && interpretation;
71
53
  const loadingInProgress = loading || isVisualizationLoading;
72
- const handleClose = () => {
73
- if (isDirty) {
74
- onInterpretationUpdate();
75
- setIsDirty(false);
76
- }
77
- onClose();
78
- };
79
- const onThreadUpdated = affectsInterpretation => {
80
- if (affectsInterpretation) {
81
- setIsDirty(true);
82
- }
83
- refetch({
84
- id: interpretationId
85
- });
86
- };
87
- const onLikeToggled = ({
88
- likedBy
89
- }) => {
90
- setIsDirty(true);
91
- interpretation.likedBy = likedBy;
92
- interpretation.likes = likedBy.length;
93
- };
94
- const onInterpretationDeleted = () => {
95
- setIsDirty(false);
96
- onInterpretationUpdate();
97
- onClose();
98
- };
99
- (0, _react.useEffect)(() => {
100
- if (interpretationId) {
101
- refetch({
102
- id: interpretationId
103
- });
104
- }
105
- }, [interpretationId, refetch]);
106
54
  const filters = (0, _react.useMemo)(() => {
107
55
  return {
108
56
  relativePeriodDate: interpretation === null || interpretation === void 0 ? void 0 : interpretation.created
@@ -110,7 +58,7 @@ const InterpretationModal = ({
110
58
  }, [interpretation === null || interpretation === void 0 ? void 0 : interpretation.created]);
111
59
  return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, loadingInProgress && /*#__PURE__*/_react.default.createElement(_ui.Layer, null, /*#__PURE__*/_react.default.createElement(_ui.CenteredContent, null, /*#__PURE__*/_react.default.createElement(_ui.CircularLoader, null))), /*#__PURE__*/_react.default.createElement(_ui.Modal, {
112
60
  fluid: true,
113
- onClose: handleClose,
61
+ onClose: onClose,
114
62
  className: (0, _classnames.default)(modalCSS.className, {
115
63
  hidden: loadingInProgress
116
64
  }),
@@ -143,17 +91,14 @@ const InterpretationModal = ({
143
91
  })), /*#__PURE__*/_react.default.createElement("div", {
144
92
  className: _style.default.dynamic([["2014146191", [_ui.colors.grey900, _ui.spacers.dp24, _ui.spacers.dp4, _ui.spacers.dp4]]]) + " " + "thread-wrap"
145
93
  }, /*#__PURE__*/_react.default.createElement(_InterpretationThread.InterpretationThread, {
146
- currentUser: currentUser,
147
- fetching: fetching,
94
+ loading: loading,
148
95
  interpretation: interpretation,
149
- onInterpretationDeleted: onInterpretationDeleted,
150
- onThreadUpdated: onThreadUpdated,
151
96
  initialFocus: initialFocus,
152
97
  downloadMenuComponent: downloadMenuComponent,
153
- onLikeToggled: onLikeToggled
98
+ onInterpretationDeleted: onClose
154
99
  }))))), /*#__PURE__*/_react.default.createElement(_ui.ModalActions, null, /*#__PURE__*/_react.default.createElement(_ui.Button, {
155
- disabled: fetching,
156
- onClick: handleClose
100
+ disabled: loading,
101
+ onClick: onClose
157
102
  }, _d2I18n.default.t('Hide interpretation'))), modalCSS.styles, modalContentCSS.styles, /*#__PURE__*/_react.default.createElement(_style.default, {
158
103
  id: "2014146191",
159
104
  dynamic: [_ui.colors.grey900, _ui.spacers.dp24, _ui.spacers.dp4, _ui.spacers.dp4]
@@ -161,7 +106,6 @@ const InterpretationModal = ({
161
106
  };
162
107
  exports.InterpretationModal = InterpretationModal;
163
108
  InterpretationModal.propTypes = {
164
- currentUser: _propTypes.default.object.isRequired,
165
109
  interpretationId: _propTypes.default.string.isRequired,
166
110
  isVisualizationLoading: _propTypes.default.bool.isRequired,
167
111
  pluginComponent: _propTypes.default.oneOfType([_propTypes.default.object, _propTypes.default.func]).isRequired,
@@ -169,6 +113,5 @@ InterpretationModal.propTypes = {
169
113
  onClose: _propTypes.default.func.isRequired,
170
114
  onResponsesReceived: _propTypes.default.func.isRequired,
171
115
  downloadMenuComponent: _propTypes.default.oneOfType([_propTypes.default.object, _propTypes.default.func]),
172
- initialFocus: _propTypes.default.bool,
173
- onInterpretationUpdate: _propTypes.default.func
116
+ initialFocus: _propTypes.default.bool
174
117
  };
@@ -12,36 +12,35 @@ var _moment = _interopRequireDefault(require("moment"));
12
12
  var _propTypes = _interopRequireDefault(require("prop-types"));
13
13
  var _react = _interopRequireWildcard(require("react"));
14
14
  var _index = require("../common/index.js");
15
+ var _hooks = require("../InterpretationsProvider/hooks.js");
15
16
  var _Comment = require("./Comment.js");
16
17
  var _CommentAddForm = require("./CommentAddForm.js");
17
18
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
18
19
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
19
20
  const InterpretationThread = ({
20
- currentUser,
21
- fetching,
21
+ loading,
22
22
  interpretation,
23
23
  onInterpretationDeleted,
24
- onLikeToggled,
25
24
  initialFocus,
26
- onThreadUpdated,
27
25
  downloadMenuComponent: DownloadMenu,
28
26
  dashboardRedirectUrl
29
27
  }) => {
28
+ const currentUser = (0, _hooks.useInterpretationsCurrentUser)();
30
29
  const {
31
30
  fromServerDate
32
31
  } = (0, _appRuntime.useTimeZoneConversion)();
33
32
  const focusRef = (0, _react.useRef)();
34
33
  (0, _react.useEffect)(() => {
35
34
  if (initialFocus && focusRef.current) {
36
- window.requestAnimationFrame(() => {
35
+ window.setTimeout(() => {
37
36
  focusRef.current.focus();
38
- });
37
+ }, 25);
39
38
  }
40
39
  }, [initialFocus]);
41
40
  const interpretationAccess = (0, _index.getInterpretationAccess)(interpretation, currentUser);
42
41
  return /*#__PURE__*/_react.default.createElement("div", {
43
42
  className: "jsx-3292109121" + " " + ((0, _classnames.default)('container', {
44
- fetching,
43
+ fetching: loading,
45
44
  dashboard: !!dashboardRedirectUrl
46
45
  }) || "")
47
46
  }, /*#__PURE__*/_react.default.createElement("div", {
@@ -54,30 +53,21 @@ const InterpretationThread = ({
54
53
  }), /*#__PURE__*/_react.default.createElement("div", {
55
54
  className: "jsx-3292109121" + " " + 'thread'
56
55
  }, /*#__PURE__*/_react.default.createElement(_index.Interpretation, {
57
- currentUser: currentUser,
58
- interpretation: interpretation,
59
- onLikeToggled: onLikeToggled,
56
+ id: interpretation.id,
60
57
  onReplyIconClick: interpretationAccess.comment ? () => {
61
58
  var _focusRef$current;
62
59
  return (_focusRef$current = focusRef.current) === null || _focusRef$current === void 0 ? void 0 : _focusRef$current.focus();
63
60
  } : null,
64
- onUpdated: () => onThreadUpdated(true),
65
- onDeleted: onInterpretationDeleted,
66
61
  dashboardRedirectUrl: dashboardRedirectUrl,
67
- isInThread: true
62
+ isInThread: true,
63
+ onDeleted: onInterpretationDeleted
68
64
  }), /*#__PURE__*/_react.default.createElement("div", {
69
65
  className: "jsx-3292109121" + " " + 'comments'
70
66
  }, interpretation.comments.map(comment => /*#__PURE__*/_react.default.createElement(_Comment.Comment, {
71
67
  key: comment.id,
72
68
  comment: comment,
73
- currentUser: currentUser,
74
- interpretationId: interpretation.id,
75
- onThreadUpdated: onThreadUpdated,
76
69
  canComment: interpretationAccess.comment
77
70
  })))), interpretationAccess.comment && /*#__PURE__*/_react.default.createElement(_CommentAddForm.CommentAddForm, {
78
- currentUser: currentUser,
79
- interpretationId: interpretation.id,
80
- onSave: () => onThreadUpdated(true),
81
71
  focusRef: focusRef
82
72
  }), /*#__PURE__*/_react.default.createElement(_style.default, {
83
73
  id: "3292109121"
@@ -85,13 +75,10 @@ const InterpretationThread = ({
85
75
  };
86
76
  exports.InterpretationThread = InterpretationThread;
87
77
  InterpretationThread.propTypes = {
88
- currentUser: _propTypes.default.object.isRequired,
89
- fetching: _propTypes.default.bool.isRequired,
90
78
  interpretation: _propTypes.default.object.isRequired,
79
+ loading: _propTypes.default.bool.isRequired,
91
80
  onInterpretationDeleted: _propTypes.default.func.isRequired,
92
- onLikeToggled: _propTypes.default.func.isRequired,
93
81
  dashboardRedirectUrl: _propTypes.default.string,
94
82
  downloadMenuComponent: _propTypes.default.oneOfType([_propTypes.default.object, _propTypes.default.func]),
95
- initialFocus: _propTypes.default.bool,
96
- onThreadUpdated: _propTypes.default.func
83
+ initialFocus: _propTypes.default.bool
97
84
  };
@@ -0,0 +1,275 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.InterpretationsManager = void 0;
7
+ var _groupInterpretationIdsByDate = require("./groupInterpretationIdsByDate.js");
8
+ class InterpretationsManager {
9
+ constructor(dataEngine, currentUser) {
10
+ if (!dataEngine || !currentUser) {
11
+ throw new Error('Initialised InterpretationsManager without dataEngine or currentUser');
12
+ }
13
+ this.query = dataEngine.query.bind(dataEngine);
14
+ this.mutate = dataEngine.mutate.bind(dataEngine);
15
+ this.currentUser = currentUser;
16
+ this.currentVisualizationId = null;
17
+ this.currentType = null;
18
+ this.interpretations = new Map();
19
+ this.activeInterpretationId = null;
20
+ this.interpretationsListCallback = null;
21
+ this.interpretationObservers = new Map();
22
+ }
23
+ getInterpretation(id) {
24
+ const interpretation = this.interpretations.get(id);
25
+ if (!interpretation) {
26
+ throw new Error(`Could not get interpretation with id ${id}`);
27
+ }
28
+ return interpretation;
29
+ }
30
+ getCurrentUser() {
31
+ return this.currentUser;
32
+ }
33
+ getActiveInterpretation() {
34
+ const activeInterpretation = this.interpretations.get(this.activeInterpretationId);
35
+ if (!activeInterpretation) {
36
+ throw new Error('There currently is no active interpretation');
37
+ }
38
+ return activeInterpretation;
39
+ }
40
+ subscribeToInterpretationsListUpdates(callback) {
41
+ this.interpretationsListCallback = callback;
42
+
43
+ // return cleanup function for useEffect hooks
44
+ return () => {
45
+ this.interpretationsListCallback = null;
46
+ };
47
+ }
48
+ subscribeToInterpretationUpdates(id, callback) {
49
+ // create callback Set if needed on the fly
50
+ if (!this.interpretationObservers.has(id)) {
51
+ this.interpretationObservers.set(id, new Set());
52
+ }
53
+ this.interpretationObservers.get(id).add(callback);
54
+
55
+ // return cleanup function for useEffect hooks
56
+ return () => {
57
+ this.interpretationObservers.get(id).delete(callback);
58
+ };
59
+ }
60
+ notifyInterpretationsListObserver() {
61
+ if (this.interpretationsListCallback) {
62
+ this.interpretationsListCallback((0, _groupInterpretationIdsByDate.groupInterpretationIdsByDate)(Array.from(this.interpretations.values())));
63
+ }
64
+ }
65
+ notifyInterpretationObservers(id) {
66
+ const callbacks = this.interpretationObservers.get(id);
67
+ if (callbacks) {
68
+ for (const callback of callbacks) {
69
+ callback(this.getInterpretation(id));
70
+ }
71
+ }
72
+ }
73
+ clearActiveInterpretation() {
74
+ this.activeInterpretationId = null;
75
+ }
76
+ clearInterpretations() {
77
+ this.clearActiveInterpretation();
78
+ this.currentVisualizationId = null;
79
+ this.currentType = null;
80
+ this.interpretations.clear();
81
+ }
82
+ resetInterpretations(newInterpretations) {
83
+ this.interpretations.clear();
84
+ for (const interpretation of newInterpretations) {
85
+ this.interpretations.set(interpretation.id, interpretation);
86
+ }
87
+ }
88
+ async fetchInterpretationDetails(id) {
89
+ const result = await this.query({
90
+ interpretation: {
91
+ resource: 'interpretations',
92
+ id,
93
+ params: {
94
+ fields: ['access[write,manage]', 'comments[id,text,created,createdBy[id,displayName]]', 'created', 'createdBy[id,displayName]', 'id', 'likedBy', 'likes', 'text']
95
+ }
96
+ }
97
+ });
98
+ return result.interpretation;
99
+ }
100
+ async fetchInterpretationsList() {
101
+ if (!this.currentType || !this.currentVisualizationId) {
102
+ throw new Error('Called fetchInterpretationsList before currentType or currentVisualizationId was set');
103
+ }
104
+ const result = await this.query({
105
+ interpretations: {
106
+ resource: 'interpretations',
107
+ params: {
108
+ fields: ['access[write,manage]', 'comments[id]', 'created', 'createdBy[id,displayName]', 'id', 'likedBy[id]', 'likes', 'text'],
109
+ filter: `${this.currentType}.id:eq:${this.currentVisualizationId}`,
110
+ paging: false
111
+ }
112
+ }
113
+ });
114
+ return result.interpretations.interpretations;
115
+ }
116
+ async loadActiveInterpretation(id) {
117
+ const interpretation = await this.fetchInterpretationDetails(id);
118
+ this.interpretations.set(id, interpretation);
119
+ this.activeInterpretationId = id;
120
+ return this.getActiveInterpretation();
121
+ }
122
+ async loadInterpretationsForVisualization(type, id) {
123
+ this.currentType = type;
124
+ this.currentVisualizationId = id;
125
+ const interpretations = await this.fetchInterpretationsList();
126
+ this.resetInterpretations(interpretations);
127
+ return (0, _groupInterpretationIdsByDate.groupInterpretationIdsByDate)(interpretations);
128
+ }
129
+ async createInterpretation({
130
+ type,
131
+ id,
132
+ text,
133
+ onComplete
134
+ }) {
135
+ await this.mutate({
136
+ resource: `interpretations/${type}/${id}`,
137
+ type: 'create',
138
+ data: text
139
+ }, {
140
+ onComplete
141
+ });
142
+ /* since the create request does not return an ID we must refetch the list
143
+ * and cannot return the created interpretation */
144
+ const interpretations = await this.fetchInterpretationsList();
145
+ this.resetInterpretations(interpretations);
146
+ this.notifyInterpretationsListObserver();
147
+ return null;
148
+ }
149
+ async deleteInterpretation({
150
+ id,
151
+ onComplete,
152
+ onError
153
+ }) {
154
+ await this.mutate({
155
+ resource: 'interpretations',
156
+ id,
157
+ type: 'delete'
158
+ }, {
159
+ onComplete,
160
+ onError
161
+ });
162
+ // This happens when deleting the interpretation from the modal
163
+ if (this.activeInterpretationId && id === this.activeInterpretationId) {
164
+ this.clearActiveInterpretation();
165
+ }
166
+ this.interpretations.delete(id);
167
+ this.notifyInterpretationsListObserver();
168
+ return null;
169
+ }
170
+ async updateInterpretationText({
171
+ id,
172
+ text,
173
+ onComplete,
174
+ onError
175
+ }) {
176
+ await this.mutate({
177
+ resource: 'interpretations',
178
+ type: 'update',
179
+ partial: false,
180
+ id,
181
+ data: text
182
+ }, {
183
+ onComplete,
184
+ onError
185
+ });
186
+ const updatedInterpretation = {
187
+ ...this.getInterpretation(id),
188
+ text
189
+ };
190
+ this.interpretations.set(id, updatedInterpretation);
191
+ this.notifyInterpretationObservers(id);
192
+ return updatedInterpretation;
193
+ }
194
+ async toggleInterpretationLike(id) {
195
+ const interpretation = this.getInterpretation(id);
196
+ const wasLikedByCurrentUser = interpretation.likedBy.some(likedBy => likedBy.id === this.currentUser.id);
197
+ await this.mutate({
198
+ resource: `interpretations/${id}/like`,
199
+ type: wasLikedByCurrentUser ? 'delete' : 'create'
200
+ });
201
+ const isLikedByCurrentUser = !wasLikedByCurrentUser;
202
+ const updatedInterpretation = {
203
+ ...interpretation,
204
+ likedBy: isLikedByCurrentUser ? interpretation.likedBy.concat({
205
+ id: this.currentUser.id
206
+ }) : interpretation.likedBy.filter(lb => lb.id !== this.currentUser.id),
207
+ likes: isLikedByCurrentUser ? interpretation.likes + 1 : interpretation.likes - 1
208
+ };
209
+ this.interpretations.set(id, updatedInterpretation);
210
+ this.notifyInterpretationObservers(id);
211
+ return updatedInterpretation;
212
+ }
213
+ async addCommentToActiveInterpretation({
214
+ text,
215
+ onComplete
216
+ }) {
217
+ const {
218
+ id
219
+ } = this.getActiveInterpretation();
220
+ await this.mutate({
221
+ resource: `interpretations/${id}/comments`,
222
+ type: 'create',
223
+ data: text
224
+ }, {
225
+ onComplete
226
+ });
227
+ const interpretation = await this.fetchInterpretationDetails(id);
228
+ this.interpretations.set(id, interpretation);
229
+ this.notifyInterpretationObservers(id);
230
+ return interpretation;
231
+ }
232
+ async deleteCommentFromActiveInterpretation({
233
+ id
234
+ }) {
235
+ const activeInterpretation = this.getActiveInterpretation();
236
+ await this.mutate({
237
+ resource: `interpretations/${activeInterpretation.id}/comments/${id}`,
238
+ type: 'delete'
239
+ });
240
+ const updatedInterpretation = {
241
+ ...activeInterpretation,
242
+ comments: activeInterpretation.comments.filter(({
243
+ id: commentId
244
+ }) => commentId !== id)
245
+ };
246
+ this.interpretations.set(activeInterpretation.id, updatedInterpretation);
247
+ this.notifyInterpretationObservers(activeInterpretation.id);
248
+ return updatedInterpretation;
249
+ }
250
+ async updateCommentForActiveInterpretation({
251
+ id,
252
+ text,
253
+ onComplete
254
+ }) {
255
+ const activeInterpretation = this.getActiveInterpretation();
256
+ this.mutate({
257
+ resource: `interpretations/${activeInterpretation.id}/comments/${id}`,
258
+ type: 'update',
259
+ partial: false,
260
+ data: text
261
+ }, {
262
+ onComplete: () => onComplete(text)
263
+ });
264
+ const updatedInterpretation = {
265
+ ...activeInterpretation,
266
+ comments: activeInterpretation.comments.map(comment => comment.id === id ? {
267
+ ...comment,
268
+ text
269
+ } : comment)
270
+ };
271
+ this.interpretations.set(activeInterpretation.id, updatedInterpretation);
272
+ return updatedInterpretation;
273
+ }
274
+ }
275
+ exports.InterpretationsManager = InterpretationsManager;
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.InterpretationsProvider = exports.InterpretationsContext = void 0;
7
+ var _appRuntime = require("@dhis2/app-runtime");
8
+ var _propTypes = _interopRequireDefault(require("prop-types"));
9
+ var _react = _interopRequireWildcard(require("react"));
10
+ var _InterpretationsManager = require("./InterpretationsManager.js");
11
+ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
12
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
13
+ const InterpretationsContext = exports.InterpretationsContext = /*#__PURE__*/(0, _react.createContext)(null);
14
+ const InterpretationsProvider = ({
15
+ currentUser,
16
+ children
17
+ }) => {
18
+ const dataEngine = (0, _appRuntime.useDataEngine)();
19
+ const [interpretationsManager] = (0, _react.useState)(() => new _InterpretationsManager.InterpretationsManager(dataEngine, currentUser));
20
+ return /*#__PURE__*/_react.default.createElement(InterpretationsContext.Provider, {
21
+ value: interpretationsManager
22
+ }, children);
23
+ };
24
+ exports.InterpretationsProvider = InterpretationsProvider;
25
+ InterpretationsProvider.propTypes = {
26
+ children: _propTypes.default.node,
27
+ currentUser: _propTypes.default.object
28
+ };
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+
3
+ var _groupInterpretationIdsByDate = require("../groupInterpretationIdsByDate.js");
4
+ describe('groupInterpretationIdsByDate', () => {
5
+ it('should group interpretations by date and sort them correctly', () => {
6
+ const interpretations = [{
7
+ id: 'id1',
8
+ created: '2025-09-04T07:47:12.477'
9
+ }, {
10
+ id: 'id2',
11
+ created: '2025-09-04T15:30:45.123'
12
+ }, {
13
+ id: 'id3',
14
+ created: '2025-09-03T10:20:30.456'
15
+ }, {
16
+ id: 'id4',
17
+ created: '2025-09-05T09:15:22.789'
18
+ }, {
19
+ id: 'id5',
20
+ created: '2025-09-03T18:45:15.012'
21
+ }];
22
+ const result = (0, _groupInterpretationIdsByDate.groupInterpretationIdsByDate)(interpretations);
23
+
24
+ // Check that dates are grouped correctly
25
+ expect(Object.keys(result)).toEqual(['2025-09-05', '2025-09-04', '2025-09-03']);
26
+
27
+ // Check that within each date group, items are sorted from most recent to oldest
28
+ expect(result['2025-09-04']).toEqual(['id2', 'id1']); // 15:30 before 07:47
29
+ expect(result['2025-09-03']).toEqual(['id5', 'id3']); // 18:45 before 10:20
30
+ expect(result['2025-09-05']).toEqual(['id4']);
31
+ });
32
+ it('should handle empty array', () => {
33
+ const interpretations = [];
34
+ const result = (0, _groupInterpretationIdsByDate.groupInterpretationIdsByDate)(interpretations);
35
+ expect(result).toEqual({});
36
+ });
37
+ });