@kenyaemr/esm-admin-app 5.4.4-pre.34 → 5.4.4-pre.340

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 (332) hide show
  1. package/.turbo/turbo-build.log +5 -12
  2. package/dist/100.js +1 -0
  3. package/dist/100.js.map +1 -0
  4. package/dist/1074.js +1 -0
  5. package/dist/1074.js.map +1 -0
  6. package/dist/1242.js +1 -0
  7. package/dist/1242.js.map +1 -0
  8. package/dist/1311.js +1 -0
  9. package/dist/1311.js.map +1 -0
  10. package/dist/1442.js +1 -0
  11. package/dist/1442.js.map +1 -0
  12. package/dist/1462.js +1 -0
  13. package/dist/1462.js.map +1 -0
  14. package/dist/1469.js +1 -0
  15. package/dist/1469.js.map +1 -0
  16. package/dist/1506.js +13 -0
  17. package/dist/1506.js.map +1 -0
  18. package/dist/1718.js +1 -0
  19. package/dist/1718.js.map +1 -0
  20. package/dist/1722.js +1 -0
  21. package/dist/1722.js.map +1 -0
  22. package/dist/1772.js +1 -0
  23. package/dist/1772.js.map +1 -0
  24. package/dist/1889.js +1 -0
  25. package/dist/1889.js.map +1 -0
  26. package/dist/1972.js +1 -0
  27. package/dist/1972.js.map +1 -0
  28. package/dist/1990.js +1 -0
  29. package/dist/1990.js.map +1 -0
  30. package/dist/2016.js +1 -0
  31. package/dist/2016.js.map +1 -0
  32. package/dist/2080.js +1 -0
  33. package/dist/2080.js.map +1 -0
  34. package/dist/2096.js +1 -0
  35. package/dist/2096.js.map +1 -0
  36. package/dist/2153.js +1 -0
  37. package/dist/2153.js.map +1 -0
  38. package/dist/216.js +1 -0
  39. package/dist/216.js.map +1 -0
  40. package/dist/2270.js +1 -0
  41. package/dist/2270.js.map +1 -0
  42. package/dist/2294.js +1 -0
  43. package/dist/2294.js.map +1 -0
  44. package/dist/2345.js +1 -0
  45. package/dist/2345.js.map +1 -0
  46. package/dist/2467.js +1 -0
  47. package/dist/2467.js.map +1 -0
  48. package/dist/2500.js +1 -0
  49. package/dist/2500.js.map +1 -0
  50. package/dist/251.js +1 -0
  51. package/dist/251.js.map +1 -0
  52. package/dist/257.js +1 -0
  53. package/dist/257.js.map +1 -0
  54. package/dist/2586.js +1 -0
  55. package/dist/2586.js.map +1 -0
  56. package/dist/2625.js +1 -0
  57. package/dist/2625.js.map +1 -0
  58. package/dist/2652.js +1 -0
  59. package/dist/2652.js.map +1 -0
  60. package/dist/2685.js +1 -0
  61. package/dist/2685.js.map +1 -0
  62. package/dist/2948.js +1 -0
  63. package/dist/2948.js.map +1 -0
  64. package/dist/3089.js +1 -0
  65. package/dist/3089.js.map +1 -0
  66. package/dist/3190.js +1 -0
  67. package/dist/3190.js.map +1 -0
  68. package/dist/3224.js +1 -0
  69. package/dist/3224.js.map +1 -0
  70. package/dist/3380.js +1 -0
  71. package/dist/3380.js.map +1 -0
  72. package/dist/3548.js +1 -0
  73. package/dist/3548.js.map +1 -0
  74. package/dist/3571.js +1 -0
  75. package/dist/3571.js.map +1 -0
  76. package/dist/3691.js +1 -0
  77. package/dist/3691.js.map +1 -0
  78. package/dist/3775.js +1 -0
  79. package/dist/3775.js.map +1 -0
  80. package/dist/3816.js +1 -0
  81. package/dist/3816.js.map +1 -0
  82. package/dist/3906.js +1 -0
  83. package/dist/3906.js.map +1 -0
  84. package/dist/3963.js +1 -0
  85. package/dist/3963.js.map +1 -0
  86. package/dist/405.js +1 -0
  87. package/dist/405.js.map +1 -0
  88. package/dist/4296.js +1 -0
  89. package/dist/4296.js.map +1 -0
  90. package/dist/4337.js +1 -0
  91. package/dist/4337.js.map +1 -0
  92. package/dist/4584.js +1 -0
  93. package/dist/4584.js.map +1 -0
  94. package/dist/4687.js +1 -0
  95. package/dist/4687.js.map +1 -0
  96. package/dist/4735.js +1 -0
  97. package/dist/4735.js.map +1 -0
  98. package/dist/4744.js +1 -0
  99. package/dist/4744.js.map +1 -0
  100. package/dist/4813.js +2 -0
  101. package/dist/4813.js.map +1 -0
  102. package/dist/4858.js +1 -0
  103. package/dist/4858.js.map +1 -0
  104. package/dist/487.js +1 -0
  105. package/dist/487.js.map +1 -0
  106. package/dist/4970.js +1 -0
  107. package/dist/4970.js.map +1 -0
  108. package/dist/5202.js +1 -0
  109. package/dist/5202.js.map +1 -0
  110. package/dist/5294.js +1 -0
  111. package/dist/5294.js.map +1 -0
  112. package/dist/5297.js +1 -0
  113. package/dist/5297.js.map +1 -0
  114. package/dist/545.js +1 -0
  115. package/dist/545.js.map +1 -0
  116. package/dist/5592.js +1 -0
  117. package/dist/5592.js.map +1 -0
  118. package/dist/5669.js +1 -0
  119. package/dist/5669.js.map +1 -0
  120. package/dist/5884.js +1 -0
  121. package/dist/5884.js.map +1 -0
  122. package/dist/5910.js +1 -0
  123. package/dist/5910.js.map +1 -0
  124. package/dist/5940.js +1 -0
  125. package/dist/5940.js.map +1 -0
  126. package/dist/6155.js +1 -0
  127. package/dist/6155.js.map +1 -0
  128. package/dist/6178.js +1 -0
  129. package/dist/6178.js.map +1 -0
  130. package/dist/6253.js +1 -0
  131. package/dist/6253.js.map +1 -0
  132. package/dist/6455.js +1 -0
  133. package/dist/6455.js.map +1 -0
  134. package/dist/6456.js +1 -0
  135. package/dist/6466.js +3 -0
  136. package/dist/6466.js.map +1 -0
  137. package/dist/6492.js +1 -0
  138. package/dist/6492.js.map +1 -0
  139. package/dist/6800.js +1 -0
  140. package/dist/6800.js.map +1 -0
  141. package/dist/6925.js +1 -0
  142. package/dist/6925.js.map +1 -0
  143. package/dist/7005.js +1 -0
  144. package/dist/7005.js.map +1 -0
  145. package/dist/7201.js +1 -0
  146. package/dist/7201.js.map +1 -0
  147. package/dist/7210.js +1 -0
  148. package/dist/7210.js.map +1 -0
  149. package/dist/7234.js +1 -0
  150. package/dist/7234.js.map +1 -0
  151. package/dist/7261.js +1 -0
  152. package/dist/7261.js.map +1 -0
  153. package/dist/7326.js +1 -0
  154. package/dist/7463.js +1 -0
  155. package/dist/7463.js.map +1 -0
  156. package/dist/7528.js +1 -0
  157. package/dist/7528.js.map +1 -0
  158. package/dist/7607.js +1 -0
  159. package/dist/7717.js +1 -0
  160. package/dist/7717.js.map +1 -0
  161. package/dist/7737.js +1 -0
  162. package/dist/7737.js.map +1 -0
  163. package/dist/7739.js +1 -0
  164. package/dist/7739.js.map +1 -0
  165. package/dist/7765.js +1 -0
  166. package/dist/7765.js.map +1 -0
  167. package/dist/7820.js +1 -0
  168. package/dist/7820.js.map +1 -0
  169. package/dist/7844.js +1 -0
  170. package/dist/7844.js.map +1 -0
  171. package/dist/7866.js +1 -0
  172. package/dist/7866.js.map +1 -0
  173. package/dist/7971.js +1 -0
  174. package/dist/7971.js.map +1 -0
  175. package/dist/8159.js +7 -0
  176. package/dist/8159.js.map +1 -0
  177. package/dist/8206.js +1 -0
  178. package/dist/8206.js.map +1 -0
  179. package/dist/8244.js +1 -0
  180. package/dist/8244.js.map +1 -0
  181. package/dist/8262.js +1 -0
  182. package/dist/8262.js.map +1 -0
  183. package/dist/8376.js +1 -0
  184. package/dist/8376.js.map +1 -0
  185. package/dist/845.js +1 -0
  186. package/dist/845.js.map +1 -0
  187. package/dist/846.js +17 -0
  188. package/dist/846.js.map +1 -0
  189. package/dist/8487.js +1 -0
  190. package/dist/8487.js.map +1 -0
  191. package/dist/8528.js +1 -0
  192. package/dist/8528.js.map +1 -0
  193. package/dist/8570.js +1 -0
  194. package/dist/8570.js.map +1 -0
  195. package/dist/87.js +1 -0
  196. package/dist/87.js.map +1 -0
  197. package/dist/8727.js +1 -0
  198. package/dist/8828.js +1 -0
  199. package/dist/8828.js.map +1 -0
  200. package/dist/8860.js +1 -0
  201. package/dist/8860.js.map +1 -0
  202. package/dist/9036.js +1 -0
  203. package/dist/9036.js.map +1 -0
  204. package/dist/9124.js +1 -0
  205. package/dist/9124.js.map +1 -0
  206. package/dist/9182.js +1 -0
  207. package/dist/921.js +1 -0
  208. package/dist/921.js.map +1 -0
  209. package/dist/9404.js +1 -0
  210. package/dist/9404.js.map +1 -0
  211. package/dist/9446.js +1 -0
  212. package/dist/9446.js.map +1 -0
  213. package/dist/9449.js +1 -0
  214. package/dist/9449.js.map +1 -0
  215. package/dist/9566.js +5 -0
  216. package/dist/9566.js.map +1 -0
  217. package/dist/9585.js +1 -0
  218. package/dist/9585.js.map +1 -0
  219. package/dist/9641.js +1 -0
  220. package/dist/9641.js.map +1 -0
  221. package/dist/9647.js +1 -0
  222. package/dist/9647.js.map +1 -0
  223. package/dist/9801.js +1 -0
  224. package/dist/9801.js.map +1 -0
  225. package/dist/9835.js +11 -0
  226. package/dist/9835.js.map +1 -0
  227. package/dist/kenyaemr-esm-admin-app.js +5 -5
  228. package/dist/kenyaemr-esm-admin-app.js.buildmanifest.json +2672 -154
  229. package/dist/kenyaemr-esm-admin-app.js.map +1 -1
  230. package/dist/main.js +5 -31
  231. package/dist/main.js.map +1 -1
  232. package/dist/routes.json +1 -1
  233. package/package.json +5 -7
  234. package/rspack.config.js +1 -1
  235. package/src/components/confirm-modal/confirmation-operation.test.tsx +8 -19
  236. package/src/components/dashboard/dashboard.component.tsx +4 -1
  237. package/src/components/empty-state/empty-state-log.test.tsx +3 -4
  238. package/src/components/facility-setup/constant/index.ts +3 -0
  239. package/src/components/facility-setup/facility-info.component.tsx +247 -108
  240. package/src/components/facility-setup/facility-info.scss +136 -55
  241. package/src/components/facility-setup/facility-setup.component.tsx +2 -2
  242. package/src/components/facility-setup/header/header.component.tsx +4 -10
  243. package/src/components/facility-setup/header/header.scss +3 -9
  244. package/src/components/facility-setup/shared/custom-info.component.tsx +9 -0
  245. package/src/components/facility-setup/shared/custom-section-card.component.tsx +10 -0
  246. package/src/components/facility-setup/shared/custom-status-tag.component.tsx +22 -0
  247. package/src/components/facility-setup/type/index.ts +61 -0
  248. package/src/components/facility-setup/useFacilityRegistry.ts +29 -0
  249. package/src/components/global-property/dashboard/global-property-dashboard.component.tsx +23 -0
  250. package/src/components/global-property/dashboard/global-property-dashboard.scss +6 -0
  251. package/src/components/global-property/hooks/useGlobalProperty.ts +64 -0
  252. package/src/components/global-property/index.ts +14 -0
  253. package/src/components/global-property/modal/delete-global-property-modal.component.tsx +71 -0
  254. package/src/components/global-property/modal/delete-global-property-modal.test.tsx +131 -0
  255. package/src/components/global-property/table/global-property-table.component.tsx +249 -0
  256. package/src/components/global-property/table/global-property-table.scss +34 -0
  257. package/src/components/global-property/table/global-property-table.test.tsx +198 -0
  258. package/src/components/global-property/workspace/global-property-form-schema.ts +32 -0
  259. package/src/components/global-property/workspace/global-property.workspace.scss +40 -0
  260. package/src/components/global-property/workspace/global-property.workspace.test.tsx +172 -0
  261. package/src/components/global-property/workspace/global-property.workspace.tsx +260 -0
  262. package/src/components/hook/healthWorkerRegistry.ts +78 -0
  263. package/src/components/hook/useProfessionalRegistryEnums.ts +59 -0
  264. package/src/components/locations/forms/add-location/add-location.workspace.tsx +96 -95
  265. package/src/components/locations/forms/search-location/search-location.workspace.tsx +90 -85
  266. package/src/components/locations/tables/locations-table.component.tsx +117 -121
  267. package/src/components/logs-table/operation-log-table.component.tsx +87 -75
  268. package/src/components/logs-table/operation-log.test.tsx +134 -28
  269. package/src/components/modal/hwr-confirmation.modal.scss +80 -4
  270. package/src/components/modal/hwr-confirmation.modal.tsx +118 -128
  271. package/src/components/modal/hwr-sync.modal.tsx +194 -106
  272. package/src/components/users/manage-users/manage-user-role-scope/user-role-scope-workspace/user-role-scope.workspace.tsx +13 -13
  273. package/src/components/users/manage-users/user-details/user-detail.scss +167 -39
  274. package/src/components/users/manage-users/user-details/user-details.component.tsx +130 -122
  275. package/src/components/users/manage-users/user-list/user-list.component.tsx +22 -9
  276. package/src/components/users/manage-users/user-management.workspace.scss +233 -95
  277. package/src/components/users/manage-users/user-management.workspace.tsx +800 -687
  278. package/src/components/users/userManagementFormSchema.tsx +17 -8
  279. package/src/config-schema.ts +48 -68
  280. package/src/index.ts +64 -31
  281. package/src/left-pannel-link.component.tsx +5 -3
  282. package/src/root.component.tsx +13 -13
  283. package/src/routes.json +57 -38
  284. package/src/types/index.ts +40 -3
  285. package/translations/am.json +196 -13
  286. package/translations/en.json +207 -24
  287. package/translations/fr.json +243 -58
  288. package/translations/sw.json +312 -129
  289. package/tsconfig.json +1 -1
  290. package/dist/127.js +0 -1
  291. package/dist/267.js +0 -1
  292. package/dist/267.js.map +0 -1
  293. package/dist/281.js +0 -15
  294. package/dist/281.js.map +0 -1
  295. package/dist/329.js +0 -1
  296. package/dist/329.js.map +0 -1
  297. package/dist/40.js +0 -1
  298. package/dist/466.js +0 -1
  299. package/dist/466.js.map +0 -1
  300. package/dist/472.js +0 -1
  301. package/dist/472.js.map +0 -1
  302. package/dist/478.js +0 -1
  303. package/dist/478.js.map +0 -1
  304. package/dist/585.js +0 -1
  305. package/dist/585.js.map +0 -1
  306. package/dist/630.js +0 -1
  307. package/dist/630.js.map +0 -1
  308. package/dist/675.js +0 -1
  309. package/dist/675.js.map +0 -1
  310. package/dist/689.js +0 -1
  311. package/dist/689.js.map +0 -1
  312. package/dist/706.js +0 -27
  313. package/dist/706.js.map +0 -1
  314. package/dist/729.js +0 -17
  315. package/dist/729.js.map +0 -1
  316. package/dist/774.js +0 -1
  317. package/dist/774.js.map +0 -1
  318. package/dist/847.js +0 -1
  319. package/dist/847.js.map +0 -1
  320. package/dist/85.js +0 -1
  321. package/dist/85.js.map +0 -1
  322. package/dist/882.js +0 -1
  323. package/dist/91.js +0 -1
  324. package/dist/91.js.map +0 -1
  325. package/dist/916.js +0 -1
  326. package/dist/998.js +0 -1
  327. package/dist/998.js.map +0 -1
  328. package/jest.config.js +0 -8
  329. package/src/components/facility-setup/card.component.tsx +0 -16
  330. package/src/components/facility-setup/facility-setup.resource.tsx +0 -7
  331. package/src/components/hook/healthWorkerAdapter.ts +0 -213
  332. package/src/components/hook/useFacilityInfo.tsx +0 -37
