@openmrs/esm-patient-tests-app 11.3.1-patch.9064 → 11.3.1-patch.9508

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 (180) hide show
  1. package/.turbo/turbo-build.log +22 -22
  2. package/dist/1119.js +1 -1
  3. package/dist/1197.js +1 -1
  4. package/dist/{6231.js → 1477.js} +1 -1
  5. package/dist/1477.js.map +1 -0
  6. package/dist/1638.js +1 -1
  7. package/dist/1638.js.map +1 -1
  8. package/dist/1935.js +1 -1
  9. package/dist/1935.js.map +1 -1
  10. package/dist/2146.js +1 -1
  11. package/dist/2690.js +1 -1
  12. package/dist/3099.js +1 -1
  13. package/dist/34.js +1 -1
  14. package/dist/34.js.map +1 -1
  15. package/dist/3509.js +1 -1
  16. package/dist/3509.js.map +1 -1
  17. package/dist/3584.js +1 -1
  18. package/dist/4055.js +1 -1
  19. package/dist/4132.js +1 -1
  20. package/dist/4300.js +1 -1
  21. package/dist/4335.js +1 -1
  22. package/dist/439.js +1 -0
  23. package/dist/4618.js +1 -1
  24. package/dist/4652.js +1 -1
  25. package/dist/4944.js +1 -1
  26. package/dist/5173.js +1 -1
  27. package/dist/5241.js +1 -1
  28. package/dist/5442.js +1 -1
  29. package/dist/5661.js +1 -1
  30. package/dist/5670.js +1 -1
  31. package/dist/5670.js.map +1 -1
  32. package/dist/6022.js +1 -1
  33. package/dist/6113.js +1 -0
  34. package/dist/6113.js.map +1 -0
  35. package/dist/6301.js +1 -1
  36. package/dist/6301.js.map +1 -1
  37. package/dist/6336.js +1 -0
  38. package/dist/6336.js.map +1 -0
  39. package/dist/6468.js +1 -1
  40. package/dist/6589.js +1 -0
  41. package/dist/6679.js +1 -1
  42. package/dist/6840.js +1 -1
  43. package/dist/6859.js +1 -1
  44. package/dist/7097.js +1 -1
  45. package/dist/7159.js +1 -1
  46. package/dist/7202.js +1 -0
  47. package/dist/7202.js.map +1 -0
  48. package/dist/723.js +1 -1
  49. package/dist/7617.js +1 -1
  50. package/dist/790.js +1 -1
  51. package/dist/790.js.map +1 -1
  52. package/dist/795.js +1 -1
  53. package/dist/8163.js +1 -1
  54. package/dist/8307.js +2 -0
  55. package/dist/8307.js.map +1 -0
  56. package/dist/8349.js +1 -1
  57. package/dist/8371.js +1 -0
  58. package/dist/8555.js +2 -0
  59. package/dist/8555.js.map +1 -0
  60. package/dist/8618.js +1 -1
  61. package/dist/890.js +1 -1
  62. package/dist/9214.js +1 -1
  63. package/dist/9538.js +1 -1
  64. package/dist/9569.js +1 -1
  65. package/dist/986.js +1 -1
  66. package/dist/9879.js +1 -1
  67. package/dist/9895.js +1 -1
  68. package/dist/9900.js +1 -1
  69. package/dist/9913.js +1 -1
  70. package/dist/main.js +1 -1
  71. package/dist/main.js.map +1 -1
  72. package/dist/openmrs-esm-patient-tests-app.js +1 -1
  73. package/dist/openmrs-esm-patient-tests-app.js.buildmanifest.json +319 -249
  74. package/dist/openmrs-esm-patient-tests-app.js.map +1 -1
  75. package/dist/routes.json +1 -1
  76. package/package.json +3 -3
  77. package/src/edit-test-results/modal/edit-lab-results.modal.tsx +6 -2
  78. package/src/index.ts +1 -1
  79. package/src/routes.json +2 -2
  80. package/src/test-orders/add-test-order/add-test-order.test.tsx +13 -10
  81. package/src/test-orders/add-test-order/add-test-order.workspace.tsx +43 -7
  82. package/src/test-orders/add-test-order/test-order-form.component.tsx +41 -7
  83. package/src/test-orders/add-test-order/test-type-search.component.tsx +56 -8
  84. package/src/test-orders/lab-order-basket-panel/lab-icon.component.tsx +27 -0
  85. package/src/test-orders/lab-order-basket-panel/lab-order-basket-panel.extension.tsx +62 -15
  86. package/src/test-orders/lab-order-basket-panel/lab-order-basket-panel.scss +26 -11
  87. package/src/test-orders/lab-order-basket-panel/lab-order-basket-panel.test.tsx +18 -5
  88. package/src/test-results/filter/filter-context.test.tsx +556 -0
  89. package/src/test-results/filter/filter-context.tsx +1 -1
  90. package/src/test-results/filter/filter-reducer.test.ts +540 -0
  91. package/src/test-results/filter/filter-reducer.ts +1 -1
  92. package/src/test-results/filter/filter-set.component.tsx +75 -48
  93. package/src/test-results/filter/filter-set.test.tsx +694 -0
  94. package/src/test-results/filter/filter-types.ts +24 -1
  95. package/src/test-results/grouped-timeline/grid.component.tsx +4 -2
  96. package/src/test-results/grouped-timeline/grouped-timeline.component.tsx +20 -22
  97. package/src/test-results/grouped-timeline/grouped-timeline.test.tsx +52 -2
  98. package/src/test-results/grouped-timeline/reference-range-helpers.test.ts +308 -0
  99. package/src/test-results/grouped-timeline/reference-range-helpers.ts +161 -0
  100. package/src/test-results/grouped-timeline/timeline-data-group.component.tsx +13 -6
  101. package/src/test-results/grouped-timeline/useObstreeData.test.ts +471 -0
  102. package/src/test-results/grouped-timeline/useObstreeData.ts +108 -13
  103. package/src/test-results/individual-results-table/individual-results-table.component.tsx +18 -6
  104. package/src/test-results/individual-results-table/individual-results-table.test.tsx +65 -3
  105. package/src/test-results/individual-results-table-tablet/helper.tsx +8 -2
  106. package/src/test-results/individual-results-table-tablet/individual-results-table-tablet.component.tsx +5 -5
  107. package/src/test-results/individual-results-table-tablet/lab-set-panel.component.tsx +2 -1
  108. package/src/test-results/individual-results-table-tablet/usePanelData.tsx +40 -26
  109. package/src/test-results/loadPatientTestData/helpers.test.ts +834 -0
  110. package/src/test-results/loadPatientTestData/helpers.ts +143 -12
  111. package/src/test-results/loadPatientTestData/loadPatientData.ts +66 -11
  112. package/src/test-results/loadPatientTestData/usePatientResultsData.ts +20 -9
  113. package/src/test-results/overview/common-datatable.component.tsx +1 -1
  114. package/src/test-results/overview/external-overview.extension.tsx +1 -2
  115. package/src/test-results/overview/useOverviewData.ts +22 -10
  116. package/src/test-results/print-modal/print-modal.extension.tsx +1 -1
  117. package/src/test-results/results-viewer/results-viewer.extension.tsx +12 -7
  118. package/src/test-results/tree-view/tree-view.component.tsx +31 -8
  119. package/src/test-results/tree-view/tree-view.test.tsx +119 -2
  120. package/src/test-results/trendline/trendline-resource.tsx +48 -5
  121. package/src/test-results/trendline/trendline.component.tsx +88 -52
  122. package/src/test-results/ui-elements/{resetFiltersEmptyState → reset-filters-empty-state}/filter-empty-data-illustration.tsx +2 -2
  123. package/src/test-results/ui-elements/{resetFiltersEmptyState → reset-filters-empty-state}/filter-empty-state.component.tsx +5 -6
  124. package/src/types.ts +20 -1
  125. package/translations/am.json +3 -4
  126. package/translations/ar.json +3 -4
  127. package/translations/ar_SY.json +3 -4
  128. package/translations/bn.json +3 -4
  129. package/translations/cs.json +119 -0
  130. package/translations/de.json +3 -4
  131. package/translations/en.json +3 -2
  132. package/translations/en_US.json +3 -4
  133. package/translations/es.json +3 -4
  134. package/translations/es_MX.json +3 -4
  135. package/translations/fr.json +5 -6
  136. package/translations/he.json +3 -4
  137. package/translations/hi.json +3 -4
  138. package/translations/hi_IN.json +3 -4
  139. package/translations/id.json +3 -4
  140. package/translations/it.json +3 -4
  141. package/translations/ka.json +3 -4
  142. package/translations/km.json +3 -4
  143. package/translations/ku.json +3 -4
  144. package/translations/ky.json +3 -4
  145. package/translations/lg.json +3 -4
  146. package/translations/ne.json +3 -4
  147. package/translations/pl.json +3 -4
  148. package/translations/pt.json +3 -4
  149. package/translations/pt_BR.json +3 -4
  150. package/translations/qu.json +3 -4
  151. package/translations/ro_RO.json +3 -4
  152. package/translations/ru_RU.json +3 -4
  153. package/translations/si.json +3 -4
  154. package/translations/sq.json +119 -0
  155. package/translations/sw.json +3 -4
  156. package/translations/sw_KE.json +3 -4
  157. package/translations/tr.json +3 -4
  158. package/translations/tr_TR.json +3 -4
  159. package/translations/uk.json +3 -4
  160. package/translations/uz.json +3 -4
  161. package/translations/uz@Latn.json +3 -4
  162. package/translations/uz_UZ.json +3 -4
  163. package/translations/vi.json +3 -4
  164. package/translations/zh.json +3 -4
  165. package/translations/zh_CN.json +3 -4
  166. package/translations/zh_TW.json +119 -0
  167. package/dist/1479.js +0 -1
  168. package/dist/1479.js.map +0 -1
  169. package/dist/2537.js +0 -1
  170. package/dist/2537.js.map +0 -1
  171. package/dist/4918.js +0 -1
  172. package/dist/4918.js.map +0 -1
  173. package/dist/5836.js +0 -2
  174. package/dist/5836.js.map +0 -1
  175. package/dist/6231.js.map +0 -1
  176. package/dist/7053.js +0 -2
  177. package/dist/7053.js.map +0 -1
  178. /package/dist/{7053.js.LICENSE.txt → 8307.js.LICENSE.txt} +0 -0
  179. /package/dist/{5836.js.LICENSE.txt → 8555.js.LICENSE.txt} +0 -0
  180. /package/src/test-results/ui-elements/{resetFiltersEmptyState/index.scss → reset-filters-empty-state/filter-empty-state.scss} +0 -0
