@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
package/dist/routes.json CHANGED
@@ -1 +1 @@
1
- {"$schema":"https://json.openmrs.org/routes.schema.json","backendDependencies":{"kenyaemrCharts":"^1.6.7"},"extensions":[{"component":"adminLeftPanelLink","name":"admin-left-panel-link","slot":"admin-left-panel-slot"},{"component":"userManagementLeftPannelLink","name":"user-management-left-panel-link","slot":"admin-left-panel-slot"},{"component":"etlAdministrationLeftPannelLink","name":"etl-administration-left-panel-link","slot":"admin-left-panel-slot"},{"component":"locationsLeftPanelLink","name":"locations-left-panel-link","slot":"admin-left-panel-slot"},{"component":"facilitySetupLeftPanelLink","name":"facility-setup-left-panel-link","slot":"admin-left-panel-slot"},{"component":"providerBanner","name":"provider-banner","slot":"provider-banner-info-slot","order":1}],"workspaces":[{"name":"manage-user-workspace","component":"manageUserWorkspace","title":"Manage User Workspace","type":"other-form","canMaximize":true,"width":"extra-wide"},{"name":"user-role-scope-workspace","component":"userRoleScopeWorkspace","title":"User Rple Scope Workspace","type":"other-form","canMaximize":true,"width":"extra-wide"},{"name":"add-location-workspace","title":"Add Location","component":"addLocation","type":"workspace"},{"name":"search-location-workspace","title":"Search Location","component":"searchLocationWorkspace","type":"workspace"},{"name":"hwr-sync-workspace","title":"HWR Sync Workspace","component":"hwrSyncWorkspace","type":"other-form"},{"name":"hwr-sync-modal","title":"HWR Sync Modal","component":"hwrSyncModal","type":"modal"}],"modals":[{"component":"operationConfirmationModal","name":"operation-confirmation-modal"},{"component":"hwrConfirmationModal","name":"hwr-confirmation-modal"},{"component":"hwrEmptyModal","name":"hwr-empty-modal"},{"component":"hwrSyncModal","name":"hwr-syncing-modal"}],"pages":[{"component":"root","route":"admin"}],"version":"5.4.4-pre.34"}
1
+ {"$schema":"https://json.openmrs.org/routes.schema.json","backendDependencies":{"kenyaemrCharts":"^1.6.7"},"extensions":[{"component":"adminLeftPanelLink","name":"admin-left-panel-link","slot":"admin-left-panel-slot"},{"component":"userManagementLeftPannelLink","name":"user-management-left-panel-link","slot":"admin-left-panel-slot"},{"component":"etlAdministrationLeftPannelLink","name":"etl-administration-left-panel-link","slot":"admin-left-panel-slot"},{"component":"locationsLeftPanelLink","name":"locations-left-panel-link","slot":"admin-left-panel-slot"},{"component":"facilitySetupLeftPanelLink","name":"facility-setup-left-panel-link","slot":"admin-left-panel-slot"},{"name":"global-property-link","component":"globalPropertyLink","slot":"admin-left-panel-slot"},{"component":"providerBanner","name":"provider-banner","slot":"provider-banner-info-slot","order":1},{"component":"globalPropertyDashboard","name":"global-property-dashboard"}],"modals":[{"component":"operationConfirmationModal","name":"operation-confirmation-modal"},{"component":"hwrConfirmationModal","name":"hwr-confirmation-modal"},{"component":"hwrEmptyModal","name":"hwr-empty-modal"},{"component":"hwrSyncModal","name":"hwr-syncing-modal"},{"component":"deleteGlobalPropertyModal","name":"delete-global-property-modal"}],"workspaces2":[{"name":"manage-user-workspace","component":"manageUserWorkspace","window":"esm-admin-extra-wide-workspace-window"},{"name":"user-role-scope-workspace","component":"userRoleScopeWorkspace","window":"esm-admin-workspace-window"},{"name":"add-location-workspace","component":"addLocation","window":"esm-admin-workspace-window"},{"name":"search-location-workspace","component":"searchLocationWorkspace","window":"esm-admin-workspace-window"},{"name":"global-property-workspace","component":"globalPropertyWorkspace","window":"esm-admin-workspace-window"}],"workspaceWindows2":[{"name":"esm-admin-workspace-window","canMaximize":true,"group":"esm-admin-workspace-group","width":"wider"},{"name":"esm-admin-extra-wide-workspace-window","canMaximize":true,"group":"esm-admin-workspace-group","width":"extra-wide"}],"workspaceGroups2":[{"name":"esm-admin-workspace-group","scopePattern":"/admin","closeable":true}],"pages":[{"component":"root","route":"admin"}],"version":"5.4.4-pre.340"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kenyaemr/esm-admin-app",
3
- "version": "5.4.4-pre.34",
3
+ "version": "5.4.4-pre.340",
4
4
  "description": "Facilitates the management of ETL tables",
5
5
  "keywords": [
6
6
  "openmrs"
@@ -18,16 +18,14 @@
18
18
  "source": true,
19
19
  "browser": "dist/kenyaemr-esm-admin-app.js",
20
20
  "scripts": {
21
- "analyze": "rspack --mode=production --env analyze=true",
21
+ "analyze": "rspack build --mode=production --analyze",
22
22
  "build": "rspack --mode=production",
23
- "coverage": "yarn test --coverage",
23
+ "coverage": "echo \"Jest has been removed; no unit test runner is configured\" && exit 0",
24
24
  "debug": "npm run serve",
25
- "extract-translations": "i18next 'src/**/*.component.tsx' 'src/index.ts' --config ../../tools/i18next-parser.config.js",
25
+ "extract-translations": "i18next 'src/**/*.{ts,tsx}' --config ../../tools/i18next-parser.config.js",
26
26
  "lint": "eslint src --ext ts,tsx",
27
27
  "serve": "rspack serve --mode=development",
28
28
  "start": "openmrs develop",
29
- "test": "cross-env TZ=UTC jest --config jest.config.js --verbose false --passWithNoTests",
30
- "test:watch": "cross-env TZ=UTC jest --watch --config jest.config.js",
31
29
  "typescript": "tsc"
32
30
  },
33
31
  "browserslist": [
@@ -39,7 +37,7 @@
39
37
  },
40
38
  "peerDependencies": {
41
39
  "@carbon/react": "1.x",
42
- "@openmrs/esm-framework": "8.x",
40
+ "@openmrs/esm-framework": "9.x",
43
41
  "react": "^18.1.0",
44
42
  "react-i18next": "11.x",
45
43
  "react-router-dom": "6.x",
package/rspack.config.js CHANGED
@@ -1 +1 @@
1
- module.exports = require('openmrs/default-rspack-config');
1
+ module.exports = require('../../tools/rs-pack-code-spliting');
@@ -1,24 +1,13 @@
1
1
  import React from 'react';
2
2
  import { render, screen, fireEvent } from '@testing-library/react';
3
3
  import '@testing-library/jest-dom';
4
- import { useTranslation } from 'react-i18next';
4
+ import { vi, describe, it, expect } from 'vitest';
5
5
  import OperationConfirmation from './confirmation-operation-modal.component';
6
6
 
7
- jest.mock('react-i18next', () => ({
8
- useTranslation: jest.fn(),
9
- }));
10
-
11
7
  describe('OperationConfirmation', () => {
12
- beforeEach(() => {
13
- (useTranslation as jest.Mock).mockReturnValue({
14
- t: (key, defaultValue, options) =>
15
- options?.operationTypeOrName ? `Do you want to ${options.operationTypeOrName}?` : defaultValue || key,
16
- });
17
- });
18
-
19
8
  it('renders the component with provided props', () => {
20
- const closeMock = jest.fn();
21
- const confirmMock = jest.fn();
9
+ const closeMock = vi.fn();
10
+ const confirmMock = vi.fn();
22
11
  const operationName = 'refresh';
23
12
  const operationType = 'refreshed';
24
13
 
@@ -32,14 +21,14 @@ describe('OperationConfirmation', () => {
32
21
  );
33
22
 
34
23
  expect(screen.getByRole('heading', { name: 'Confirmation' })).toBeInTheDocument();
35
- expect(screen.getByText('Do you want to refreshed?')).toBeInTheDocument();
24
+ expect(screen.getByText('Do you want to {{operationTypeOrName}}?')).toBeInTheDocument();
36
25
  expect(screen.getByText('No')).toBeInTheDocument();
37
26
  expect(screen.getByText('Yes')).toBeInTheDocument();
38
27
  });
39
28
 
40
29
  it('calls close when the No button is clicked', () => {
41
- const closeMock = jest.fn();
42
- const confirmMock = jest.fn();
30
+ const closeMock = vi.fn();
31
+ const confirmMock = vi.fn();
43
32
 
44
33
  render(
45
34
  <OperationConfirmation close={closeMock} confirm={confirmMock} operationName="delete" operationType="deleted" />,
@@ -53,8 +42,8 @@ describe('OperationConfirmation', () => {
53
42
  });
54
43
 
55
44
  it('calls confirm when the Yes button is clicked', () => {
56
- const closeMock = jest.fn();
57
- const confirmMock = jest.fn();
45
+ const closeMock = vi.fn();
46
+ const confirmMock = vi.fn();
58
47
 
59
48
  render(
60
49
  <OperationConfirmation close={closeMock} confirm={confirmMock} operationName="delete" operationType="deleted" />,
@@ -12,6 +12,7 @@ import {
12
12
  } from '../logs-table/operation-log-resource';
13
13
  import LogTable from '../logs-table/operation-log-table.component';
14
14
  import { showModal, showSnackbar } from '@openmrs/esm-framework';
15
+ import { extractErrorMessagesFromResponse } from '../locations/helpers';
15
16
 
16
17
  const Dashboard: React.FC = () => {
17
18
  const { t } = useTranslation();
@@ -59,7 +60,9 @@ const Dashboard: React.FC = () => {
59
60
  } catch (error) {
60
61
  showSnackbar({
61
62
  title: t('operationError', '{{operationName}} failed', { operationName }),
62
- subtitle: t('operationErrorSubtitle', 'An error occurred during the operation.'),
63
+ subtitle: t('operationErrorSubtitle', 'An error occurred during the operation. {{error}}', {
64
+ error: extractErrorMessagesFromResponse(error),
65
+ }),
63
66
  kind: 'error',
64
67
  isLowContrast: true,
65
68
  });
@@ -1,12 +1,11 @@
1
1
  import { render, screen } from '@testing-library/react';
2
2
  import '@testing-library/jest-dom';
3
- import { EmptyDataIllustration } from '@openmrs/esm-patient-common-lib';
3
+ import { vi, describe, it, expect } from 'vitest';
4
4
  import EmptyState from './empty-state-log.components';
5
5
  import React from 'react';
6
6
 
7
- // Mock the EmptyDataIllustration component
8
- jest.mock('@openmrs/esm-patient-common-lib', () => ({
9
- EmptyDataIllustration: jest.fn(() => <div>Mocked EmptyDataIllustration</div>),
7
+ vi.mock('@openmrs/esm-patient-common-lib', () => ({
8
+ EmptyDataIllustration: vi.fn(() => <div>Mocked EmptyDataIllustration</div>),
10
9
  }));
11
10
 
12
11
  describe('EmptyState', () => {
@@ -0,0 +1,3 @@
1
+ import { restBaseUrl } from '@openmrs/esm-framework';
2
+
3
+ export const facilityRegistryUrl = `${restBaseUrl}/virtualclaims/facility-registry`;
@@ -1,142 +1,281 @@
1
- import { Button, Column, Grid, InlineLoading, Layer, Tile } from '@carbon/react';
1
+ import { Button, Column, Grid, InlineLoading, Tag, Tile } from '@carbon/react';
2
+ import { Renew, Phone, Email } from '@carbon/react/icons';
2
3
  import { formatDate, parseDate, showSnackbar } from '@openmrs/esm-framework';
3
- import React, { useCallback, useMemo, useState } from 'react';
4
+ import React, { useCallback } from 'react';
4
5
  import { useTranslation } from 'react-i18next';
5
- import { useLocalFacilityInfo, useShaFacilityInfo } from '../hook/useFacilityInfo';
6
6
  import styles from './facility-info.scss';
7
- import Card from './card.component';
8
- import dayjs from 'dayjs';
9
- import { syncPackagesAndInterventions } from './facility-setup.resource';
7
+ import { InfoRow } from './shared/custom-info.component';
8
+ import { SectionCard } from './shared/custom-section-card.component';
9
+ import { StatusTag } from './shared/custom-status-tag.component';
10
+ import { syncFacilityRegistry, useFacilityRegistry } from './useFacilityRegistry';
11
+ import { FacilityRegistryRecord } from './type';
12
+ import { CardHeader } from '@openmrs/esm-patient-common-lib/src';
13
+ import EmptyState from '../empty-state/empty-state-log.components';
10
14
 
11
- const FacilityInfo: React.FC = () => {
15
+ const FacilityRegistryView: React.FC = () => {
12
16
  const { t } = useTranslation();
13
- const [shouldSynchronize, setshouldSynchronize] = useState<boolean>(false);
14
- const {
15
- shaFacility,
16
- isLoading: isShaFacilityLoading,
17
- error: shaFacilityError,
18
- mutate: mutateShafacility,
19
- } = useShaFacilityInfo(shouldSynchronize);
20
- const {
21
- localFacility,
22
- isLoading: isLocalFacilityLoading,
23
- mutate: mutateLocalFacility,
24
- error: localFacilityError,
25
- } = useLocalFacilityInfo();
26
- const mutateFacility = useCallback(async () => {
27
- const defaultFacility = await mutateLocalFacility();
28
- const shaFacility = await mutateShafacility();
29
- return {
30
- shaFacility,
31
- defaultFacility,
32
- };
33
- }, [mutateLocalFacility, mutateShafacility]);
34
- const shaStatus = useMemo(
35
- () =>
36
- shaFacility?.operationalStatus
37
- ? `${shaFacility.operationalStatus.at(0).toUpperCase()}${shaFacility.operationalStatus
38
- .slice(1)
39
- ?.toLocaleLowerCase()}`
40
- : undefined,
41
- [shaFacility],
42
- );
43
- const shaExpiry = useMemo(
44
- () =>
45
- shaFacility?.shaFacilityExpiryDate && dayjs(shaFacility.shaFacilityExpiryDate).isValid()
46
- ? formatDate(parseDate(shaFacility.shaFacilityExpiryDate))
47
- : undefined,
48
- [shaFacility],
49
- );
17
+ const { facility, isLoading, error, notYetSynced, mutate } = useFacilityRegistry();
18
+ const [isSyncing, setIsSyncing] = React.useState(false);
50
19
 
51
- const synchronizeFacilityData = useCallback(async () => {
20
+ const handleSync = useCallback(async () => {
21
+ setIsSyncing(true);
52
22
  try {
53
- setshouldSynchronize(true);
54
- const { shaFacility } = await mutateFacility();
23
+ const result = await syncFacilityRegistry();
24
+ await mutate();
55
25
  showSnackbar({
56
- title: t('syncingHieSuccess', 'Synchronization Complete'),
26
+ title: t('facilityRegistrySyncSuccess', 'Facility synced'),
27
+ subtitle: t('facilityRegistrySyncedFrom', 'Pulled {{name}} from the facility registry', {
28
+ name: result.official_name,
29
+ }),
57
30
  kind: 'success',
58
31
  isLowContrast: true,
59
32
  });
60
- if (shaFacility.data?.source != 'HIE') {
61
- showSnackbar({
62
- kind: 'warning',
63
- title: 'HIE Sync Failed. Pulling local info.',
64
- isLowContrast: true,
65
- });
66
- }
67
- // sync packages and intervensions
68
- await syncPackagesAndInterventions();
69
- } catch (error) {
70
- const errorMessage =
71
- error?.responseBody?.error?.message ??
72
- t('hieSynchronizationError', 'An error occurred while synchronizing with HIE');
33
+ } catch (err: any) {
73
34
  showSnackbar({
74
- title: t('syncingHieError', 'Syncing with HIE Failed'),
75
- subtitle: errorMessage,
35
+ title: t('facilityRegistrySyncFailed', 'Sync failed'),
36
+ subtitle:
37
+ err?.responseBody?.error ??
38
+ err?.message ??
39
+ t('facilityRegistrySyncError', 'Unable to reach the facility registry'),
76
40
  kind: 'error',
77
41
  isLowContrast: true,
78
42
  });
43
+ } finally {
44
+ setIsSyncing(false);
79
45
  }
80
- }, [mutateFacility, t]);
46
+ }, [mutate, t]);
81
47
 
82
- return (
83
- <div className={styles.facilityInfoContainer}>
48
+ if (isLoading) {
49
+ return (
50
+ <div className={styles.root}>
51
+ <InlineLoading description={t('loadingFacility', 'Loading facility…')} />
52
+ </div>
53
+ );
54
+ }
55
+
56
+ if (error) {
57
+ return (
58
+ <div className={styles.root}>
59
+ <Tile className={styles.errorTile}>
60
+ <p>{t('facilityRegistryLoadError', 'Failed to load the facility record.')}</p>
61
+ <Button kind="secondary" size="sm" renderIcon={Renew} onClick={handleSync} disabled={isSyncing}>
62
+ {isSyncing ? t('syncing', 'Syncing…') : t('retry', 'Retry sync')}
63
+ </Button>
64
+ </Tile>
65
+ </div>
66
+ );
67
+ }
68
+
69
+ if (notYetSynced || !facility) {
70
+ return (
84
71
  <div>
85
- <Layer className={styles.btnLayer}>
86
- {isShaFacilityLoading || isLocalFacilityLoading ? (
87
- <InlineLoading
88
- description={t('synchronizingFacilityData', 'Please wait, Synchronizing Info.')}
89
- className={styles.loading}
90
- />
72
+ <CardHeader title={t('facilityDetails', 'Facility details')}>
73
+ <Button kind="primary" size="sm" renderIcon={Renew} onClick={handleSync} disabled={isSyncing}>
74
+ {isSyncing ? t('syncing', 'Syncing…') : t('syncNow', 'Sync now')}
75
+ </Button>
76
+ </CardHeader>
77
+ <EmptyState
78
+ subTitle={t(
79
+ 'facilityRegistryNotSyncedBody',
80
+ 'This facility has not been pulled from the registry. Click sync to fetch the latest record.',
81
+ )}
82
+ />
83
+ </div>
84
+ );
85
+ }
86
+
87
+ return (
88
+ <div className={styles.root}>
89
+ <div className={styles.pageHeader}>
90
+ <div className={styles.pageActions}>
91
+ {facility.last_synced_at && (
92
+ <span className={styles.lastSynced}>
93
+ {t('lastSynced', 'Last synced')}: {formatDate(parseDate(facility.last_synced_at))}
94
+ </span>
95
+ )}
96
+ {isSyncing ? (
97
+ <InlineLoading description={t('syncing', 'Syncing…')} />
91
98
  ) : (
92
- <Button kind="primary" onClick={synchronizeFacilityData}>
93
- {t('synchronizeWithHie', 'Synchronize with HIE')}
99
+ <Button kind="secondary" size="sm" renderIcon={Renew} onClick={handleSync}>
100
+ {t('syncWithRegistry', 'Sync with facility registry')}
94
101
  </Button>
95
102
  )}
96
- </Layer>
103
+ </div>
97
104
  </div>
98
105
 
99
- <Grid narrow>
100
- {/* General Info Column */}
106
+ <Grid narrow className={styles.grid}>
107
+ <Column sm={4} md={4} lg={8}>
108
+ <SectionCard title={t('generalInformation', 'General information')}>
109
+ <InfoRow label={t('facilityName', 'Facility name')} value={facility.official_name} />
110
+ <InfoRow label={t('registrationNumber', 'Registration no.')} value={facility.registration_number} />
111
+ <InfoRow label={t('frCode', 'FR code')} value={facility.fr_code} />
112
+ <InfoRow label={t('fidCode', 'FID code')} value={facility.fid_code} />
113
+ <InfoRow label={t('kephLevel', 'KEPH level')} value={facility.keph_level} />
114
+ <InfoRow label={t('facilityType', 'Facility type')} value={facility.facility_type} />
115
+ <InfoRow label={t('facilityOwnership', 'Ownership')} value={facility.facility_ownership} />
116
+ <InfoRow
117
+ label={t('hubFacility', 'Hub facility')}
118
+ value={
119
+ <Tag type={facility.is_hub ? 'blue' : 'gray'} size="sm">
120
+ {facility.is_hub ? t('yes', 'Yes') : t('no', 'No')}
121
+ </Tag>
122
+ }
123
+ />
124
+ </SectionCard>
125
+ </Column>
126
+
127
+ <Column sm={4} md={4} lg={8}>
128
+ <SectionCard title={t('licenseAndShaStatus', 'License & SHA status')}>
129
+ <InfoRow label={t('licenseNumber', 'License no.')} value={facility.license_number} />
130
+ <InfoRow label={t('regulatoryBody', 'Regulatory body')} value={facility.regulatory_body} />
131
+ <InfoRow
132
+ label={t('licenseStatus', 'License status')}
133
+ value={<StatusTag value={facility.license_status} />}
134
+ />
135
+ <InfoRow
136
+ label={t('licenseStart', 'License start')}
137
+ value={facility.license_start_date ? formatDate(parseDate(facility.license_start_date)) : '—'}
138
+ />
139
+ <InfoRow
140
+ label={t('licenseExpiry', 'License expiry')}
141
+ value={facility.license_end_date ? formatDate(parseDate(facility.license_end_date)) : '—'}
142
+ />
143
+ <InfoRow
144
+ label={t('regulatoryOperationalStatus', 'Regulatory status')}
145
+ value={<StatusTag value={facility.regulatory_operational_status} />}
146
+ />
147
+ <InfoRow
148
+ label={t('shaOperationalStatus', 'SHA status')}
149
+ value={<StatusTag value={facility.sha_operational_status} />}
150
+ />
151
+ <InfoRow
152
+ label={t('shaContractStatus', 'SHA contract')}
153
+ value={
154
+ facility.sha_contract_status && facility.sha_contract_status !== '' ? (
155
+ <StatusTag value={facility.sha_contract_status} />
156
+ ) : (
157
+ <Tag type="gray" size="sm">
158
+ {t('notContracted', 'Not contracted')}
159
+ </Tag>
160
+ )
161
+ }
162
+ />
163
+ {facility.bed_occupancy && (
164
+ <InfoRow label={t('totalBeds', 'Total beds')} value={facility.bed_occupancy.totalBeds?.toString()} />
165
+ )}
166
+ </SectionCard>
167
+ </Column>
168
+
101
169
  <Column sm={4} md={4} lg={8}>
102
- <Tile className={styles.card}>
103
- <h3 className={styles.cardTitle}>{t('generalInformation', 'General Information')}</h3>
104
- <hr className={styles.cardDivider} />
105
- <div className={styles.cardContent}>
106
- <Card label={t('facilityName', 'Facility Name')} value={localFacility?.display} />
107
- <Card label={t('facilityCode', 'Facility KMHFR Code')} value={shaFacility?.mflCode} />
108
- <Card label={t('kephLevel', 'Keph Level')} value={shaFacility?.kephLevel} />
109
- <br />
110
- <br />
111
- </div>
112
- </Tile>
170
+ <SectionCard title={t('location', 'Location')}>
171
+ <InfoRow label={t('country', 'Country')} value={facility.address?.country} />
172
+ <InfoRow label={t('county', 'County')} value={facility.address?.county} />
173
+ <InfoRow label={t('subCounty', 'Sub-county')} value={facility.address?.sub_county} />
174
+ <InfoRow label={t('town', 'Town')} value={facility.address?.town} />
175
+ <InfoRow label={t('physicalLocation', 'Physical location')} value={facility.address?.physical_location} />
176
+ <InfoRow label={t('postalAddress', 'Postal address')} value={facility.address?.postal_address} />
177
+ {facility.address?.latitude != null && facility.address?.longitude != null && (
178
+ <InfoRow
179
+ label={t('coordinates', 'Coordinates')}
180
+ value={`${facility.address.latitude}, ${facility.address.longitude}`}
181
+ />
182
+ )}
183
+ </SectionCard>
113
184
  </Column>
114
185
 
115
- {/* SHA Info Column */}
116
186
  <Column sm={4} md={4} lg={8}>
117
- <Layer>
118
- <Tile className={styles.card}>
119
- <h3 className={styles.cardTitle}>{t('facilityInformation', 'Facility Information')}</h3>
120
- <hr className={styles.cardDivider} />
121
- <div className={styles.cardContent}>
122
- <Card
123
- label={t('facilityRegistryCode', 'Facility Registry Code')}
124
- value={shaFacility?.facilityRegistryCode}
125
- />
126
- <Card
127
- label={t('shalicenceNumber', 'SHA License Number')}
128
- value={shaFacility?.shaFacilityLicenseNumber}
129
- />
130
- <Card label={t('shaStatus', 'SHA Status')} value={shaStatus} />
131
- <Card label={t('shaContracted', 'SHA Contracted')} value={shaFacility?.approved} />
132
- <Card label={t('shaContractExpiry', 'SHA Contract Expiry Date')} value={shaExpiry} />
187
+ <SectionCard title={t('contactAndAdministrator', 'Contact & administrator')}>
188
+ <InfoRow
189
+ label={t('facilityPhone', 'Phone')}
190
+ value={
191
+ facility.facility_phone_number ? (
192
+ <span className={styles.contactValue}>
193
+ <Phone size={14} />
194
+ {facility.facility_phone_number}
195
+ </span>
196
+ ) : undefined
197
+ }
198
+ />
199
+ <InfoRow
200
+ label={t('facilityEmail', 'Email')}
201
+ value={
202
+ facility.facility_email ? (
203
+ <span className={styles.contactValue}>
204
+ <Email size={14} />
205
+ {facility.facility_email}
206
+ </span>
207
+ ) : undefined
208
+ }
209
+ />
210
+ {facility.facility_administrator_name && (
211
+ <div className={styles.adminCard}>
212
+ <div>
213
+ <p className={styles.adminName}>{facility.facility_administrator_name}</p>
214
+ {facility.facility_administrator_phone && (
215
+ <p className={styles.adminDetail}>{facility.facility_administrator_phone}</p>
216
+ )}
217
+ {facility.facility_administrator_email && (
218
+ <p className={styles.adminDetail}>{facility.facility_administrator_email}</p>
219
+ )}
220
+ </div>
133
221
  </div>
134
- </Tile>
135
- </Layer>
222
+ )}
223
+ </SectionCard>
224
+ </Column>
225
+
226
+ <Column sm={4} md={8} lg={16}>
227
+ <SectionCard title={t('bedOccupancy', 'Bed occupancy')}>
228
+ <BedOccupancyView facility={facility} />
229
+ </SectionCard>
230
+ </Column>
231
+
232
+ <Column sm={4} md={8} lg={16}>
233
+ <SectionCard title={t('shaContractedServices', 'SHA contracted services')}>
234
+ <ServicesView facility={facility} />
235
+ </SectionCard>
136
236
  </Column>
137
237
  </Grid>
138
238
  </div>
139
239
  );
140
240
  };
141
241
 
142
- export default FacilityInfo;
242
+ const BedOccupancyView: React.FC<{ facility: FacilityRegistryRecord }> = ({ facility }) => {
243
+ const { t } = useTranslation();
244
+ const bo = facility.bed_occupancy;
245
+ if (!bo) {
246
+ return <p className={styles.emptyState}>{t('noBedOccupancyData', 'No bed occupancy data')}</p>;
247
+ }
248
+ return (
249
+ <div className={styles.bedGrid}>
250
+ <InfoRow label={t('totalBeds', 'Total')} value={bo.totalBeds?.toString()} />
251
+ <InfoRow label={t('normalBeds', 'Normal')} value={bo.normalBeds?.toString()} />
252
+ <InfoRow label={t('icuBeds', 'ICU')} value={bo.icuBeds?.toString()} />
253
+ <InfoRow label={t('hduBeds', 'HDU')} value={bo.hduBeds?.toString()} />
254
+ <InfoRow label={t('dialysisBeds', 'Dialysis')} value={bo.dialysisBeds?.toString()} />
255
+ <InfoRow label={t('cots', 'Cots')} value={bo.numberOfCots?.toString()} />
256
+ </div>
257
+ );
258
+ };
259
+
260
+ const ServicesView: React.FC<{ facility: FacilityRegistryRecord }> = ({ facility }) => {
261
+ const { t } = useTranslation();
262
+ const services = facility.sha_contracted_services ?? [];
263
+ if (services.length === 0) {
264
+ return (
265
+ <p className={styles.emptyState}>
266
+ {t('noContractedServices', 'No contracted services on record for this facility.')}
267
+ </p>
268
+ );
269
+ }
270
+ return (
271
+ <div className={styles.serviceList}>
272
+ {services.map((svc, idx) => (
273
+ <Tag key={idx} type="teal" size="sm">
274
+ {typeof svc === 'string' ? svc : svc.name ?? JSON.stringify(svc)}
275
+ </Tag>
276
+ ))}
277
+ </div>
278
+ );
279
+ };
280
+
281
+ export default FacilityRegistryView;