@@ -1,4 +1,4 @@
1
- import React, { useMemo, useState, useEffect } from 'react';
1
+ import React, { useMemo, useState } from 'react';
2
2
  import { useTranslation } from 'react-i18next';
3
3
  import {
4
4
  Button,
@@ -18,7 +18,7 @@ import {
18
18
  Tag,
19
19
  } from '@carbon/react';
20
20
  import { Add } from '@carbon/react/icons';
21
- import { WorkspaceContainer, isDesktop as desktopLayout, launchWorkspace, useLayoutType } from '@openmrs/esm-framework';
21
+ import { launchWorkspace2, useLayoutType } from '@openmrs/esm-framework';
22
22
  import styles from './locations-table.scss';
23
23
  import { CardHeader } from '@openmrs/esm-patient-common-lib';
24
24
  import { useFacilitiesTagged } from './locations-table.resource';
@@ -28,23 +28,26 @@ const LocationsTable: React.FC = () => {
28
28
  const { t } = useTranslation();
29
29
  const layout = useLayoutType();
30
30
  const isTablet = layout === 'tablet';
31
- const isDesktop = desktopLayout(layout);
32
31
  const [pageSize, setPageSize] = useState(10);
33
32
  const [searchTerm, setSearchTerm] = useState('');
34
33
  const [currentPage, setCurrentPage] = useState(1);
35
34
 
36
- const { locationTagList, isLoading: tagsLoading, error: tagsError } = useLocationTags();
35
+ const { locationTagList, isLoading: tagsLoading } = useLocationTags();
37
36
  const { facilityList, isLoading: taggedLoading } = useFacilitiesTagged({ results: locationTagList });
38
37
 
39
38
  const handleAddLocationWorkspace = () => {
40
- launchWorkspace('add-location-workspace', {
41
- workspaceTitle: t('addLocation', 'Add Location'),
39
+ launchWorkspace2('add-location-workspace', {
40
+ workspaceProps: {
41
+ workspaceTitle: t('addLocation', 'Add Location'),
42
+ },
42
43
  });
43
44
  };
44
45
 
45
46
  const handleSearchLocationWorkspace = () => {
46
- launchWorkspace('search-location-workspace', {
47
- workspaceTitle: t('tagLocation', 'Tag Location'),
47
+ launchWorkspace2('search-location-workspace', {
48
+ workspaceProps: {
49
+ workspaceTitle: t('tagLocation', 'Tag Location'),
50
+ },
48
51
  });
49
52
  };
50
53
 
@@ -117,126 +120,119 @@ const LocationsTable: React.FC = () => {
117
120
 
118
121
  if (tagsLoading || (taggedLoading && !facilityList.length)) {
119
122
  return (
120
- <>
121
- <div className={styles.widgetCard}>
122
- <DataTableSkeleton role="progressbar" compact={isDesktop} zebra />
123
- </div>
124
- </>
123
+ <div className={styles.widgetCard}>
124
+ <DataTableSkeleton role="progressbar" zebra />
125
+ </div>
125
126
  );
126
127
  }
127
128
 
128
129
  return (
129
- <>
130
- <div className={styles.widgetCard}>
131
- <CardHeader title={t('locations', 'Locations')}>
132
- <span className={styles.backgroundDataFetchingIndicator}>
133
- <span>{tagsLoading ? <InlineLoading /> : null}</span>
134
- </span>
135
- <div className={styles.headerActions}>
136
- <>
137
- <div className={styles.filterContainer}>
138
- <Search
139
- onChange={(e) => {
140
- setSearchTerm(e.target.value);
141
- setCurrentPage(1);
142
- }}
143
- placeholder={t('search', 'Search location')}
144
- value={searchTerm}
145
- labelText={t('search', 'Search location')}
146
- />
147
- </div>
148
- <Button
149
- kind="ghost"
150
- renderIcon={(props) => <Add size={16} {...props} />}
151
- onClick={handleAddLocationWorkspace}>
152
- {t('addLocation', 'Add Location')}
153
- </Button>
154
- <Button
155
- kind="ghost"
156
- renderIcon={(props) => <Add size={16} {...props} />}
157
- onClick={handleSearchLocationWorkspace}>
158
- {t('tagLocation', 'Tag Location')}
159
- </Button>
160
- </>
130
+ <div className={styles.widgetCard}>
131
+ <CardHeader title={t('locations', 'Locations')}>
132
+ <span className={styles.backgroundDataFetchingIndicator}>
133
+ <span>{tagsLoading ? <InlineLoading /> : null}</span>
134
+ </span>
135
+ <div className={styles.headerActions}>
136
+ <div className={styles.filterContainer}>
137
+ <Search
138
+ onChange={(e) => {
139
+ setSearchTerm(e.target.value);
140
+ setCurrentPage(1);
141
+ }}
142
+ placeholder={t('search', 'Search location')}
143
+ value={searchTerm}
144
+ labelText={t('search', 'Search location')}
145
+ />
161
146
  </div>
162
- </CardHeader>
163
-
164
- <DataTable rows={paginatedRows} headers={tableHeaders} isSortable size={isTablet ? 'lg' : 'sm'} useZebraStyles>
165
- {({ rows, headers, getTableProps }) => {
166
- return (
167
- <TableContainer>
168
- <Table {...getTableProps()}>
169
- <TableHead>
170
- <TableRow>
171
- {headers.map((header) => (
172
- <TableHeader key={header.key}>{header.header}</TableHeader>
147
+ <Button
148
+ kind="ghost"
149
+ renderIcon={(props) => <Add size={16} {...props} />}
150
+ onClick={handleAddLocationWorkspace}>
151
+ {t('addLocation', 'Add Location')}
152
+ </Button>
153
+ <Button
154
+ kind="ghost"
155
+ renderIcon={(props) => <Add size={16} {...props} />}
156
+ onClick={handleSearchLocationWorkspace}>
157
+ {t('tagLocation', 'Tag Location')}
158
+ </Button>
159
+ </div>
160
+ </CardHeader>
161
+
162
+ <DataTable rows={paginatedRows} headers={tableHeaders} isSortable size={isTablet ? 'lg' : 'sm'} useZebraStyles>
163
+ {({ rows, headers, getTableProps }) => {
164
+ return (
165
+ <TableContainer>
166
+ <Table {...getTableProps()}>
167
+ <TableHead>
168
+ <TableRow>
169
+ {headers.map((header) => (
170
+ <TableHeader key={header.key}>{header.header}</TableHeader>
171
+ ))}
172
+ </TableRow>
173
+ </TableHead>
174
+ <TableBody>
175
+ {rows.map((row) => (
176
+ <TableRow key={row.id}>
177
+ {row.cells.map((cell) => (
178
+ <TableCell key={cell.id}>
179
+ {cell.id.includes('tags') && cell.value !== 'No tags' ? (
180
+ <div style={{ display: 'flex', flexWrap: 'wrap', gap: '4px' }}>
181
+ {cell.value.split(', ').map((tag, index) => (
182
+ <Tag key={index} type="blue" size="sm">
183
+ {tag}
184
+ </Tag>
185
+ ))}
186
+ </div>
187
+ ) : (
188
+ cell.value
189
+ )}
190
+ </TableCell>
173
191
  ))}
174
192
  </TableRow>
175
- </TableHead>
176
- <TableBody>
177
- {rows.map((row) => (
178
- <TableRow key={row.id}>
179
- {row.cells.map((cell) => (
180
- <TableCell key={cell.id}>
181
- {cell.id.includes('tags') && cell.value !== 'No tags' ? (
182
- <div style={{ display: 'flex', flexWrap: 'wrap', gap: '4px' }}>
183
- {cell.value.split(', ').map((tag, index) => (
184
- <Tag key={index} type="blue" size="sm">
185
- {tag}
186
- </Tag>
187
- ))}
188
- </div>
189
- ) : (
190
- cell.value
191
- )}
192
- </TableCell>
193
- ))}
194
- </TableRow>
195
- ))}
196
- </TableBody>
197
- </Table>
198
- {rows.length === 0 ? (
199
- <div className={styles.tileContainer}>
200
- <Tile className={styles.tile}>
201
- <div className={styles.tileContent}>
202
- <p className={styles.content}>
203
- {searchTerm
204
- ? t('noSearchResults', `No results found for "${searchTerm}"`)
205
- : t('noData', 'No data to display')}
206
- </p>
207
- <p className={styles.helper}>
208
- {searchTerm
209
- ? t('tryDifferentSearch', 'Try a different search term')
210
- : t('checkFilters', 'Check the filters above')}
211
- </p>
212
- </div>
213
- </Tile>
214
- </div>
215
- ) : null}
216
- <Pagination
217
- backwardText="Previous page"
218
- forwardText="Next page"
219
- page={currentPage}
220
- pageSize={pageSize}
221
- pageSizes={[10, 20, 30, 40, 50]}
222
- totalItems={filteredRows.length}
223
- onChange={({ pageSize: newPageSize, page }) => {
224
- if (newPageSize !== pageSize) {
225
- setPageSize(newPageSize);
226
- setCurrentPage(1);
227
- }
228
- if (page !== currentPage) {
229
- setCurrentPage(page);
230
- }
231
- }}
232
- />
233
- </TableContainer>
234
- );
235
- }}
236
- </DataTable>
237
- </div>
238
- <WorkspaceContainer contextKey="locations" />
239
- </>
193
+ ))}
194
+ </TableBody>
195
+ </Table>
196
+ {rows.length === 0 ? (
197
+ <div className={styles.tileContainer}>
198
+ <Tile className={styles.tile}>
199
+ <div className={styles.tileContent}>
200
+ <p className={styles.content}>
201
+ {searchTerm
202
+ ? t('noSearchResults', `No results found for "${searchTerm}"`)
203
+ : t('noData', 'No data to display')}
204
+ </p>
205
+ <p className={styles.helper}>
206
+ {searchTerm
207
+ ? t('tryDifferentSearch', 'Try a different search term')
208
+ : t('checkFilters', 'Check the filters above')}
209
+ </p>
210
+ </div>
211
+ </Tile>
212
+ </div>
213
+ ) : null}
214
+ <Pagination
215
+ backwardText="Previous page"
216
+ forwardText="Next page"
217
+ page={currentPage}
218
+ pageSize={pageSize}
219
+ pageSizes={[10, 20, 30, 40, 50]}
220
+ totalItems={filteredRows.length}
221
+ onChange={({ pageSize: newPageSize, page }) => {
222
+ if (newPageSize !== pageSize) {
223
+ setPageSize(newPageSize);
224
+ setCurrentPage(1);
225
+ }
226
+ if (page !== currentPage) {
227
+ setCurrentPage(page);
228
+ }
229
+ }}
230
+ />
231
+ </TableContainer>
232
+ );
233
+ }}
234
+ </DataTable>
235
+ </div>
240
236
  );
241
237
  };
242
238
 
@@ -12,7 +12,6 @@ import {
12
12
  TableRow,
13
13
  Tag,
14
14
  DataTableSkeleton,
15
- InlineLoading,
16
15
  } from '@carbon/react';
17
16
  import { CardHeader, PatientChartPagination } from '@openmrs/esm-patient-common-lib';
18
17
  import { useLayoutType, usePagination, formatDate } from '@openmrs/esm-framework';
@@ -21,13 +20,14 @@ import { ETLResponse } from '../../types';
21
20
  import EmptyState from '../empty-state/empty-state-log.components';
22
21
 
23
22
  interface LogTableProps {
24
- logData: ETLResponse[];
23
+ logData: Array<ETLResponse>;
25
24
  isLoading: boolean;
26
25
  }
27
26
 
27
+ const PAGE_SIZE = 10;
28
+
28
29
  const LogTable: React.FC<LogTableProps> = ({ logData, isLoading }) => {
29
30
  const { t } = useTranslation();
30
- const pageSize = 10;
31
31
  const isTablet = useLayoutType() === 'tablet';
32
32
 
33
33
  const headers = [
@@ -37,82 +37,94 @@ const LogTable: React.FC<LogTableProps> = ({ logData, isLoading }) => {
37
37
  { header: t('completionStatus', 'Completion status'), key: 'status' },
38
38
  ];
39
39
 
40
- const rows = logData?.map((item, index) => ({
41
- id: index.toString(),
42
- script_name: item.script_name,
43
- start_time: item?.start_time ? formatDate(new Date(item?.start_time)) : '--',
44
- stop_time: item?.stop_time ? formatDate(new Date(item?.stop_time)) : '--',
45
- status: item.status,
46
- }));
40
+ const rows =
41
+ logData?.map((item, index) => ({
42
+ id: index.toString(),
43
+ script_name: item.script_name,
44
+ start_time: item?.start_time ? formatDate(new Date(item.start_time)) : '--',
45
+ stop_time: item?.stop_time ? formatDate(new Date(item.stop_time)) : '--',
46
+ status: item.status,
47
+ })) ?? [];
48
+
49
+ const { results: paginatedData, currentPage, goTo } = usePagination(rows, PAGE_SIZE);
50
+
51
+ const renderTable = ({ rows, headers, getHeaderProps, getTableProps }: any) => (
52
+ <TableContainer className={styles.tableContainer}>
53
+ <Table {...getTableProps()}>
54
+ <TableHead>
55
+ <TableRow>
56
+ {headers.map((header: any) => (
57
+ <TableHeader
58
+ className={classNames(styles.productiveHeading01, styles.text02)}
59
+ {...getHeaderProps({ header })}>
60
+ {header.header}
61
+ </TableHeader>
62
+ ))}
63
+ </TableRow>
64
+ </TableHead>
65
+ <TableBody>
66
+ {rows.map((row: any) => (
67
+ <TableRow key={row.id}>
68
+ {row.cells.map((cell: any) => (
69
+ <TableCell key={cell.id}>
70
+ {cell.info.header === 'status' ? (
71
+ <Tag size="md" type={cell.value === 'Success' ? 'green' : 'red'}>
72
+ {cell.value}
73
+ </Tag>
74
+ ) : (
75
+ cell.value
76
+ )}
77
+ </TableCell>
78
+ ))}
79
+ </TableRow>
80
+ ))}
81
+ </TableBody>
82
+ </Table>
83
+ </TableContainer>
84
+ );
85
+
86
+ const renderContent = () => {
87
+ if (isLoading && logData.length === 0) {
88
+ return (
89
+ <DataTableSkeleton
90
+ headers={headers}
91
+ aria-label="etl table"
92
+ showToolbar={false}
93
+ showHeader={false}
94
+ rowCount={4}
95
+ zebra
96
+ columnCount={3}
97
+ className={styles.dataTableSkeleton}
98
+ />
99
+ );
100
+ }
101
+
102
+ if (logData.length === 0) {
103
+ return <EmptyState subTitle={t('noRecordsFound', 'No ETL Operation logs found')} />;
104
+ }
47
105
 
48
- const { results: paginatedData, currentPage, goTo } = usePagination(rows, pageSize);
106
+ return (
107
+ <>
108
+ <DataTable rows={paginatedData} headers={headers} isSortable size={isTablet ? 'lg' : 'sm'} useZebraStyles>
109
+ {renderTable}
110
+ </DataTable>
111
+ <PatientChartPagination
112
+ currentItems={paginatedData.length}
113
+ totalItems={rows.length}
114
+ onPageNumberChange={({ page }: { page: number }) => goTo(page)}
115
+ pageNumber={currentPage}
116
+ pageSize={PAGE_SIZE}
117
+ />
118
+ </>
119
+ );
120
+ };
49
121
 
50
122
  return (
51
123
  <div className={styles.table}>
52
- <CardHeader title={t('facilityInfo', 'Facility Info')} children={''} />
53
- <div className={styles.logTable}>
54
- {isLoading && logData.length === 0 ? (
55
- <DataTableSkeleton
56
- headers={headers}
57
- aria-label="etl table"
58
- showToolbar={false}
59
- showHeader={false}
60
- rowCount={4}
61
- zebra
62
- columnCount={3}
63
- className={styles.dataTableSkeleton}
64
- />
65
- ) : logData.length === 0 ? (
66
- <EmptyState subTitle={t('noRecordsFound', 'No ETL Operation logs found')} />
67
- ) : (
68
- <>
69
- <DataTable rows={paginatedData} headers={headers} isSortable size={isTablet ? 'lg' : 'sm'} useZebraStyles>
70
- {({ rows, headers, getHeaderProps, getTableProps }) => (
71
- <TableContainer className={styles.tableContainer}>
72
- <Table {...getTableProps()}>
73
- <TableHead>
74
- <TableRow>
75
- {headers.map((header) => (
76
- <TableHeader
77
- key={header.key}
78
- className={classNames(styles.productiveHeading01, styles.text02)}
79
- {...getHeaderProps({ header })}>
80
- {header.header}
81
- </TableHeader>
82
- ))}
83
- </TableRow>
84
- </TableHead>
85
- <TableBody>
86
- {rows.map((row) => (
87
- <TableRow key={row.id}>
88
- {row.cells.map((cell) => (
89
- <TableCell key={cell.id}>
90
- {cell.info.header === 'status' ? (
91
- <Tag size="md" type={cell.value === 'Success' ? 'green' : 'red'}>
92
- {cell.value}
93
- </Tag>
94
- ) : (
95
- cell.value
96
- )}
97
- </TableCell>
98
- ))}
99
- </TableRow>
100
- ))}
101
- </TableBody>
102
- </Table>
103
- </TableContainer>
104
- )}
105
- </DataTable>
106
- <PatientChartPagination
107
- currentItems={paginatedData.length}
108
- totalItems={rows.length}
109
- onPageNumberChange={({ page }) => goTo(page)}
110
- pageNumber={currentPage}
111
- pageSize={pageSize}
112
- />
113
- </>
114
- )}
115
- </div>
124
+ <CardHeader title={t('facilityInfo', 'Facility Info')}>
125
+ <></>
126
+ </CardHeader>
127
+ <div className={styles.logTable}>{renderContent()}</div>
116
128
  </div>
117
129
  );
118
130
  };
@@ -1,47 +1,153 @@
1
1
  import React from 'react';
2
2
  import { render, screen } from '@testing-library/react';
3
3
  import '@testing-library/jest-dom';
4
+ import { vi, describe, it, expect, beforeEach } from 'vitest';
4
5
  import LogTable from './operation-log-table.component';
5
6
  import { ETLResponse } from '../../types';
6
7
 
7
- describe('LogTable Component', () => {
8
- const mockLogData: ETLResponse[] = [
9
- {
10
- script_name: 'Initial Population of Tables',
11
- start_time: '2024-11-28T09:30:12',
12
- stop_time: '2024-11-28T09:33:15',
13
- status: 'Success',
14
- },
15
- {
16
- script_name: 'Initial Creation of Tables',
17
- start_time: '2024-11-28T09:30:12',
18
- stop_time: '2024-11-28T09:33:15',
19
- status: 'Failed',
8
+ vi.mock('react-i18next', () => ({
9
+ useTranslation: () => ({ t: (key: string, fallback: string) => fallback }),
10
+ }));
11
+
12
+ vi.mock('@openmrs/esm-framework', () => ({
13
+ useLayoutType: vi.fn(() => 'desktop'),
14
+ usePagination: vi.fn((data) => ({
15
+ results: data,
16
+ currentPage: 1,
17
+ goTo: vi.fn(),
18
+ })),
19
+ formatDate: vi.fn((date: Date) => date.toISOString()),
20
+ }));
21
+
22
+ vi.mock('@openmrs/esm-patient-common-lib', () => ({
23
+ CardHeader: ({ title }: { title: string }) => <div>{title}</div>,
24
+ PatientChartPagination: ({ currentItems, totalItems }: { currentItems: number; totalItems: number }) => (
25
+ <div data-testid="pagination">
26
+ {currentItems}/{totalItems}
27
+ </div>
28
+ ),
29
+ }));
30
+
31
+ vi.mock('../empty-state/empty-state-log.components', () => ({
32
+ default: ({ subTitle }: { subTitle: string }) => <div data-testid="empty-state">{subTitle}</div>,
33
+ }));
34
+
35
+ vi.mock('@carbon/react', async (importOriginal) => {
36
+ const original = await importOriginal<typeof import('@carbon/react')>();
37
+ return {
38
+ ...original,
39
+ DataTable: ({
40
+ children,
41
+ rows,
42
+ headers,
43
+ }: {
44
+ children: (props: any) => React.ReactNode;
45
+ rows: any[];
46
+ headers: any[];
47
+ }) => {
48
+ if (typeof children === 'function') {
49
+ const renderRows = rows.map((row) => ({
50
+ ...row,
51
+ cells: headers.map((h) => ({
52
+ id: `${row.id}:${h.key}`,
53
+ value: row[h.key],
54
+ info: { header: h.key },
55
+ })),
56
+ }));
57
+ return children({
58
+ rows: renderRows,
59
+ headers,
60
+ getTableProps: () => ({}),
61
+ getHeaderProps: ({ header }: any) => ({ key: header.key }),
62
+ getRowProps: () => ({}),
63
+ });
64
+ }
65
+ return children;
20
66
  },
21
- ];
67
+ };
68
+ });
22
69
 
23
- it('renders the table with correct headers and rows', () => {
24
- render(<LogTable logData={mockLogData} isLoading={false} />);
70
+ vi.mock('classnames', () => ({ default: (...args: string[]) => args.filter(Boolean).join(' ') }));
25
71
 
26
- const headers = ['Procedure', 'Start time', 'End time', 'Completion status'];
27
- headers.forEach((header) => {
28
- expect(screen.getByText(header)).toBeInTheDocument();
29
- });
72
+ vi.mock('./operation-log.scss', () => ({ default: {} }));
30
73
 
31
- const rows = screen.getAllByRole('row');
32
- expect(rows.length).toBe(3);
74
+ const mockLogData: ETLResponse[] = [
75
+ {
76
+ script_name: 'Script A',
77
+ start_time: '2024-01-01T08:00:00Z',
78
+ stop_time: '2024-01-01T09:00:00Z',
79
+ status: 'Success',
80
+ },
81
+ {
82
+ script_name: 'Script B',
83
+ start_time: '2024-01-02T10:00:00Z',
84
+ stop_time: '2024-01-02T11:00:00Z',
85
+ status: 'Failed',
86
+ },
87
+ ];
88
+
89
+ describe('LogTable', () => {
90
+ beforeEach(() => {
91
+ vi.clearAllMocks();
33
92
  });
34
93
 
35
- it('displays "No ETL Operation logs found" when logData is empty', () => {
36
- render(<LogTable logData={[]} isLoading={false} />);
94
+ it('renders the card header', () => {
95
+ render(<LogTable logData={mockLogData} isLoading={false} />);
96
+ expect(screen.getByText('Facility Info')).toBeInTheDocument();
97
+ });
98
+
99
+ it('shows skeleton while loading with no data', () => {
100
+ render(<LogTable logData={[]} isLoading={true} />);
101
+ expect(screen.getByRole('table')).toBeInTheDocument();
102
+ });
37
103
 
104
+ it('shows empty state when not loading and no data', () => {
105
+ render(<LogTable logData={[]} isLoading={false} />);
106
+ expect(screen.getByTestId('empty-state')).toBeInTheDocument();
38
107
  expect(screen.getByText('No ETL Operation logs found')).toBeInTheDocument();
39
108
  });
40
109
 
41
- it('shows skeleton loading state when isLoading is true', () => {
42
- render(<LogTable logData={[]} isLoading={true} />);
110
+ it('renders table headers when data is present', () => {
111
+ render(<LogTable logData={mockLogData} isLoading={false} />);
112
+ expect(screen.getByText('Procedure')).toBeInTheDocument();
113
+ expect(screen.getByText('Start time')).toBeInTheDocument();
114
+ expect(screen.getByText('End time')).toBeInTheDocument();
115
+ expect(screen.getByText('Completion status')).toBeInTheDocument();
116
+ });
117
+
118
+ it('renders script names in the table', () => {
119
+ render(<LogTable logData={mockLogData} isLoading={false} />);
120
+ expect(screen.getByText('Script A')).toBeInTheDocument();
121
+ expect(screen.getByText('Script B')).toBeInTheDocument();
122
+ });
123
+
124
+ it('renders Success status as a green tag', () => {
125
+ render(<LogTable logData={mockLogData} isLoading={false} />);
126
+ const successTag = screen.getByText('Success');
127
+ expect(successTag).toBeInTheDocument();
128
+ });
129
+
130
+ it('renders Failed status as a red tag', () => {
131
+ render(<LogTable logData={mockLogData} isLoading={false} />);
132
+ const failedTag = screen.getByText('Failed');
133
+ expect(failedTag).toBeInTheDocument();
134
+ });
135
+
136
+ it('renders pagination when data is present', () => {
137
+ render(<LogTable logData={mockLogData} isLoading={false} />);
138
+ expect(screen.getByTestId('pagination')).toBeInTheDocument();
139
+ });
140
+
141
+ it('renders -- for missing start_time', () => {
142
+ const dataWithMissingTime: ETLResponse[] = [
143
+ { script_name: 'Script C', start_time: '', stop_time: '', status: 'Success' },
144
+ ];
145
+ render(<LogTable logData={dataWithMissingTime} isLoading={false} />);
146
+ expect(screen.getAllByText('--').length).toBeGreaterThanOrEqual(2);
147
+ });
43
148
 
44
- const skeleton = screen.getByLabelText('etl table');
45
- expect(skeleton).toBeInTheDocument();
149
+ it('does not show empty state when data is present', () => {
150
+ render(<LogTable logData={mockLogData} isLoading={false} />);
151
+ expect(screen.queryByTestId('empty-state')).not.toBeInTheDocument();
46
152
  });
47
153
  });