@@ -1,15 +1,15 @@
1
- import React, { useContext, useState, useEffect } from 'react';
2
- import { useTranslation } from 'react-i18next';
1
+ import React, { useContext, useEffect, useState } from 'react';
3
2
  import classNames from 'classnames';
3
+ import { useTranslation } from 'react-i18next';
4
4
  import { Accordion, AccordionItem, Button, Checkbox } from '@carbon/react';
5
5
  import { useConfig, useLayoutType } from '@openmrs/esm-framework';
6
6
  import { type ConfigObject } from '../../config-schema';
7
7
  import type { FilterNodeProps, FilterLeafProps } from './filter-types';
8
- import { FilterEmptyState } from '../ui-elements/resetFiltersEmptyState/filter-empty-state.component';
8
+ import FilterEmptyState from '../ui-elements/reset-filters-empty-state/filter-empty-state.component';
9
9
  import FilterContext from './filter-context';
10
10
  import styles from './filter-set.scss';
11
11
 
12
- const isIndeterminate = (kids, checkboxes) => {
12
+ const isIndeterminate = (kids: Array<string>, checkboxes: Record<string, boolean>) => {
13
13
  return kids && !kids?.every((kid) => checkboxes[kid]) && !kids?.every((kid) => !checkboxes[kid]);
14
14
  };
15
15
 
@@ -17,65 +17,87 @@ interface FilterSetProps {
17
17
  hideFilterSetHeader?: boolean;
18
18
  }
19
19
 
20
- interface filterNodeParentProps extends Pick<FilterNodeProps, 'root'> {
20
+ interface FilterNodeParentProps extends Pick<FilterNodeProps, 'root'> {
21
21
  itemNumber: number;
22
22
  }
23
23
 
24
- function filterTreeNode(inputValue, treeNode) {
25
- // If the tree node's display value contains the user input, or any of its children's display contains the user input, return true
26
- if (
27
- treeNode &&
28
- (treeNode.display.toLowerCase().includes(inputValue.toLowerCase()) ||
29
- (treeNode.subSets && treeNode.subSets.some((child) => filterTreeNode(inputValue, child))))
30
- ) {
31
- return true;
24
+ function filterTreeNode(inputValue: string, treeNode: any): boolean {
25
+ if (!treeNode) {
26
+ return false;
32
27
  }
33
28
 
34
- // Otherwise, return false
35
- return false;
29
+ const matchesSearch = treeNode.display.toLowerCase().includes(inputValue.toLowerCase());
30
+ const childMatches = treeNode.subSets?.some((child) => filterTreeNode(inputValue, child)) ?? false;
31
+
32
+ return matchesSearch || childMatches;
36
33
  }
37
34
 
38
35
  const FilterSet: React.FC<FilterSetProps> = () => {
39
36
  const { roots } = useContext(FilterContext);
37
+ const config = useConfig<ConfigObject>();
40
38
  const [searchTerm, setSearchTerm] = useState('');
41
39
  const [treeDataFiltered, setTreeDataFiltered] = useState(roots);
42
40
 
43
41
  useEffect(() => {
44
- // Filter the tree data, but ensure only parent categories (with subSets) are at root level
42
+ const configuredConceptUuids = config?.resultsViewerConcepts?.map((c) => c.conceptUuid) ?? [];
43
+
44
+ // Helper to check if a node or its children match configured concepts
45
+ const isConfiguredNode = (node: any): boolean => {
46
+ // Check if the node itself has a matching conceptUuid
47
+ if (node.conceptUuid && configuredConceptUuids.includes(node.conceptUuid)) {
48
+ return true;
49
+ }
50
+ // Check if any direct child has a matching conceptUuid
51
+ return (
52
+ node.subSets?.some((child: any) => child.conceptUuid && configuredConceptUuids.includes(child.conceptUuid)) ??
53
+ false
54
+ );
55
+ };
56
+
57
+ // Filter the tree data, ensuring only parent categories (with subSets) are at root level
45
58
  const filteredData = (roots as any[]).filter((node) => {
46
- // Only include nodes that are parent categories (have subSets)
47
- if (!node.subSets || !Array.isArray(node.subSets) || node.subSets.length === 0) {
48
- return false; // Exclude leaf tests from root level
59
+ // Only include nodes that have subSets (are parent categories)
60
+ if (!node.subSets || !Array.isArray(node.subSets)) {
61
+ return false;
49
62
  }
63
+
64
+ // Keep configured concepts even if they have no children after filtering
65
+ const isConfiguredConcept = isConfiguredNode(node);
66
+ if (node.subSets.length === 0 && !isConfiguredConcept) {
67
+ return false;
68
+ }
69
+
70
+ // Apply search filter
50
71
  return filterTreeNode(searchTerm, node);
51
72
  });
73
+
52
74
  setTreeDataFiltered(filteredData);
53
- }, [searchTerm, roots]);
75
+ }, [searchTerm, roots, config]);
54
76
 
55
77
  return (
56
- <div>
57
- <div className={styles.filterSetContent}>
58
- {treeDataFiltered?.length > 0 ? (
59
- treeDataFiltered?.map((root, index) => (
60
- <div className={`${styles.nestedAccordion} ${styles.nestedAccordionTablet}`} key={`filter-node-${index}`}>
61
- <FilterNodeParent root={root} itemNumber={index} />
62
- </div>
63
- ))
64
- ) : (
65
- <FilterEmptyState clearFilter={() => setSearchTerm('')} />
66
- )}
67
- </div>
78
+ <div className={styles.filterSetContent}>
79
+ {treeDataFiltered?.length > 0 ? (
80
+ treeDataFiltered?.map((root, index) => (
81
+ <div className={`${styles.nestedAccordion} ${styles.nestedAccordionTablet}`} key={`filter-node-${index}`}>
82
+ <FilterNodeParent root={root} itemNumber={index} />
83
+ </div>
84
+ ))
85
+ ) : (
86
+ <FilterEmptyState clearFilter={() => setSearchTerm('')} />
87
+ )}
68
88
  </div>
69
89
  );
70
90
  };
71
91
 
72
- const FilterNodeParent = ({ root, itemNumber }: filterNodeParentProps) => {
92
+ const FilterNodeParent = ({ root, itemNumber }: FilterNodeParentProps) => {
73
93
  const config = useConfig<ConfigObject>();
74
94
  const { t } = useTranslation();
75
95
  const tablet = useLayoutType() === 'tablet';
76
96
  const [expandAll, setExpandAll] = useState<boolean | undefined>(undefined);
77
97
 
78
- if (!root.subSets) return;
98
+ if (!root.subSets) {
99
+ return;
100
+ }
79
101
 
80
102
  const filterParent = root.subSets.map((node, key) => {
81
103
  // If node has obs data, it's a leaf - render as FilterLeaf
@@ -88,7 +110,7 @@ const FilterNodeParent = ({ root, itemNumber }: filterNodeParentProps) => {
88
110
  }
89
111
 
90
112
  // If node has subSets, it's a parent - render as FilterNode
91
- if (node?.subSets && Array.isArray(node.subSets) && node.subSets.length > 0) {
113
+ if (node?.subSets && Array.isArray(node.subSets)) {
92
114
  return (
93
115
  <div key={key}>
94
116
  <FilterNode
@@ -108,18 +130,22 @@ const FilterNodeParent = ({ root, itemNumber }: filterNodeParentProps) => {
108
130
  );
109
131
  });
110
132
 
133
+ const hasChildren = root.subSets && root.subSets.length > 0;
134
+
111
135
  return (
112
136
  <div>
113
137
  <div className={classNames(styles.treeNodeHeader, { [styles.treeNodeHeaderTablet]: tablet })}>
114
138
  <h5>{t(root.display)}</h5>
115
- <Button
116
- className={styles.button}
117
- kind="ghost"
118
- size={tablet ? 'md' : 'sm'}
119
- onClick={() => setExpandAll((prevValue) => !prevValue)}
120
- >
121
- <span>{expandAll ? t('collapseAll', 'Collapse all') : t('expandAll', 'Expand all')}</span>
122
- </Button>
139
+ {hasChildren && (
140
+ <Button
141
+ className={styles.button}
142
+ kind="ghost"
143
+ size={tablet ? 'md' : 'sm'}
144
+ onClick={() => setExpandAll((prevValue) => !prevValue)}
145
+ >
146
+ <span>{expandAll ? t('collapseAll', 'Collapse all') : t('expandAll', 'Expand all')}</span>
147
+ </Button>
148
+ )}
123
149
  </div>
124
150
  {filterParent}
125
151
  </div>
@@ -134,11 +160,11 @@ const FilterNode = ({ root, level, open }: FilterNodeProps) => {
134
160
 
135
161
  // Determine if this is a parent (has children) or leaf (individual test)
136
162
  const isParent = parents[root.flatName] && parents[root.flatName].length > 0;
137
- const isLeaf = !isParent;
138
163
 
139
164
  return (
140
165
  <Accordion align="start" size={tablet ? 'md' : 'sm'}>
141
166
  <AccordionItem
167
+ disabled={!root.hasData}
142
168
  title={
143
169
  <Checkbox
144
170
  id={root?.flatName}
@@ -156,11 +182,8 @@ const FilterNode = ({ root, level, open }: FilterNodeProps) => {
156
182
  {/* If this node has obs data, it's a leaf - render as FilterLeaf */}
157
183
  {root?.obs && Array.isArray(root.obs) && root.obs.length > 0 ? (
158
184
  <FilterLeaf leaf={root} />
159
- ) : (
185
+ ) : root?.subSets && Array.isArray(root.subSets) && root.subSets.length > 0 ? (
160
186
  /* If this node has subSets, it's a parent - recursively render children */
161
- root?.subSets &&
162
- Array.isArray(root.subSets) &&
163
- root.subSets.length > 0 &&
164
187
  root.subSets.map((child, index) => {
165
188
  /* Check if child is a leaf (has obs) or parent (has subSets) */
166
189
  if (child?.obs && Array.isArray(child.obs) && child.obs.length > 0) {
@@ -174,6 +197,9 @@ const FilterNode = ({ root, level, open }: FilterNodeProps) => {
174
197
  return <FilterLeaf leaf={child} key={index} />;
175
198
  }
176
199
  })
200
+ ) : (
201
+ /* Parent node with empty subSets - render empty div to maintain accordion structure */
202
+ <div />
177
203
  )}
178
204
  </AccordionItem>
179
205
  </Accordion>
@@ -182,6 +208,7 @@ const FilterNode = ({ root, level, open }: FilterNodeProps) => {
182
208
 
183
209
  const FilterLeaf = ({ leaf }: FilterLeafProps) => {
184
210
  const { checkboxes, toggleVal } = useContext(FilterContext);
211
+
185
212
  return (
186
213
  <div className={styles.filterItem}>
187
214
  <Checkbox