@kenyaemr/esm-admin-app 5.4.4-pre.7 → 5.4.4-pre.71

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 (280) hide show
  1. package/.turbo/turbo-build.log +5 -12
  2. package/dist/1074.js +1 -0
  3. package/dist/1074.js.map +1 -0
  4. package/dist/12.js +17 -0
  5. package/dist/12.js.map +1 -0
  6. package/dist/1201.js +1 -0
  7. package/dist/1201.js.map +1 -0
  8. package/dist/1242.js +1 -0
  9. package/dist/1242.js.map +1 -0
  10. package/dist/1311.js +1 -0
  11. package/dist/1311.js.map +1 -0
  12. package/dist/1425.js +1 -0
  13. package/dist/1425.js.map +1 -0
  14. package/dist/1462.js +1 -0
  15. package/dist/1462.js.map +1 -0
  16. package/dist/1469.js +1 -0
  17. package/dist/1469.js.map +1 -0
  18. package/dist/1506.js +13 -0
  19. package/dist/1506.js.map +1 -0
  20. package/dist/171.js +1 -0
  21. package/dist/171.js.map +1 -0
  22. package/dist/1718.js +1 -0
  23. package/dist/1718.js.map +1 -0
  24. package/dist/1722.js +1 -0
  25. package/dist/1722.js.map +1 -0
  26. package/dist/1772.js +1 -0
  27. package/dist/1772.js.map +1 -0
  28. package/dist/1889.js +1 -0
  29. package/dist/1889.js.map +1 -0
  30. package/dist/1972.js +1 -0
  31. package/dist/1972.js.map +1 -0
  32. package/dist/1990.js +1 -0
  33. package/dist/1990.js.map +1 -0
  34. package/dist/2080.js +1 -0
  35. package/dist/2080.js.map +1 -0
  36. package/dist/2096.js +1 -0
  37. package/dist/2096.js.map +1 -0
  38. package/dist/2153.js +1 -0
  39. package/dist/2153.js.map +1 -0
  40. package/dist/216.js +1 -0
  41. package/dist/216.js.map +1 -0
  42. package/dist/2270.js +1 -0
  43. package/dist/2270.js.map +1 -0
  44. package/dist/2294.js +1 -0
  45. package/dist/2294.js.map +1 -0
  46. package/dist/2345.js +1 -0
  47. package/dist/2345.js.map +1 -0
  48. package/dist/2402.js +1 -0
  49. package/dist/2402.js.map +1 -0
  50. package/dist/2500.js +1 -0
  51. package/dist/2500.js.map +1 -0
  52. package/dist/251.js +1 -0
  53. package/dist/251.js.map +1 -0
  54. package/dist/257.js +1 -0
  55. package/dist/257.js.map +1 -0
  56. package/dist/2586.js +1 -0
  57. package/dist/2586.js.map +1 -0
  58. package/dist/2625.js +1 -0
  59. package/dist/2625.js.map +1 -0
  60. package/dist/2652.js +1 -0
  61. package/dist/2652.js.map +1 -0
  62. package/dist/2685.js +1 -0
  63. package/dist/2685.js.map +1 -0
  64. package/dist/2948.js +1 -0
  65. package/dist/2948.js.map +1 -0
  66. package/dist/3089.js +1 -0
  67. package/dist/3089.js.map +1 -0
  68. package/dist/3190.js +1 -0
  69. package/dist/3190.js.map +1 -0
  70. package/dist/3224.js +1 -0
  71. package/dist/3224.js.map +1 -0
  72. package/dist/3366.js +1 -0
  73. package/dist/3366.js.map +1 -0
  74. package/dist/3548.js +1 -0
  75. package/dist/3548.js.map +1 -0
  76. package/dist/3571.js +1 -0
  77. package/dist/3571.js.map +1 -0
  78. package/dist/3691.js +1 -0
  79. package/dist/3691.js.map +1 -0
  80. package/dist/3775.js +1 -0
  81. package/dist/3775.js.map +1 -0
  82. package/dist/3816.js +1 -0
  83. package/dist/3816.js.map +1 -0
  84. package/dist/3852.js +1 -0
  85. package/dist/3852.js.map +1 -0
  86. package/dist/3906.js +1 -0
  87. package/dist/3906.js.map +1 -0
  88. package/dist/3963.js +1 -0
  89. package/dist/3963.js.map +1 -0
  90. package/dist/405.js +1 -0
  91. package/dist/405.js.map +1 -0
  92. package/dist/4296.js +1 -0
  93. package/dist/4296.js.map +1 -0
  94. package/dist/4337.js +1 -0
  95. package/dist/4337.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/4970.js +1 -0
  105. package/dist/4970.js.map +1 -0
  106. package/dist/5202.js +1 -0
  107. package/dist/5202.js.map +1 -0
  108. package/dist/5294.js +1 -0
  109. package/dist/5294.js.map +1 -0
  110. package/dist/545.js +1 -0
  111. package/dist/545.js.map +1 -0
  112. package/dist/552.js +1 -0
  113. package/dist/552.js.map +1 -0
  114. package/dist/5592.js +1 -0
  115. package/dist/5592.js.map +1 -0
  116. package/dist/5669.js +1 -0
  117. package/dist/5669.js.map +1 -0
  118. package/dist/5884.js +1 -0
  119. package/dist/5884.js.map +1 -0
  120. package/dist/5940.js +1 -0
  121. package/dist/5940.js.map +1 -0
  122. package/dist/6092.js +1 -0
  123. package/dist/6092.js.map +1 -0
  124. package/dist/6155.js +1 -0
  125. package/dist/6155.js.map +1 -0
  126. package/dist/6178.js +1 -0
  127. package/dist/6178.js.map +1 -0
  128. package/dist/6399.js +1 -0
  129. package/dist/6399.js.map +1 -0
  130. package/dist/6456.js +1 -0
  131. package/dist/6466.js +3 -0
  132. package/dist/6466.js.map +1 -0
  133. package/dist/6492.js +1 -0
  134. package/dist/6492.js.map +1 -0
  135. package/dist/6676.js +1 -0
  136. package/dist/6676.js.map +1 -0
  137. package/dist/6800.js +1 -0
  138. package/dist/6800.js.map +1 -0
  139. package/dist/6976.js +1 -0
  140. package/dist/6976.js.map +1 -0
  141. package/dist/7005.js +1 -0
  142. package/dist/7005.js.map +1 -0
  143. package/dist/7201.js +1 -0
  144. package/dist/7201.js.map +1 -0
  145. package/dist/7210.js +1 -0
  146. package/dist/7210.js.map +1 -0
  147. package/dist/7234.js +1 -0
  148. package/dist/7234.js.map +1 -0
  149. package/dist/7261.js +1 -0
  150. package/dist/7261.js.map +1 -0
  151. package/dist/7326.js +1 -0
  152. package/dist/7463.js +1 -0
  153. package/dist/7463.js.map +1 -0
  154. package/dist/7528.js +1 -0
  155. package/dist/7528.js.map +1 -0
  156. package/dist/7584.js +1 -0
  157. package/dist/7584.js.map +1 -0
  158. package/dist/{127.js → 7607.js} +1 -1
  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/7916.js +1 -0
  174. package/dist/7916.js.map +1 -0
  175. package/dist/7971.js +1 -0
  176. package/dist/7971.js.map +1 -0
  177. package/dist/8159.js +7 -0
  178. package/dist/8159.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/834.js +1 -0
  184. package/dist/834.js.map +1 -0
  185. package/dist/8376.js +1 -0
  186. package/dist/8376.js.map +1 -0
  187. package/dist/845.js +1 -0
  188. package/dist/845.js.map +1 -0
  189. package/dist/8546.js +1 -0
  190. package/dist/8546.js.map +1 -0
  191. package/dist/8570.js +1 -0
  192. package/dist/8570.js.map +1 -0
  193. package/dist/87.js +1 -0
  194. package/dist/87.js.map +1 -0
  195. package/dist/8727.js +1 -0
  196. package/dist/8828.js +1 -0
  197. package/dist/8828.js.map +1 -0
  198. package/dist/8860.js +1 -0
  199. package/dist/8860.js.map +1 -0
  200. package/dist/9036.js +1 -0
  201. package/dist/9036.js.map +1 -0
  202. package/dist/9124.js +1 -0
  203. package/dist/9124.js.map +1 -0
  204. package/dist/9182.js +1 -0
  205. package/dist/921.js +1 -0
  206. package/dist/921.js.map +1 -0
  207. package/dist/9404.js +1 -0
  208. package/dist/9404.js.map +1 -0
  209. package/dist/9406.js +1 -0
  210. package/dist/9406.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/9641.js +1 -0
  218. package/dist/9641.js.map +1 -0
  219. package/dist/9711.js +1 -0
  220. package/dist/9711.js.map +1 -0
  221. package/dist/9801.js +1 -0
  222. package/dist/9801.js.map +1 -0
  223. package/dist/9835.js +11 -0
  224. package/dist/9835.js.map +1 -0
  225. package/dist/kenyaemr-esm-admin-app.js +5 -5
  226. package/dist/kenyaemr-esm-admin-app.js.buildmanifest.json +2679 -156
  227. package/dist/kenyaemr-esm-admin-app.js.map +1 -1
  228. package/dist/main.js +5 -31
  229. package/dist/main.js.map +1 -1
  230. package/dist/routes.json +1 -1
  231. package/package.json +4 -6
  232. package/rspack.config.js +1 -1
  233. package/src/components/locations/forms/add-location/add-location.workspace.tsx +96 -95
  234. package/src/components/locations/forms/search-location/search-location.workspace.tsx +90 -85
  235. package/src/components/locations/tables/locations-table.component.tsx +117 -121
  236. package/src/components/users/manage-users/manage-user-role-scope/user-role-scope-workspace/user-role-scope.workspace.tsx +13 -13
  237. package/src/components/users/manage-users/user-list/user-list.component.tsx +22 -9
  238. package/src/components/users/manage-users/user-management.workspace.tsx +538 -531
  239. package/src/index.ts +51 -28
  240. package/src/root.component.tsx +11 -13
  241. package/src/routes.json +40 -40
  242. package/tsconfig.json +1 -1
  243. package/dist/267.js +0 -1
  244. package/dist/267.js.map +0 -1
  245. package/dist/281.js +0 -15
  246. package/dist/281.js.map +0 -1
  247. package/dist/329.js +0 -1
  248. package/dist/329.js.map +0 -1
  249. package/dist/40.js +0 -1
  250. package/dist/466.js +0 -1
  251. package/dist/466.js.map +0 -1
  252. package/dist/472.js +0 -1
  253. package/dist/472.js.map +0 -1
  254. package/dist/478.js +0 -1
  255. package/dist/478.js.map +0 -1
  256. package/dist/585.js +0 -1
  257. package/dist/585.js.map +0 -1
  258. package/dist/630.js +0 -1
  259. package/dist/630.js.map +0 -1
  260. package/dist/675.js +0 -1
  261. package/dist/675.js.map +0 -1
  262. package/dist/689.js +0 -1
  263. package/dist/689.js.map +0 -1
  264. package/dist/706.js +0 -27
  265. package/dist/706.js.map +0 -1
  266. package/dist/729.js +0 -17
  267. package/dist/729.js.map +0 -1
  268. package/dist/774.js +0 -1
  269. package/dist/774.js.map +0 -1
  270. package/dist/847.js +0 -1
  271. package/dist/847.js.map +0 -1
  272. package/dist/85.js +0 -1
  273. package/dist/85.js.map +0 -1
  274. package/dist/882.js +0 -1
  275. package/dist/91.js +0 -1
  276. package/dist/91.js.map +0 -1
  277. package/dist/916.js +0 -1
  278. package/dist/998.js +0 -1
  279. package/dist/998.js.map +0 -1
  280. package/jest.config.js +0 -8
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.7"}
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}],"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"}],"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"}],"workspaceWindows2":[{"name":"esm-admin-workspace-window","canMaximize":true,"group":"esm-admin-workspace-group"},{"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.71"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kenyaemr/esm-admin-app",
3
- "version": "5.4.4-pre.7",
3
+ "version": "5.4.4-pre.71",
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
25
  "extract-translations": "i18next 'src/**/*.component.tsx' 'src/index.ts' --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,35 +1,36 @@
1
- import React, { useEffect, useMemo } from 'react';
1
+ import React, { useEffect, useMemo, useState } from 'react';
2
2
  import {
3
- type DefaultWorkspaceProps,
4
3
  ResponsiveWrapper,
5
- useLayoutType,
6
- showSnackbar,
7
- useConfig,
4
+ Workspace2,
8
5
  restBaseUrl,
6
+ showSnackbar,
7
+ useLayoutType,
8
+ type Workspace2DefinitionProps,
9
9
  } from '@openmrs/esm-framework';
10
10
  import { useTranslation } from 'react-i18next';
11
11
  import { Controller, useForm } from 'react-hook-form';
12
12
  import {
13
- ButtonSet,
14
13
  Button,
15
- InlineLoading,
16
- TextInput,
14
+ ButtonSet,
15
+ FilterableMultiSelect,
16
+ Form,
17
17
  FormGroup,
18
+ InlineLoading,
18
19
  Stack,
19
- Form,
20
- FilterableMultiSelect,
20
+ TextInput,
21
21
  } from '@carbon/react';
22
- import classNames from 'classnames';
23
- import { z } from 'zod';
24
22
  import { zodResolver } from '@hookform/resolvers/zod';
23
+ import { z } from 'zod';
24
+ import classNames from 'classnames';
25
+ import { mutate } from 'swr';
26
+
25
27
  import styles from './add-location.workspace.scss';
26
28
  import { type LocationResponse } from '../../types';
27
29
  import { extractErrorMessagesFromResponse } from '../../helpers';
28
30
  import { useLocationTags } from '../../hooks/useLocationTags';
29
- import { mutate } from 'swr';
30
31
  import { saveOrUpdateLocation } from '../../hooks/useLocation';
31
32
 
32
- type AddLocationWorkspaceProps = DefaultWorkspaceProps & {
33
+ type AddLocationWorkspaceProps = {
33
34
  location?: LocationResponse;
34
35
  };
35
36
 
@@ -46,16 +47,14 @@ const locationFormSchema = z.object({
46
47
 
47
48
  type LocationFormType = z.infer<typeof locationFormSchema>;
48
49
 
49
- const AddLocationWorkspace: React.FC<AddLocationWorkspaceProps> = ({
50
+ const AddLocationWorkspace: React.FC<Workspace2DefinitionProps<AddLocationWorkspaceProps, {}, {}>> = ({
50
51
  closeWorkspace,
51
- closeWorkspaceWithSavedChanges,
52
- promptBeforeClosing,
53
- location,
52
+ workspaceProps: { location = {} as LocationResponse },
54
53
  }) => {
55
54
  const { t } = useTranslation();
56
55
  const isTablet = useLayoutType() === 'tablet';
57
56
  const { locationTagList: Tags } = useLocationTags();
58
-
57
+ const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
59
58
  const hasLocationAttributes = useMemo(() => {
60
59
  return location?.attributes && location.attributes.length > 0;
61
60
  }, [location?.attributes]);
@@ -105,7 +104,7 @@ const AddLocationWorkspace: React.FC<AddLocationWorkspaceProps> = ({
105
104
  }),
106
105
  });
107
106
  handleMutation();
108
- closeWorkspaceWithSavedChanges();
107
+ closeWorkspace({ discardUnsavedChanges: true });
109
108
  } catch (error: any) {
110
109
  const errorMessages = extractErrorMessagesFromResponse(error);
111
110
  showSnackbar({
@@ -117,83 +116,85 @@ const AddLocationWorkspace: React.FC<AddLocationWorkspaceProps> = ({
117
116
  };
118
117
 
119
118
  useEffect(() => {
120
- promptBeforeClosing(() => isDirty);
121
- }, [isDirty, promptBeforeClosing]);
119
+ setHasUnsavedChanges(true);
120
+ }, [isDirty, setHasUnsavedChanges]);
122
121
 
123
122
  return (
124
- <Form onSubmit={handleSubmit(onSubmit)} className={styles.form}>
125
- <div className={styles.formContainer}>
126
- <Stack gap={3}>
127
- <ResponsiveWrapper>
128
- <FormGroup legendText="">
129
- <Controller
130
- control={control}
131
- name="name"
132
- render={({ field }) => (
133
- <TextInput
134
- id="locationName"
135
- placeholder={t('locationPlaceholder', 'Add a location')}
136
- labelText={t('locationName', 'Location Name')}
137
- value={field.value}
138
- onChange={field.onChange}
139
- invalid={!!errors.name?.message}
140
- invalidText={errors.name?.message}
141
- disabled={hasLocationAttributes}
142
- />
143
- )}
144
- />
145
- </FormGroup>
146
- </ResponsiveWrapper>
147
-
148
- <ResponsiveWrapper>
149
- <FormGroup legendText="">
150
- <Controller
151
- control={control}
152
- name="tags"
153
- render={({ field: { onChange, value, ref } }) => (
154
- <FilterableMultiSelect
155
- id="locationTags"
156
- titleText={t('selectTags', 'Select tag(s)')}
157
- placeholder={t('selectTagPlaceholder', 'Select a tag')}
158
- items={Tags || []}
159
- selectedItems={(value || []).map(
160
- (selected) => Tags?.find((tag) => tag.uuid === selected.uuid) || selected,
161
- )}
162
- onChange={({ selectedItems }) => onChange(selectedItems || [])}
163
- itemToString={(item) => (item && typeof item === 'object' ? item.display : '')}
164
- selectionFeedback="top-after-reopen"
165
- invalid={!!errors.tags?.message}
166
- invalidText={errors.tags?.message}
167
- disabled={!Tags?.length}
168
- ref={ref}
169
- />
170
- )}
171
- />
172
- </FormGroup>
173
- </ResponsiveWrapper>
174
- </Stack>
175
- </div>
176
-
177
- <ButtonSet
178
- className={classNames({
179
- [styles.tablet]: isTablet,
180
- [styles.desktop]: !isTablet,
181
- })}>
182
- <Button className={styles.buttonContainer} kind="secondary" onClick={() => closeWorkspace()}>
183
- {t('cancel', 'Cancel')}
184
- </Button>
185
- <Button className={styles.buttonContainer} disabled={isSubmitting || !isDirty} kind="primary" type="submit">
186
- {isSubmitting ? (
187
- <span className={styles.inlineLoading}>
188
- {t('submitting', 'Submitting' + '...')}
189
- <InlineLoading status="active" iconDescription="Loading" />
190
- </span>
191
- ) : (
192
- t('saveAndClose', 'Save & close')
193
- )}
194
- </Button>
195
- </ButtonSet>
196
- </Form>
123
+ <Workspace2 title={t('addLocation', 'Add Location')} hasUnsavedChanges={hasUnsavedChanges}>
124
+ <Form onSubmit={handleSubmit(onSubmit)} className={styles.form}>
125
+ <div className={styles.formContainer}>
126
+ <Stack gap={3}>
127
+ <ResponsiveWrapper>
128
+ <FormGroup legendText="">
129
+ <Controller
130
+ control={control}
131
+ name="name"
132
+ render={({ field }) => (
133
+ <TextInput
134
+ id="locationName"
135
+ placeholder={t('locationPlaceholder', 'Add a location')}
136
+ labelText={t('locationName', 'Location Name')}
137
+ value={field.value}
138
+ onChange={field.onChange}
139
+ invalid={!!errors.name?.message}
140
+ invalidText={errors.name?.message}
141
+ disabled={hasLocationAttributes}
142
+ />
143
+ )}
144
+ />
145
+ </FormGroup>
146
+ </ResponsiveWrapper>
147
+
148
+ <ResponsiveWrapper>
149
+ <FormGroup legendText="">
150
+ <Controller
151
+ control={control}
152
+ name="tags"
153
+ render={({ field: { onChange, value, ref } }) => (
154
+ <FilterableMultiSelect
155
+ id="locationTags"
156
+ titleText={t('selectTags', 'Select tag(s)')}
157
+ placeholder={t('selectTagPlaceholder', 'Select a tag')}
158
+ items={Tags || []}
159
+ selectedItems={(value || []).map(
160
+ (selected) => Tags?.find((tag) => tag.uuid === selected.uuid) || selected,
161
+ )}
162
+ onChange={({ selectedItems }) => onChange(selectedItems || [])}
163
+ itemToString={(item) => (item && typeof item === 'object' ? item.display : '')}
164
+ selectionFeedback="top-after-reopen"
165
+ invalid={!!errors.tags?.message}
166
+ invalidText={errors.tags?.message}
167
+ disabled={!Tags?.length}
168
+ ref={ref}
169
+ />
170
+ )}
171
+ />
172
+ </FormGroup>
173
+ </ResponsiveWrapper>
174
+ </Stack>
175
+ </div>
176
+
177
+ <ButtonSet
178
+ className={classNames({
179
+ [styles.tablet]: isTablet,
180
+ [styles.desktop]: !isTablet,
181
+ })}>
182
+ <Button className={styles.buttonContainer} kind="secondary" onClick={() => closeWorkspace()}>
183
+ {t('cancel', 'Cancel')}
184
+ </Button>
185
+ <Button className={styles.buttonContainer} disabled={isSubmitting || !isDirty} kind="primary" type="submit">
186
+ {isSubmitting ? (
187
+ <span className={styles.inlineLoading}>
188
+ {t('submitting', 'Submitting' + '...')}
189
+ <InlineLoading status="active" iconDescription="Loading" />
190
+ </span>
191
+ ) : (
192
+ t('saveAndClose', 'Save & close')
193
+ )}
194
+ </Button>
195
+ </ButtonSet>
196
+ </Form>
197
+ </Workspace2>
197
198
  );
198
199
  };
199
200
 
@@ -1,27 +1,29 @@
1
- import { Button, ButtonSet, FilterableMultiSelect, Form, FormGroup, InlineLoading, Stack } from '@carbon/react';
1
+ import React, { useEffect, useState } from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import { mutate } from 'swr';
4
+ import classNames from 'classnames';
5
+ import { z } from 'zod';
6
+ import { Controller, useForm } from 'react-hook-form';
2
7
  import { zodResolver } from '@hookform/resolvers/zod';
3
8
  import {
4
- type DefaultWorkspaceProps,
9
+ Workspace2,
10
+ Workspace2DefinitionProps,
11
+ useLayoutType,
5
12
  ResponsiveWrapper,
6
13
  restBaseUrl,
7
14
  showSnackbar,
8
- useLayoutType,
9
15
  } from '@openmrs/esm-framework';
10
- import classNames from 'classnames';
11
- import React, { useEffect, useState } from 'react';
12
- import { Controller, useForm } from 'react-hook-form';
13
- import { useTranslation } from 'react-i18next';
14
- import { mutate } from 'swr';
15
- import { z } from 'zod';
16
+ import { Button, ButtonSet, FilterableMultiSelect, Form, FormGroup, InlineLoading, Stack } from '@carbon/react';
17
+
16
18
  import { LocationAutosuggest } from '../../auto-suggest/location-autosuggest.component';
17
19
  import ResultsTile from '../../common/results-tile.component';
18
- import { extractErrorMessagesFromResponse } from '../../helpers';
19
- import { saveOrUpdateLocation } from '../../hooks/useLocation';
20
20
  import { useLocationTags } from '../../hooks/useLocationTags';
21
+ import { saveOrUpdateLocation } from '../../hooks/useLocation';
22
+ import { extractErrorMessagesFromResponse } from '../../helpers';
21
23
  import { LocationResponse } from '../../types';
22
24
  import styles from './search-location.workspace.scss';
23
25
 
24
- type AddLocationWorkspaceProps = DefaultWorkspaceProps & {
26
+ type AddLocationWorkspaceProps = {
25
27
  location?: LocationResponse;
26
28
  };
27
29
 
@@ -37,17 +39,15 @@ const locationFormSchema = z.object({
37
39
 
38
40
  type LocationFormType = z.infer<typeof locationFormSchema>;
39
41
 
40
- const SearchLocationWorkspace: React.FC<AddLocationWorkspaceProps> = ({
42
+ const SearchLocationWorkspace: React.FC<Workspace2DefinitionProps<AddLocationWorkspaceProps, {}, {}>> = ({
41
43
  closeWorkspace,
42
- closeWorkspaceWithSavedChanges,
43
- promptBeforeClosing,
44
- location,
44
+ workspaceProps: { location = {} as LocationResponse },
45
45
  }) => {
46
46
  const { t } = useTranslation();
47
47
  const isTablet = useLayoutType() === 'tablet';
48
48
  const { locationTagList: Tags } = useLocationTags();
49
49
  const [selectedLocation, setSelectedLocation] = useState<LocationResponse | null>(null);
50
-
50
+ const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
51
51
  const handleMutation = () => {
52
52
  const url = `${restBaseUrl}/location`;
53
53
  mutate((key) => typeof key === 'string' && key.startsWith(url), undefined, { revalidate: true });
@@ -56,7 +56,6 @@ const SearchLocationWorkspace: React.FC<AddLocationWorkspaceProps> = ({
56
56
  const {
57
57
  handleSubmit,
58
58
  control,
59
- getValues,
60
59
  setValue,
61
60
  reset,
62
61
  formState: { isSubmitting, isDirty, errors },
@@ -89,7 +88,7 @@ const SearchLocationWorkspace: React.FC<AddLocationWorkspaceProps> = ({
89
88
  });
90
89
 
91
90
  handleMutation();
92
- closeWorkspaceWithSavedChanges();
91
+ closeWorkspace({ discardUnsavedChanges: true });
93
92
  } catch (error: any) {
94
93
  console.error('Error saving location:', error);
95
94
  const errorMessages = extractErrorMessagesFromResponse(error);
@@ -137,78 +136,84 @@ const SearchLocationWorkspace: React.FC<AddLocationWorkspaceProps> = ({
137
136
  };
138
137
 
139
138
  useEffect(() => {
140
- promptBeforeClosing(() => isDirty);
141
- }, [isDirty, promptBeforeClosing]);
139
+ setHasUnsavedChanges(true);
140
+ }, [isDirty, setHasUnsavedChanges]);
142
141
 
143
142
  const isFormReady = (selectedLocation || location) && isDirty;
144
143
 
145
144
  return (
146
- <Form onSubmit={handleSubmit(onSubmit)} className={styles.form}>
147
- <div className={styles.formContainer}>
148
- <Stack gap={3}>
149
- <ResponsiveWrapper>
150
- <FormGroup legendText="">
151
- {!selectedLocation ? (
152
- <LocationAutosuggest
153
- onLocationSelected={handleLocationSelected}
154
- labelText={t('searchForLocation', 'Search for location')}
155
- placeholder={t('searchParentLocation', 'Search for location...')}
156
- />
157
- ) : (
158
- renderSelectedLocationTile()
159
- )}
160
- </FormGroup>
161
- </ResponsiveWrapper>
162
-
163
- <ResponsiveWrapper>
164
- <FormGroup legendText="">
165
- <Controller
166
- control={control}
167
- name="tags"
168
- render={({ field: { onChange, value, ref } }) => (
169
- <FilterableMultiSelect
170
- id="locationTags"
171
- titleText={t('selectTags', 'Select tag(s)')}
172
- placeholder={t('selectTagPlaceholder', 'Select a tag')}
173
- items={Tags || []}
174
- selectedItems={(value || []).map(
175
- (selected) => Tags?.find((tag) => tag.uuid === selected.uuid) || selected,
176
- )}
177
- onChange={({ selectedItems }) => onChange(selectedItems || [])}
178
- itemToString={(item) => (item && typeof item === 'object' ? item.display : '')}
179
- selectionFeedback="top-after-reopen"
180
- invalid={!!errors.tags?.message}
181
- invalidText={errors.tags?.message}
182
- disabled={!Tags?.length}
183
- ref={ref}
145
+ <Workspace2 title={t('searchLocation', 'Search Location')} hasUnsavedChanges={hasUnsavedChanges}>
146
+ <Form onSubmit={handleSubmit(onSubmit)} className={styles.form}>
147
+ <div className={styles.formContainer}>
148
+ <Stack gap={3}>
149
+ <ResponsiveWrapper>
150
+ <FormGroup legendText="">
151
+ {!selectedLocation ? (
152
+ <LocationAutosuggest
153
+ onLocationSelected={handleLocationSelected}
154
+ labelText={t('searchForLocation', 'Search for location')}
155
+ placeholder={t('searchParentLocation', 'Search for location...')}
184
156
  />
157
+ ) : (
158
+ renderSelectedLocationTile()
185
159
  )}
186
- />
187
- </FormGroup>
188
- </ResponsiveWrapper>
189
- </Stack>
190
- </div>
191
-
192
- <ButtonSet
193
- className={classNames({
194
- [styles.tablet]: isTablet,
195
- [styles.desktop]: !isTablet,
196
- })}>
197
- <Button className={styles.buttonContainer} kind="secondary" onClick={() => closeWorkspace()}>
198
- {t('cancel', 'Cancel')}
199
- </Button>
200
- <Button className={styles.buttonContainer} disabled={isSubmitting || !isFormReady} kind="primary" type="submit">
201
- {isSubmitting ? (
202
- <span className={styles.inlineLoading}>
203
- {t('submitting', 'Submitting' + '...')}
204
- <InlineLoading status="active" iconDescription="Loading" />
205
- </span>
206
- ) : (
207
- t('saveAndClose', 'Save & close')
208
- )}
209
- </Button>
210
- </ButtonSet>
211
- </Form>
160
+ </FormGroup>
161
+ </ResponsiveWrapper>
162
+
163
+ <ResponsiveWrapper>
164
+ <FormGroup legendText="">
165
+ <Controller
166
+ control={control}
167
+ name="tags"
168
+ render={({ field: { onChange, value, ref } }) => (
169
+ <FilterableMultiSelect
170
+ id="locationTags"
171
+ titleText={t('selectTags', 'Select tag(s)')}
172
+ placeholder={t('selectTagPlaceholder', 'Select a tag')}
173
+ items={Tags || []}
174
+ selectedItems={(value || []).map(
175
+ (selected) => Tags?.find((tag) => tag.uuid === selected.uuid) || selected,
176
+ )}
177
+ onChange={({ selectedItems }) => onChange(selectedItems || [])}
178
+ itemToString={(item) => (item && typeof item === 'object' ? item.display : '')}
179
+ selectionFeedback="top-after-reopen"
180
+ invalid={!!errors.tags?.message}
181
+ invalidText={errors.tags?.message}
182
+ disabled={!Tags?.length}
183
+ ref={ref}
184
+ />
185
+ )}
186
+ />
187
+ </FormGroup>
188
+ </ResponsiveWrapper>
189
+ </Stack>
190
+ </div>
191
+
192
+ <ButtonSet
193
+ className={classNames({
194
+ [styles.tablet]: isTablet,
195
+ [styles.desktop]: !isTablet,
196
+ })}>
197
+ <Button className={styles.buttonContainer} kind="secondary" onClick={() => closeWorkspace()}>
198
+ {t('cancel', 'Cancel')}
199
+ </Button>
200
+ <Button
201
+ className={styles.buttonContainer}
202
+ disabled={isSubmitting || !isFormReady}
203
+ kind="primary"
204
+ type="submit">
205
+ {isSubmitting ? (
206
+ <span className={styles.inlineLoading}>
207
+ {t('submitting', 'Submitting' + '...')}
208
+ <InlineLoading status="active" iconDescription="Loading" />
209
+ </span>
210
+ ) : (
211
+ t('saveAndClose', 'Save & close')
212
+ )}
213
+ </Button>
214
+ </ButtonSet>
215
+ </Form>
216
+ </Workspace2>
212
217
  );
213
218
  };
214
219