@kenyaemr/esm-admin-app 5.4.4-pre.142 → 5.4.4-pre.15

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 (279) hide show
  1. package/package.json +1 -1
  2. package/src/components/confirm-modal/confirmation-operation.test.tsx +8 -19
  3. package/src/components/dashboard/dashboard.component.tsx +4 -1
  4. package/src/components/empty-state/empty-state-log.test.tsx +3 -4
  5. package/src/components/facility-setup/constant/index.ts +3 -0
  6. package/src/components/facility-setup/facility-info.component.tsx +176 -106
  7. package/src/components/facility-setup/facility-info.scss +39 -1
  8. package/src/components/facility-setup/facility-setup.component.tsx +2 -2
  9. package/src/components/facility-setup/type/index.ts +61 -0
  10. package/src/components/facility-setup/useFacilityRegistry.ts +29 -0
  11. package/src/components/global-property/dashboard/global-property-dashboard.component.tsx +23 -0
  12. package/src/components/global-property/dashboard/global-property-dashboard.scss +6 -0
  13. package/src/components/global-property/hooks/useGlobalProperty.ts +64 -0
  14. package/src/components/global-property/index.ts +14 -0
  15. package/src/components/global-property/modal/delete-global-property-modal.component.tsx +71 -0
  16. package/src/components/global-property/modal/delete-global-property-modal.test.tsx +131 -0
  17. package/src/components/global-property/table/global-property-table.component.tsx +249 -0
  18. package/src/components/global-property/table/global-property-table.scss +34 -0
  19. package/src/components/global-property/table/global-property-table.test.tsx +198 -0
  20. package/src/components/global-property/workspace/global-property-form-schema.ts +32 -0
  21. package/src/components/global-property/workspace/global-property.workspace.scss +40 -0
  22. package/src/components/global-property/workspace/global-property.workspace.test.tsx +172 -0
  23. package/src/components/global-property/workspace/global-property.workspace.tsx +260 -0
  24. package/src/components/hook/healthWorkerRegistry.ts +78 -0
  25. package/src/components/hook/useProfessionalRegistryEnums.ts +59 -0
  26. package/src/components/logs-table/operation-log-table.component.tsx +87 -75
  27. package/src/components/logs-table/operation-log.test.tsx +134 -28
  28. package/src/components/modal/hwr-confirmation.modal.scss +80 -4
  29. package/src/components/modal/hwr-confirmation.modal.tsx +118 -128
  30. package/src/components/modal/hwr-sync.modal.tsx +194 -106
  31. package/src/components/users/manage-users/user-details/user-detail.scss +167 -39
  32. package/src/components/users/manage-users/user-details/user-details.component.tsx +130 -122
  33. package/src/components/users/manage-users/user-management.workspace.scss +233 -95
  34. package/src/components/users/manage-users/user-management.workspace.tsx +481 -375
  35. package/src/components/users/userManagementFormSchema.tsx +17 -8
  36. package/src/config-schema.ts +48 -68
  37. package/src/index.ts +9 -1
  38. package/src/root.component.tsx +2 -0
  39. package/src/routes.json +20 -1
  40. package/src/types/index.ts +11 -2
  41. package/translations/am.json +175 -10
  42. package/translations/en.json +108 -40
  43. package/translations/fr.json +171 -101
  44. package/translations/sw.json +312 -147
  45. package/.turbo/turbo-build.log +0 -6
  46. package/dist/1074.js +0 -1
  47. package/dist/1074.js.map +0 -1
  48. package/dist/12.js +0 -17
  49. package/dist/12.js.map +0 -1
  50. package/dist/1201.js +0 -1
  51. package/dist/1201.js.map +0 -1
  52. package/dist/1242.js +0 -1
  53. package/dist/1242.js.map +0 -1
  54. package/dist/1311.js +0 -1
  55. package/dist/1311.js.map +0 -1
  56. package/dist/1462.js +0 -1
  57. package/dist/1462.js.map +0 -1
  58. package/dist/1469.js +0 -1
  59. package/dist/1469.js.map +0 -1
  60. package/dist/1506.js +0 -13
  61. package/dist/1506.js.map +0 -1
  62. package/dist/1718.js +0 -1
  63. package/dist/1718.js.map +0 -1
  64. package/dist/1722.js +0 -1
  65. package/dist/1722.js.map +0 -1
  66. package/dist/1772.js +0 -1
  67. package/dist/1772.js.map +0 -1
  68. package/dist/1889.js +0 -1
  69. package/dist/1889.js.map +0 -1
  70. package/dist/1972.js +0 -1
  71. package/dist/1972.js.map +0 -1
  72. package/dist/1990.js +0 -1
  73. package/dist/1990.js.map +0 -1
  74. package/dist/2016.js +0 -1
  75. package/dist/2016.js.map +0 -1
  76. package/dist/2080.js +0 -1
  77. package/dist/2080.js.map +0 -1
  78. package/dist/2096.js +0 -1
  79. package/dist/2096.js.map +0 -1
  80. package/dist/2153.js +0 -1
  81. package/dist/2153.js.map +0 -1
  82. package/dist/216.js +0 -1
  83. package/dist/216.js.map +0 -1
  84. package/dist/2270.js +0 -1
  85. package/dist/2270.js.map +0 -1
  86. package/dist/2294.js +0 -1
  87. package/dist/2294.js.map +0 -1
  88. package/dist/2345.js +0 -1
  89. package/dist/2345.js.map +0 -1
  90. package/dist/2402.js +0 -1
  91. package/dist/2402.js.map +0 -1
  92. package/dist/2500.js +0 -1
  93. package/dist/2500.js.map +0 -1
  94. package/dist/251.js +0 -1
  95. package/dist/251.js.map +0 -1
  96. package/dist/257.js +0 -1
  97. package/dist/257.js.map +0 -1
  98. package/dist/2586.js +0 -1
  99. package/dist/2586.js.map +0 -1
  100. package/dist/2625.js +0 -1
  101. package/dist/2625.js.map +0 -1
  102. package/dist/2652.js +0 -1
  103. package/dist/2652.js.map +0 -1
  104. package/dist/2685.js +0 -1
  105. package/dist/2685.js.map +0 -1
  106. package/dist/2948.js +0 -1
  107. package/dist/2948.js.map +0 -1
  108. package/dist/3089.js +0 -1
  109. package/dist/3089.js.map +0 -1
  110. package/dist/3190.js +0 -1
  111. package/dist/3190.js.map +0 -1
  112. package/dist/3224.js +0 -1
  113. package/dist/3224.js.map +0 -1
  114. package/dist/3366.js +0 -1
  115. package/dist/3366.js.map +0 -1
  116. package/dist/349.js +0 -1
  117. package/dist/349.js.map +0 -1
  118. package/dist/3548.js +0 -1
  119. package/dist/3548.js.map +0 -1
  120. package/dist/3571.js +0 -1
  121. package/dist/3571.js.map +0 -1
  122. package/dist/3691.js +0 -1
  123. package/dist/3691.js.map +0 -1
  124. package/dist/3775.js +0 -1
  125. package/dist/3775.js.map +0 -1
  126. package/dist/3816.js +0 -1
  127. package/dist/3816.js.map +0 -1
  128. package/dist/3852.js +0 -1
  129. package/dist/3852.js.map +0 -1
  130. package/dist/3906.js +0 -1
  131. package/dist/3906.js.map +0 -1
  132. package/dist/3963.js +0 -1
  133. package/dist/3963.js.map +0 -1
  134. package/dist/405.js +0 -1
  135. package/dist/405.js.map +0 -1
  136. package/dist/4296.js +0 -1
  137. package/dist/4296.js.map +0 -1
  138. package/dist/4337.js +0 -1
  139. package/dist/4337.js.map +0 -1
  140. package/dist/4735.js +0 -1
  141. package/dist/4735.js.map +0 -1
  142. package/dist/4744.js +0 -1
  143. package/dist/4744.js.map +0 -1
  144. package/dist/4813.js +0 -2
  145. package/dist/4813.js.map +0 -1
  146. package/dist/4858.js +0 -1
  147. package/dist/4858.js.map +0 -1
  148. package/dist/487.js +0 -1
  149. package/dist/487.js.map +0 -1
  150. package/dist/4970.js +0 -1
  151. package/dist/4970.js.map +0 -1
  152. package/dist/5202.js +0 -1
  153. package/dist/5202.js.map +0 -1
  154. package/dist/5294.js +0 -1
  155. package/dist/5294.js.map +0 -1
  156. package/dist/545.js +0 -1
  157. package/dist/545.js.map +0 -1
  158. package/dist/552.js +0 -1
  159. package/dist/552.js.map +0 -1
  160. package/dist/5592.js +0 -1
  161. package/dist/5592.js.map +0 -1
  162. package/dist/5669.js +0 -1
  163. package/dist/5669.js.map +0 -1
  164. package/dist/5884.js +0 -1
  165. package/dist/5884.js.map +0 -1
  166. package/dist/5940.js +0 -1
  167. package/dist/5940.js.map +0 -1
  168. package/dist/6092.js +0 -1
  169. package/dist/6092.js.map +0 -1
  170. package/dist/6155.js +0 -1
  171. package/dist/6155.js.map +0 -1
  172. package/dist/6178.js +0 -1
  173. package/dist/6178.js.map +0 -1
  174. package/dist/6399.js +0 -1
  175. package/dist/6399.js.map +0 -1
  176. package/dist/6456.js +0 -1
  177. package/dist/6466.js +0 -3
  178. package/dist/6466.js.map +0 -1
  179. package/dist/6492.js +0 -1
  180. package/dist/6492.js.map +0 -1
  181. package/dist/6676.js +0 -1
  182. package/dist/6676.js.map +0 -1
  183. package/dist/6800.js +0 -1
  184. package/dist/6800.js.map +0 -1
  185. package/dist/6976.js +0 -1
  186. package/dist/6976.js.map +0 -1
  187. package/dist/7005.js +0 -1
  188. package/dist/7005.js.map +0 -1
  189. package/dist/7201.js +0 -1
  190. package/dist/7201.js.map +0 -1
  191. package/dist/7210.js +0 -1
  192. package/dist/7210.js.map +0 -1
  193. package/dist/7234.js +0 -1
  194. package/dist/7234.js.map +0 -1
  195. package/dist/7261.js +0 -1
  196. package/dist/7261.js.map +0 -1
  197. package/dist/7326.js +0 -1
  198. package/dist/7463.js +0 -1
  199. package/dist/7463.js.map +0 -1
  200. package/dist/7528.js +0 -1
  201. package/dist/7528.js.map +0 -1
  202. package/dist/7584.js +0 -1
  203. package/dist/7584.js.map +0 -1
  204. package/dist/7607.js +0 -1
  205. package/dist/7717.js +0 -1
  206. package/dist/7717.js.map +0 -1
  207. package/dist/7737.js +0 -1
  208. package/dist/7737.js.map +0 -1
  209. package/dist/7739.js +0 -1
  210. package/dist/7739.js.map +0 -1
  211. package/dist/7765.js +0 -1
  212. package/dist/7765.js.map +0 -1
  213. package/dist/7820.js +0 -1
  214. package/dist/7820.js.map +0 -1
  215. package/dist/7844.js +0 -1
  216. package/dist/7844.js.map +0 -1
  217. package/dist/7866.js +0 -1
  218. package/dist/7866.js.map +0 -1
  219. package/dist/7916.js +0 -1
  220. package/dist/7916.js.map +0 -1
  221. package/dist/7971.js +0 -1
  222. package/dist/7971.js.map +0 -1
  223. package/dist/8159.js +0 -7
  224. package/dist/8159.js.map +0 -1
  225. package/dist/8244.js +0 -1
  226. package/dist/8244.js.map +0 -1
  227. package/dist/8262.js +0 -1
  228. package/dist/8262.js.map +0 -1
  229. package/dist/834.js +0 -1
  230. package/dist/834.js.map +0 -1
  231. package/dist/8376.js +0 -1
  232. package/dist/8376.js.map +0 -1
  233. package/dist/845.js +0 -1
  234. package/dist/845.js.map +0 -1
  235. package/dist/8546.js +0 -1
  236. package/dist/8546.js.map +0 -1
  237. package/dist/8570.js +0 -1
  238. package/dist/8570.js.map +0 -1
  239. package/dist/87.js +0 -1
  240. package/dist/87.js.map +0 -1
  241. package/dist/8727.js +0 -1
  242. package/dist/8828.js +0 -1
  243. package/dist/8828.js.map +0 -1
  244. package/dist/8860.js +0 -1
  245. package/dist/8860.js.map +0 -1
  246. package/dist/9036.js +0 -1
  247. package/dist/9036.js.map +0 -1
  248. package/dist/9124.js +0 -1
  249. package/dist/9124.js.map +0 -1
  250. package/dist/9182.js +0 -1
  251. package/dist/921.js +0 -1
  252. package/dist/921.js.map +0 -1
  253. package/dist/9404.js +0 -1
  254. package/dist/9404.js.map +0 -1
  255. package/dist/9406.js +0 -1
  256. package/dist/9406.js.map +0 -1
  257. package/dist/9446.js +0 -1
  258. package/dist/9446.js.map +0 -1
  259. package/dist/9449.js +0 -1
  260. package/dist/9449.js.map +0 -1
  261. package/dist/9566.js +0 -5
  262. package/dist/9566.js.map +0 -1
  263. package/dist/9641.js +0 -1
  264. package/dist/9641.js.map +0 -1
  265. package/dist/9711.js +0 -1
  266. package/dist/9711.js.map +0 -1
  267. package/dist/9801.js +0 -1
  268. package/dist/9801.js.map +0 -1
  269. package/dist/9835.js +0 -11
  270. package/dist/9835.js.map +0 -1
  271. package/dist/kenyaemr-esm-admin-app.js +0 -5
  272. package/dist/kenyaemr-esm-admin-app.js.buildmanifest.json +0 -3173
  273. package/dist/kenyaemr-esm-admin-app.js.map +0 -1
  274. package/dist/main.js +0 -5
  275. package/dist/main.js.map +0 -1
  276. package/dist/routes.json +0 -1
  277. package/src/components/facility-setup/facility-setup.resource.tsx +0 -7
  278. package/src/components/hook/healthWorkerAdapter.ts +0 -213
  279. package/src/components/hook/useFacilityInfo.tsx +0 -37
@@ -49,17 +49,55 @@ import UserManagementFormSchema from '../userManagementFormSchema';
49
49
  import { ChevronLeft, Query, ChevronRight } from '@carbon/react/icons';
50
50
  import { useSystemUserRoleConfigSetting } from '../../hook/useSystemRoleSetting';
51
51
  import { Provider, User } from '../../../types';
52
- import { searchHealthCareWork, HealthWorkerAdapter, NormalizedPractitioner } from '../../hook/healthWorkerAdapter';
53
52
  import { ROLE_CATEGORIES, SECTIONS } from '../../../constants';
54
53
  import { ConfigObject } from '../../../config-schema';
55
54
  import { mutate } from 'swr';
56
55
  import { createProviderAttribute, updateProviderAttributes } from '../../modal/hwr-sync.resource';
57
56
  import { useUsers } from './user-list/user-list.resource';
57
+ import { searchHealthCareWork, ProfessionalRegistryResponse } from '../../hook/healthWorkerRegistry';
58
+ import {
59
+ useProfessionalRegistryIdentificationTypes,
60
+ useProfessionalRegistryRegulators,
61
+ } from '../../hook/useProfessionalRegistryEnums';
58
62
 
59
63
  type ManageUserWorkspaceProps = DefaultWorkspaceProps & {
60
64
  initialUserValue?: User;
61
65
  };
62
66
 
67
+ /**
68
+ * Pick the active license: latest end-date that hasn't expired yet.
69
+ * Fall back to the most recently expired license if none are current,
70
+ * so the UI surfaces something concrete rather than nothing.
71
+ */
72
+ const pickCurrentLicense = (licenses: ProfessionalRegistryResponse['professional']['licenses']) => {
73
+ if (!licenses || licenses.length === 0) {
74
+ return undefined;
75
+ }
76
+ const sorted = [...licenses]
77
+ .filter((l) => l.license_end)
78
+ .sort((a, b) => new Date(b.license_end).getTime() - new Date(a.license_end).getTime());
79
+ const now = Date.now();
80
+ return sorted.find((l) => new Date(l.license_end).getTime() >= now) ?? sorted[0];
81
+ };
82
+
83
+ /**
84
+ * Savannah returns "Male"/"Female"; OpenMRS uses "M"/"F".
85
+ * Normalize the upstream gender to the OpenMRS shape, or undefined if unknown.
86
+ */
87
+ const normalizeGender = (upstream?: string): 'M' | 'F' | undefined => {
88
+ if (!upstream) {
89
+ return undefined;
90
+ }
91
+ const v = upstream.trim().toUpperCase();
92
+ if (v === 'MALE' || v === 'M') {
93
+ return 'M';
94
+ }
95
+ if (v === 'FEMALE' || v === 'F') {
96
+ return 'F';
97
+ }
98
+ return undefined;
99
+ };
100
+
63
101
  const ManageUserWorkspace: React.FC<Workspace2DefinitionProps<ManageUserWorkspaceProps, {}, {}>> = ({
64
102
  closeWorkspace,
65
103
  workspaceProps: { initialUserValue = {} as User },
@@ -68,7 +106,7 @@ const ManageUserWorkspace: React.FC<Workspace2DefinitionProps<ManageUserWorkspac
68
106
  const isTablet = useLayoutType() === 'tablet';
69
107
  const [activeSection, setActiveSection] = useState('demographic');
70
108
  const [currentIndex, setCurrentIndex] = useState(0);
71
- const [healthWorker, setHealthWorker] = useState(null);
109
+ const [healthWorker, setHealthWorker] = useState<ProfessionalRegistryResponse | null>(null);
72
110
  const { provider = [], loadingProvider, providerError } = useProvider(initialUserValue.systemId);
73
111
  const { users, isLoading: isLoadingUsers, error: usersError } = useUsers();
74
112
  const usernames =
@@ -77,63 +115,77 @@ const ManageUserWorkspace: React.FC<Workspace2DefinitionProps<ManageUserWorkspac
77
115
  const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
78
116
 
79
117
  const { userManagementFormSchema } = UserManagementFormSchema(usernames);
80
-
81
118
  const { providerAttributeType = [] } = useProviderAttributeType();
82
-
83
119
  const { roles = [], isLoading } = useRoles();
84
120
  const { rolesConfig, error } = useSystemUserRoleConfigSetting();
121
+
85
122
  const {
86
- identifierTypes,
87
- regulatorOptions,
88
- licenseBodyUuid,
123
+ providerNationalIdUuid,
89
124
  passportNumberUuid,
90
- personEmailAttributeUuid,
91
- personPhonenumberAttributeUuid,
125
+ providerUniqueIdentifierAttributeTypeUuid,
126
+ externalProviderIdentifierUuid,
127
+ licenseNumberUuid,
128
+ licenseExpiryDateUuid,
129
+ licenseBodyUuid,
130
+ qualificationUuid,
131
+ specialtyUuid,
132
+ providerCadreUuid,
133
+ practiceTypeUuid,
92
134
  phoneNumberUuid,
93
135
  providerAddressUuid,
94
136
  providerHieFhirReference,
95
- qualificationUuid,
96
- providerNationalIdUuid,
97
- licenseNumberUuid,
98
- licenseExpiryDateUuid,
99
- providerUniqueIdentifierAttributeTypeUuid,
137
+ personEmailAttributeUuid,
138
+ personPhonenumberAttributeUuid,
100
139
  } = useConfig<ConfigObject>();
140
+
141
+ const { regulators } = useProfessionalRegistryRegulators();
142
+ const { identificationTypes } = useProfessionalRegistryIdentificationTypes();
143
+
101
144
  const [searchHWR, setSearchHWR] = useState({
102
- identifierType: identifierTypes[0]?.key ?? '',
103
- regulator: regulatorOptions[0]?.key ?? '',
145
+ identifierType: '',
146
+ regulator: '',
104
147
  identifier: '',
105
148
  isHWRLoading: false,
106
149
  });
107
- const defaultIdentifierType = identifierTypes.find((item) => item.key === searchHWR.identifierType);
108
- const defaultRegulator = regulatorOptions.find((item) => item.key === searchHWR.regulator);
109
150
 
110
151
  const attributeTypeMapping = useMemo(() => {
111
152
  return {
112
- licenseNumber: providerAttributeType.find((type) => type.uuid === licenseNumberUuid)?.uuid || '',
113
- licenseExpiry: providerAttributeType.find((type) => type.uuid === licenseExpiryDateUuid)?.uuid || '',
114
153
  providerNationalId: providerAttributeType.find((type) => type.uuid === providerNationalIdUuid)?.uuid || '',
154
+ passportNumber: providerAttributeType.find((type) => type.uuid === passportNumberUuid)?.uuid || '',
155
+ providerUniqueIdentifier:
156
+ providerAttributeType.find((type) => type.uuid === providerUniqueIdentifierAttributeTypeUuid)?.uuid || '',
157
+ externalProviderIdentifier:
158
+ providerAttributeType.find((type) => type.uuid === externalProviderIdentifierUuid)?.uuid || '',
115
159
  providerHieFhirReference:
116
160
  providerAttributeType.find((type) => type.uuid === providerHieFhirReference)?.uuid || '',
117
- qualification: providerAttributeType.find((type) => type.uuid === qualificationUuid)?.uuid || '',
161
+
162
+ licenseNumber: providerAttributeType.find((type) => type.uuid === licenseNumberUuid)?.uuid || '',
163
+ licenseExpiry: providerAttributeType.find((type) => type.uuid === licenseExpiryDateUuid)?.uuid || '',
118
164
  licenseBody: providerAttributeType.find((type) => type.uuid === licenseBodyUuid)?.uuid || '',
165
+ qualification: providerAttributeType.find((type) => type.uuid === qualificationUuid)?.uuid || '',
166
+ specialty: providerAttributeType.find((type) => type.uuid === specialtyUuid)?.uuid || '',
167
+ providerCadre: providerAttributeType.find((type) => type.uuid === providerCadreUuid)?.uuid || '',
168
+ practiceType: providerAttributeType.find((type) => type.uuid === practiceTypeUuid)?.uuid || '',
169
+
119
170
  phoneNumber: providerAttributeType.find((type) => type.uuid === phoneNumberUuid)?.uuid || '',
120
171
  providerAddress: providerAttributeType.find((type) => type.uuid === providerAddressUuid)?.uuid || '',
121
- passportNumber: providerAttributeType.find((type) => type.uuid === passportNumberUuid)?.uuid || '',
122
- providerUniqueIdentifier:
123
- providerAttributeType.find((type) => type.uuid === providerUniqueIdentifierAttributeTypeUuid)?.uuid || '',
124
172
  };
125
173
  }, [
126
- licenseBodyUuid,
127
- licenseExpiryDateUuid,
128
- licenseNumberUuid,
129
- passportNumberUuid,
130
- phoneNumberUuid,
131
- providerAddressUuid,
132
174
  providerAttributeType,
133
- providerHieFhirReference,
134
175
  providerNationalIdUuid,
135
- qualificationUuid,
176
+ passportNumberUuid,
136
177
  providerUniqueIdentifierAttributeTypeUuid,
178
+ externalProviderIdentifierUuid,
179
+ providerHieFhirReference,
180
+ licenseNumberUuid,
181
+ licenseExpiryDateUuid,
182
+ licenseBodyUuid,
183
+ qualificationUuid,
184
+ specialtyUuid,
185
+ providerCadreUuid,
186
+ practiceTypeUuid,
187
+ phoneNumberUuid,
188
+ providerAddressUuid,
137
189
  ]);
138
190
 
139
191
  const providerAttributes = useMemo(() => provider.flatMap((item) => item.attributes || []), [provider]);
@@ -165,7 +217,7 @@ const ManageUserWorkspace: React.FC<Workspace2DefinitionProps<ManageUserWorkspac
165
217
  () => getProviderAttributeValue(attributeTypeMapping.passportNumber),
166
218
  [attributeTypeMapping, getProviderAttributeValue],
167
219
  );
168
- const registrationNumber = useMemo(
220
+ const licenseBody = useMemo(
169
221
  () => getProviderAttributeValue(attributeTypeMapping.licenseBody),
170
222
  [attributeTypeMapping, getProviderAttributeValue],
171
223
  );
@@ -181,25 +233,37 @@ const ManageUserWorkspace: React.FC<Workspace2DefinitionProps<ManageUserWorkspac
181
233
  () => getProviderAttributeValue(attributeTypeMapping.providerUniqueIdentifier),
182
234
  [attributeTypeMapping, getProviderAttributeValue],
183
235
  );
236
+ const specialty = useMemo(
237
+ () => getProviderAttributeValue(attributeTypeMapping.specialty),
238
+ [attributeTypeMapping, getProviderAttributeValue],
239
+ );
240
+ const providerCadre = useMemo(
241
+ () => getProviderAttributeValue(attributeTypeMapping.providerCadre),
242
+ [attributeTypeMapping, getProviderAttributeValue],
243
+ );
244
+ const practiceType = useMemo(
245
+ () => getProviderAttributeValue(attributeTypeMapping.practiceType),
246
+ [attributeTypeMapping, getProviderAttributeValue],
247
+ );
248
+
184
249
  type UserFormSchema = z.infer<typeof userManagementFormSchema>;
250
+
185
251
  const formDefaultValues = useMemo(() => {
186
252
  if (isInitialValuesEmpty) {
187
253
  return {};
188
254
  }
189
255
  const extractNameParts = (display = '') => {
190
256
  const nameParts = display.split(' ');
191
-
192
257
  const [givenName = '', middleName = '', familyName = ''] =
193
258
  nameParts.length === 3 ? nameParts : [nameParts[0], '', nameParts[1] || ''];
194
-
195
259
  return { givenName, middleName, familyName };
196
260
  };
197
261
 
198
262
  return {
199
263
  ...initialUserValue,
200
264
  ...extractNameParts(initialUserValue.person?.display || ''),
201
- phoneNumber: phoneNumber,
202
- email: email,
265
+ phoneNumber,
266
+ email,
203
267
  roles:
204
268
  initialUserValue.roles?.map((role) => ({
205
269
  uuid: role.uuid,
@@ -209,11 +273,14 @@ const ManageUserWorkspace: React.FC<Workspace2DefinitionProps<ManageUserWorkspac
209
273
  gender: initialUserValue.person?.gender,
210
274
  providerLicense: providerLicenseNumber,
211
275
  licenseExpiryDate: licenseExpiryDate ? new Date(licenseExpiryDate) : undefined,
212
- qualification: qualification,
213
- nationalId: nationalId,
214
- passportNumber: passportNumber,
215
- registrationNumber: registrationNumber,
216
- providerUniqueIdentifier: providerUniqueIdentifier,
276
+ qualification,
277
+ nationalId,
278
+ passportNumber,
279
+ licenseBody,
280
+ providerUniqueIdentifier,
281
+ specialty,
282
+ providerCadre,
283
+ practiceType,
217
284
  };
218
285
  }, [
219
286
  isInitialValuesEmpty,
@@ -225,8 +292,11 @@ const ManageUserWorkspace: React.FC<Workspace2DefinitionProps<ManageUserWorkspac
225
292
  qualification,
226
293
  nationalId,
227
294
  passportNumber,
228
- registrationNumber,
295
+ licenseBody,
229
296
  providerUniqueIdentifier,
297
+ specialty,
298
+ providerCadre,
299
+ practiceType,
230
300
  ]);
231
301
 
232
302
  const userFormMethods = useForm<UserFormSchema>({
@@ -236,7 +306,6 @@ const ManageUserWorkspace: React.FC<Workspace2DefinitionProps<ManageUserWorkspac
236
306
  });
237
307
 
238
308
  const { reset, setValue } = userFormMethods;
239
-
240
309
  const { errors, isSubmitting, isDirty } = userFormMethods.formState;
241
310
 
242
311
  useEffect(() => {
@@ -251,54 +320,59 @@ const ManageUserWorkspace: React.FC<Workspace2DefinitionProps<ManageUserWorkspac
251
320
  }
252
321
  }, [isDirty, setHasUnsavedChanges]);
253
322
 
254
- const setPractitionerValuesFromNormalized = (normalized: NormalizedPractitioner) => {
255
- setValue('givenName', normalized.firstName || '');
256
- setValue('middleName', normalized.middleName || '');
257
- setValue('familyName', normalized.lastName || '');
258
- setValue('nationalId', normalized.nationalId || '');
259
- setValue('providerLicense', normalized.licenseNumber || '');
260
- setValue('registrationNumber', normalized.registrationId || '');
261
- setValue('providerUniqueIdentifier', normalized.providerUniqueIdentifier || '');
262
- setValue('phoneNumber', normalized.phoneNumber || '');
263
- setValue('email', normalized.email || '');
264
- setValue('qualification', normalized.qualification || '');
265
- setValue('passportNumber', normalized.passportNumber || '');
266
- setValue(
267
- 'licenseExpiryDate',
268
- normalized.licenseEndDate ? parseDate(normalized.licenseEndDate) : parseDate(t('unknown', 'Unknown')),
269
- );
323
+ const setHealthWorkerValues = (response: ProfessionalRegistryResponse) => {
324
+ const { membership, contacts, identifiers, professional_details, licenses } = response.professional;
325
+ const currentLicense = pickCurrentLicense(licenses);
326
+ const gender = normalizeGender(membership.gender);
327
+
328
+ setValue('givenName', membership.first_name || '');
329
+ setValue('middleName', membership.middle_name || '');
330
+ setValue('familyName', membership.last_name || '');
331
+
332
+ if (gender) {
333
+ setValue('gender', gender, {
334
+ shouldDirty: true,
335
+ shouldTouch: true,
336
+ shouldValidate: true,
337
+ });
338
+ }
339
+
340
+ setValue('nationalId', identifiers?.identification_number || '');
341
+ setValue('passportNumber', '');
342
+ setValue('providerLicense', currentLicense?.external_reference_id || '');
343
+ setValue('licenseBody', membership.licensing_body || '');
344
+ setValue('providerUniqueIdentifier', membership.id || '');
345
+ setValue('phoneNumber', contacts?.phone || '');
346
+ setValue('email', contacts?.email || '');
347
+ setValue('qualification', professional_details?.educational_qualifications || '');
348
+ setValue('specialty', membership.specialty || '');
349
+ setValue('providerCadre', professional_details?.professional_cadre || '');
350
+ setValue('practiceType', professional_details?.practice_type || '');
351
+ setValue('licenseExpiryDate', currentLicense?.license_end ? parseDate(currentLicense.license_end) : undefined);
270
352
  };
271
353
 
272
354
  const handleSearch = async () => {
273
355
  try {
274
- setSearchHWR({ ...searchHWR, isHWRLoading: true });
275
- const unifiedResponse = await searchHealthCareWork(
276
- searchHWR.identifierType,
277
- searchHWR.identifier,
278
- searchHWR.regulator,
279
- );
356
+ setSearchHWR((prev) => ({ ...prev, isHWRLoading: true }));
357
+ const response = await searchHealthCareWork(searchHWR.identifierType, searchHWR.identifier, searchHWR.regulator);
280
358
 
281
- const normalizedData = HealthWorkerAdapter.normalize(unifiedResponse);
282
-
283
- if (!normalizedData) {
359
+ if (!response?.professional) {
284
360
  showModal('hwr-empty-modal', { errorCode: t('noResults', 'No results found') });
285
361
  return;
286
362
  }
287
363
 
288
364
  const dispose = showModal('hwr-confirmation-modal', {
289
- healthWorker: unifiedResponse.data,
290
- normalizedData: normalizedData,
291
- fhirFormat: unifiedResponse.fhirFormat,
365
+ healthWorker: response,
292
366
  onConfirm: () => {
293
367
  dispose();
294
- setPractitionerValuesFromNormalized(normalizedData);
295
- setHealthWorker(unifiedResponse.data);
368
+ setHealthWorkerValues(response);
369
+ setHealthWorker(response);
296
370
  },
297
371
  });
298
- } catch (error) {
299
- showModal('hwr-empty-modal', { errorCode: error.message });
372
+ } catch (err: any) {
373
+ showModal('hwr-empty-modal', { errorCode: err?.message ?? t('searchFailed', 'Search failed') });
300
374
  } finally {
301
- setSearchHWR({ ...searchHWR, isHWRLoading: false });
375
+ setSearchHWR((prev) => ({ ...prev, isHWRLoading: false }));
302
376
  }
303
377
  };
304
378
 
@@ -314,35 +388,27 @@ const ManageUserWorkspace: React.FC<Workspace2DefinitionProps<ManageUserWorkspac
314
388
  attributeType: attributeTypeMapping.licenseExpiry,
315
389
  value: data.licenseExpiryDate ? data.licenseExpiryDate.toISOString() : '',
316
390
  },
317
- { attributeType: attributeTypeMapping.licenseBody, value: data.registrationNumber },
318
- {
319
- attributeType: attributeTypeMapping?.providerHieFhirReference,
320
- value: JSON.stringify(healthWorker),
321
- },
322
- {
323
- attributeType: attributeTypeMapping.providerUniqueIdentifier,
324
- value: data.providerUniqueIdentifier,
325
- },
326
- {
327
- attributeType: attributeTypeMapping.providerNationalId,
328
- value: data.nationalId,
329
- },
330
- {
331
- attributeType: attributeTypeMapping.qualification,
332
- value: data.qualification,
333
- },
334
- {
335
- attributeType: attributeTypeMapping.passportNumber,
336
- value: data.passportNumber,
337
- },
391
+ { attributeType: attributeTypeMapping.licenseBody, value: data.licenseBody },
392
+
393
+ { attributeType: attributeTypeMapping.providerUniqueIdentifier, value: data.providerUniqueIdentifier },
338
394
  {
339
- attributeType: attributeTypeMapping.phoneNumber,
340
- value: data.phoneNumber,
395
+ attributeType: attributeTypeMapping.externalProviderIdentifier,
396
+ value: healthWorker?.professional?.membership?.external_reference_id ?? '',
341
397
  },
342
398
  {
343
- attributeType: attributeTypeMapping.providerAddress,
344
- value: data.email,
399
+ attributeType: attributeTypeMapping.providerHieFhirReference,
400
+ value: healthWorker?.professional?.membership?.id ?? '',
345
401
  },
402
+ { attributeType: attributeTypeMapping.providerNationalId, value: data.nationalId },
403
+ { attributeType: attributeTypeMapping.passportNumber, value: data.passportNumber },
404
+
405
+ { attributeType: attributeTypeMapping.qualification, value: data.qualification },
406
+ { attributeType: attributeTypeMapping.specialty, value: data.specialty },
407
+ { attributeType: attributeTypeMapping.providerCadre, value: data.providerCadre },
408
+ { attributeType: attributeTypeMapping.practiceType, value: data.practiceType },
409
+
410
+ { attributeType: attributeTypeMapping.phoneNumber, value: data.phoneNumber },
411
+ { attributeType: attributeTypeMapping.providerAddress, value: data.email },
346
412
  ].filter((attr) => attr.value),
347
413
  };
348
414
 
@@ -387,7 +453,7 @@ const ManageUserWorkspace: React.FC<Workspace2DefinitionProps<ManageUserWorkspac
387
453
  if (providerResponse.ok) {
388
454
  showSnackbarMessage(t('providerSaved', 'Provider saved successfully'), '', 'success');
389
455
  }
390
- } catch (error) {
456
+ } catch {
391
457
  showSnackbarMessage(
392
458
  t('providerFail', 'Failed to save provider'),
393
459
  t('providerFailedSubtitle', 'An error occurred while creating provider'),
@@ -395,27 +461,20 @@ const ManageUserWorkspace: React.FC<Workspace2DefinitionProps<ManageUserWorkspac
395
461
  );
396
462
  }
397
463
  }
464
+
398
465
  if (editProvider) {
399
466
  const updatableAttributes = [
467
+ { attributeType: licenseBodyUuid, value: data?.licenseBody },
468
+ { attributeType: providerNationalIdUuid, value: data?.nationalId },
469
+ { attributeType: licenseNumberUuid, value: data?.providerLicense },
470
+ { attributeType: passportNumberUuid, value: data?.passportNumber },
471
+ { attributeType: providerUniqueIdentifierAttributeTypeUuid, value: data?.providerUniqueIdentifier },
472
+ { attributeType: specialtyUuid, value: data?.specialty },
473
+ { attributeType: providerCadreUuid, value: data?.providerCadre },
474
+ { attributeType: practiceTypeUuid, value: data?.practiceType },
400
475
  {
401
- attributeType: licenseBodyUuid,
402
- value: data?.registrationNumber,
403
- },
404
- {
405
- attributeType: providerNationalIdUuid,
406
- value: data?.nationalId,
407
- },
408
- {
409
- attributeType: licenseNumberUuid,
410
- value: data?.providerLicense,
411
- },
412
- {
413
- attributeType: passportNumberUuid,
414
- value: data?.passportNumber,
415
- },
416
- {
417
- attributeType: providerUniqueIdentifierAttributeTypeUuid,
418
- value: data?.providerUniqueIdentifier,
476
+ attributeType: externalProviderIdentifierUuid,
477
+ value: healthWorker?.professional?.membership?.external_reference_id,
419
478
  },
420
479
  ].filter((attr) => attr?.value !== undefined && attr?.value !== null && attr?.value !== '');
421
480
 
@@ -425,15 +484,15 @@ const ManageUserWorkspace: React.FC<Workspace2DefinitionProps<ManageUserWorkspac
425
484
  (at) => at?.attributeType?.uuid === attr?.attributeType,
426
485
  )?.uuid;
427
486
 
428
- const payload = {
487
+ const attrPayload = {
429
488
  attributeType: attr?.attributeType,
430
489
  value: attr?.value,
431
490
  };
432
491
 
433
492
  if (!existingAttributes) {
434
- return createProviderAttribute(payload, providerUUID);
493
+ return createProviderAttribute(attrPayload, providerUUID);
435
494
  }
436
- return updateProviderAttributes(payload, providerUUID, existingAttributes);
495
+ return updateProviderAttributes(attrPayload, providerUUID, existingAttributes);
437
496
  }),
438
497
  );
439
498
  showSnackbar({
@@ -445,10 +504,11 @@ const ManageUserWorkspace: React.FC<Workspace2DefinitionProps<ManageUserWorkspac
445
504
  } else {
446
505
  throw new Error('User creation failed');
447
506
  }
507
+
448
508
  handleMutation(`${restBaseUrl}/user`);
449
509
  mutate((key) => typeof key === 'string' && key.startsWith(`${restBaseUrl}/provider`));
450
510
  closeWorkspace({ discardUnsavedChanges: true });
451
- } catch (error) {
511
+ } catch (err) {
452
512
  showSnackbarMessage(
453
513
  t('userSaveFailed', 'Failed to save user'),
454
514
  t('userCreationFailedSubtitle', 'An error occurred while saving user form '),
@@ -457,12 +517,12 @@ const ManageUserWorkspace: React.FC<Workspace2DefinitionProps<ManageUserWorkspac
457
517
  }
458
518
  };
459
519
 
460
- const handleError = (error, response) => {
520
+ const handleError = (err, response) => {
461
521
  showSnackbar({
462
522
  title: String(t('userSaveFailed', 'Fail to save {{error}}', response)),
463
523
  subtitle: String(
464
524
  t('userCreationFailedSubtitle', 'An error occurred while creating user {{errorMessage}}', {
465
- errorMessage: JSON.stringify(error, null, 2),
525
+ errorMessage: JSON.stringify(err, null, 2),
466
526
  }),
467
527
  ),
468
528
  kind: 'error',
@@ -494,12 +554,9 @@ const ManageUserWorkspace: React.FC<Workspace2DefinitionProps<ManageUserWorkspac
494
554
  const hasProviderAccount = activeSection === SECTIONS.PROVIDER;
495
555
 
496
556
  const isSaveAndClose = () => !(hasDemographicInfo || hasLoginInfo || hasProviderAccount);
497
-
498
557
  const getSubmitButtonText = () =>
499
558
  t(isSaveAndClose() ? 'saveAndClose' : 'next', isSaveAndClose() ? 'Save & close' : 'Next');
500
-
501
559
  const getSubmitButtonType = () => (isSaveAndClose() ? 'submit' : 'button');
502
-
503
560
  const getSubmitButtonIcon = () => (isSaveAndClose() ? ChevronLeft : ChevronRight);
504
561
 
505
562
  const handleBackClick = () => {
@@ -535,7 +592,7 @@ const ManageUserWorkspace: React.FC<Workspace2DefinitionProps<ManageUserWorkspac
535
592
  setCurrentIndex(newIndex);
536
593
  }
537
594
  }}>
538
- {steps.map((step, index) => (
595
+ {steps.map((step) => (
539
596
  <ProgressStep key={step.id} label={step.label} className={styles.ProgresStep} />
540
597
  ))}
541
598
  </ProgressIndicator>
@@ -546,199 +603,198 @@ const ManageUserWorkspace: React.FC<Workspace2DefinitionProps<ManageUserWorkspac
546
603
  <Stack className={styles.formStackControl} gap={7}>
547
604
  {hasDemographicInfo && (
548
605
  <ResponsiveWrapper>
549
- <span className={styles.formHeaderSection}>
550
- {t('healthWorkVerify', 'Health worker registry verification')}
551
- </span>
552
- {searchHWR.isHWRLoading ? (
553
- <InlineLoading
554
- className={styles.formLoading}
555
- description={t('pullDetailsfromHWR', 'Pulling data from Health worker registry...')}
556
- />
557
- ) : (
606
+ {isInitialValuesEmpty && (
558
607
  <>
559
- <Column>
560
- <ComboBox
561
- onChange={({ selectedItem }) => {
562
- setSearchHWR({ ...searchHWR, identifierType: selectedItem?.key ?? '' });
563
- }}
564
- id="formIdentifierType"
565
- titleText={t('identificationType', 'Identification Type')}
566
- placeholder={t('chooseIdentifierType', 'Choose identifier type')}
567
- initialSelectedItem={defaultIdentifierType}
568
- items={identifierTypes}
569
- itemToString={(item) => (item ? item.name : '')}
570
- disabled={!isInitialValuesEmpty}
571
- />
572
- </Column>
573
- <Column>
574
- <ComboBox
575
- onChange={({ selectedItem }) => {
576
- setSearchHWR({ ...searchHWR, regulator: selectedItem?.key ?? '' });
577
- }}
578
- id="formRegulatorOptions"
579
- titleText={t('regulator', 'Regulator')}
580
- placeholder={t('chooseRegulatorType', 'Choose regulator option')}
581
- initialSelectedItem={defaultRegulator}
582
- items={regulatorOptions}
583
- itemToString={(item) => (item ? item.name : '')}
584
- disabled={!isInitialValuesEmpty}
585
- />
586
- </Column>
587
- <Column>
588
- <span className={styles.formIdentifierType}>
589
- {t('identifierNumber', 'Identifier number*')}
590
- </span>
591
- <Row className={styles.formRow}>
592
- <Search
593
- labelText={t('enterIdentifierNumber', 'Enter identifier number')}
594
- className={styles.formSearch}
595
- defaultValue={searchHWR.identifier}
596
- placeholder={t('enterIdentifierNumber', 'Enter identifier number')}
597
- id="formSearchHealthWorkers"
598
- disabled={!isInitialValuesEmpty}
599
- onChange={(value) => {
600
- setSearchHWR({ ...searchHWR, identifier: value.target.value });
601
- }}
602
- />
603
- <Button
604
- kind="secondary"
605
- size="md"
606
- renderIcon={Query}
607
- disabled={
608
- !searchHWR.identifier ||
609
- searchHWR.isHWRLoading ||
610
- !searchHWR.identifierType ||
611
- !searchHWR.regulator ||
612
- !isInitialValuesEmpty
613
- }
614
- iconDescription={t('search', 'Search')}
615
- hasIconOnly
616
- className={styles.formSearchButton}
617
- onClick={handleSearch}
618
- />
619
- </Row>
620
- </Column>
621
608
  <span className={styles.formHeaderSection}>
622
- {t('demographicInfo', 'Demographic info')}
609
+ {t('healthWorkVerify', 'Health worker registry verification')}
623
610
  </span>
624
- <ResponsiveWrapper>
625
- <Controller
626
- name="givenName"
627
- control={userFormMethods.control}
628
- render={({ field }) => (
629
- <TextInput
630
- {...field}
631
- id="givenName"
632
- type="text"
633
- labelText={t('givenName', 'Given Name')}
634
- placeholder={t('userGivenName', 'Enter Given Name')}
635
- invalid={!!errors.givenName}
636
- invalidText={errors.givenName?.message}
637
- />
638
- )}
639
- />
640
- </ResponsiveWrapper>
641
- <ResponsiveWrapper>
642
- <Controller
643
- name="middleName"
644
- control={userFormMethods.control}
645
- render={({ field }) => (
646
- <TextInput
647
- {...field}
648
- id="middleName"
649
- labelText={t('middleName', 'Middle Name')}
650
- placeholder={t('middleName', 'Middle Name')}
651
- />
652
- )}
653
- />
654
- </ResponsiveWrapper>
655
- <ResponsiveWrapper>
656
- <Controller
657
- name="familyName"
658
- control={userFormMethods.control}
659
- render={({ field }) => (
660
- <TextInput
661
- {...field}
662
- id="familyName"
663
- labelText={t('familyName', 'Family Name')}
664
- placeholder={t('familyName', 'Family Name')}
665
- invalid={!!errors.familyName}
666
- invalidText={errors.familyName?.message}
667
- />
668
- )}
611
+
612
+ {searchHWR.isHWRLoading ? (
613
+ <InlineLoading
614
+ className={styles.formLoading}
615
+ description={t('pullDetailsfromHWR', 'Pulling data from Health worker registry...')}
669
616
  />
670
- </ResponsiveWrapper>
671
- <ResponsiveWrapper>
672
- <Controller
673
- name="phoneNumber"
674
- control={userFormMethods.control}
675
- render={({ field }) => (
676
- <TextInput
677
- {...field}
678
- id="phoneNumber"
679
- type="text"
680
- disabled={isInitialValuesEmpty}
681
- labelText={t('phoneNumber', 'Phone Number')}
682
- placeholder={t('phoneNumber', 'Enter Phone Number')}
683
- invalid={!!errors.phoneNumber}
684
- invalidText={errors.phoneNumber?.message}
617
+ ) : (
618
+ <>
619
+ <Column>
620
+ <ComboBox
621
+ onChange={({ selectedItem }) => {
622
+ setSearchHWR({
623
+ ...searchHWR,
624
+ identifierType: selectedItem?.code ?? '',
625
+ });
626
+ }}
627
+ id="formIdentifierType"
628
+ titleText={t('identificationType', 'Identification Type')}
629
+ placeholder={t('chooseIdentifierType', 'Choose identifier type')}
630
+ items={identificationTypes}
631
+ itemToString={(item) => item?.label ?? ''}
685
632
  />
686
- )}
687
- />
688
- </ResponsiveWrapper>
689
- <ResponsiveWrapper>
690
- <Controller
691
- name="email"
692
- control={userFormMethods.control}
693
- render={({ field }) => (
694
- <TextInput
695
- {...field}
696
- id="email"
697
- type="email"
698
- disabled={isInitialValuesEmpty}
699
- labelText={t('email', 'Email')}
700
- placeholder={t('email', 'Enter Email')}
701
- invalid={!!errors.email}
702
- invalidText={errors.email?.message}
703
- className={styles.checkboxLabelSingleLine}
633
+ </Column>
634
+ <Column>
635
+ <ComboBox
636
+ onChange={({ selectedItem }) => {
637
+ setSearchHWR({
638
+ ...searchHWR,
639
+ regulator: selectedItem?.code ?? '',
640
+ });
641
+ }}
642
+ id="formRegulatorOptions"
643
+ titleText={t('regulator', 'Regulator')}
644
+ placeholder={t('chooseRegulatorType', 'Choose regulator option')}
645
+ items={regulators}
646
+ itemToString={(item) => item?.label ?? ''}
704
647
  />
705
- )}
706
- />
707
- </ResponsiveWrapper>
708
- <ResponsiveWrapper>
709
- <Controller
710
- name="gender"
711
- control={userFormMethods.control}
712
- render={({ field }) => (
713
- <RadioButtonGroup
714
- {...field}
715
- legendText={t('sex', 'Sex')}
716
- orientation="vertical"
717
- invalid={!!errors.gender}
718
- invalidText={errors.gender?.message}>
719
- <RadioButton
720
- value="M"
721
- id="M"
722
- labelText={t('male', 'Male')}
723
- checked={field.value === 'M'}
648
+ </Column>
649
+ <Column>
650
+ <span className={styles.formIdentifierType}>
651
+ {t('identifierNumber', 'Identifier number*')}
652
+ </span>
653
+ <Row className={styles.formRow}>
654
+ <Search
655
+ labelText={t('enterIdentifierNumber', 'Enter identifier number')}
656
+ className={styles.formSearch}
657
+ defaultValue={searchHWR.identifier}
658
+ placeholder={t('enterIdentifierNumber', 'Enter identifier number')}
659
+ id="formSearchHealthWorkers"
660
+ onChange={(value) => {
661
+ setSearchHWR({ ...searchHWR, identifier: value.target.value });
662
+ }}
724
663
  />
725
- <RadioButton
726
- value="F"
727
- id="F"
728
- labelText={t('female', 'Female')}
729
- checked={field.value === 'F'}
664
+ <Button
665
+ kind="secondary"
666
+ size="md"
667
+ renderIcon={Query}
668
+ disabled={
669
+ !searchHWR.identifier ||
670
+ searchHWR.isHWRLoading ||
671
+ !searchHWR.identifierType ||
672
+ !searchHWR.regulator
673
+ }
674
+ iconDescription={t('search', 'Search')}
675
+ hasIconOnly
676
+ className={styles.formSearchButton}
677
+ onClick={handleSearch}
730
678
  />
731
- </RadioButtonGroup>
732
- )}
733
- />
734
- </ResponsiveWrapper>
679
+ </Row>
680
+ </Column>
681
+ </>
682
+ )}
735
683
  </>
736
684
  )}
685
+
686
+ <span className={styles.formHeaderSection}>{t('demographicInfo', 'Demographic info')}</span>
687
+ <ResponsiveWrapper>
688
+ <Controller
689
+ name="givenName"
690
+ control={userFormMethods.control}
691
+ render={({ field }) => (
692
+ <TextInput
693
+ {...field}
694
+ id="givenName"
695
+ type="text"
696
+ labelText={t('givenName', 'Given Name')}
697
+ placeholder={t('userGivenName', 'Enter Given Name')}
698
+ invalid={!!errors.givenName}
699
+ invalidText={errors.givenName?.message}
700
+ />
701
+ )}
702
+ />
703
+ </ResponsiveWrapper>
704
+ <ResponsiveWrapper>
705
+ <Controller
706
+ name="middleName"
707
+ control={userFormMethods.control}
708
+ render={({ field }) => (
709
+ <TextInput
710
+ {...field}
711
+ id="middleName"
712
+ labelText={t('middleName', 'Middle Name')}
713
+ placeholder={t('middleName', 'Middle Name')}
714
+ />
715
+ )}
716
+ />
717
+ </ResponsiveWrapper>
718
+ <ResponsiveWrapper>
719
+ <Controller
720
+ name="familyName"
721
+ control={userFormMethods.control}
722
+ render={({ field }) => (
723
+ <TextInput
724
+ {...field}
725
+ id="familyName"
726
+ labelText={t('familyName', 'Family Name')}
727
+ placeholder={t('familyName', 'Family Name')}
728
+ invalid={!!errors.familyName}
729
+ invalidText={errors.familyName?.message}
730
+ />
731
+ )}
732
+ />
733
+ </ResponsiveWrapper>
734
+ <ResponsiveWrapper>
735
+ <Controller
736
+ name="phoneNumber"
737
+ control={userFormMethods.control}
738
+ render={({ field }) => (
739
+ <TextInput
740
+ {...field}
741
+ id="phoneNumber"
742
+ type="text"
743
+ disabled={isInitialValuesEmpty}
744
+ labelText={t('phoneNumber', 'Phone Number')}
745
+ placeholder={t('phoneNumber', 'Enter Phone Number')}
746
+ invalid={!!errors.phoneNumber}
747
+ invalidText={errors.phoneNumber?.message}
748
+ />
749
+ )}
750
+ />
751
+ </ResponsiveWrapper>
752
+ <ResponsiveWrapper>
753
+ <Controller
754
+ name="email"
755
+ control={userFormMethods.control}
756
+ render={({ field }) => (
757
+ <TextInput
758
+ {...field}
759
+ id="email"
760
+ type="email"
761
+ disabled={isInitialValuesEmpty}
762
+ labelText={t('email', 'Email')}
763
+ placeholder={t('email', 'Enter Email')}
764
+ invalid={!!errors.email}
765
+ invalidText={errors.email?.message}
766
+ className={styles.checkboxLabelSingleLine}
767
+ />
768
+ )}
769
+ />
770
+ </ResponsiveWrapper>
771
+ <ResponsiveWrapper>
772
+ <Controller
773
+ name="gender"
774
+ control={userFormMethods.control}
775
+ render={({ field: { onChange, value } }) => (
776
+ <RadioButtonGroup
777
+ legendText={t('sex', 'Sex')}
778
+ orientation="vertical"
779
+ name="gender"
780
+ valueSelected={value ?? ''}
781
+ key={value ?? 'empty'}
782
+ onChange={(selected) => onChange(selected)}
783
+ invalid={!!errors.gender}
784
+ invalidText={errors.gender?.message}>
785
+ <RadioButton value="M" id="genderMale" labelText={t('male', 'Male')} />
786
+ <RadioButton value="F" id="genderFemale" labelText={t('female', 'Female')} />
787
+ </RadioButtonGroup>
788
+ )}
789
+ />
790
+ </ResponsiveWrapper>
737
791
  </ResponsiveWrapper>
738
792
  )}
793
+
739
794
  {hasProviderAccount && (
740
795
  <ResponsiveWrapper>
741
796
  <span className={styles.formHeaderSection}>{t('providerDetails', 'Provider details')}</span>
797
+
742
798
  <ResponsiveWrapper>
743
799
  <Controller
744
800
  name="providerUniqueIdentifier"
@@ -748,17 +804,16 @@ const ManageUserWorkspace: React.FC<Workspace2DefinitionProps<ManageUserWorkspac
748
804
  {...field}
749
805
  id="providerUniqueIdentifier"
750
806
  type="text"
807
+ disabled
751
808
  labelText={t('providerUniqueIdentifier', 'Provider Unique Identifier')}
752
- placeholder={t(
753
- 'providerUniqueIdentifierPlaceholder',
754
- 'Enter Provider Unqiue Identifier',
755
- )}
809
+ placeholder={t('providerUniqueIdentifierPlaceholder', 'e.g. PUID-0002011-6')}
756
810
  invalid={!!errors.providerUniqueIdentifier}
757
811
  invalidText={errors.providerUniqueIdentifier?.message}
758
812
  />
759
813
  )}
760
814
  />
761
815
  </ResponsiveWrapper>
816
+
762
817
  <ResponsiveWrapper>
763
818
  <Controller
764
819
  name="nationalId"
@@ -769,8 +824,8 @@ const ManageUserWorkspace: React.FC<Workspace2DefinitionProps<ManageUserWorkspac
769
824
  id="nationalId"
770
825
  disabled={isInitialValuesEmpty}
771
826
  type="text"
772
- labelText={t('nationalID', 'National id')}
773
- placeholder={t('nationalID', 'National id')}
827
+ labelText={t('nationalID', 'National ID')}
828
+ placeholder={t('nationalID', 'National ID')}
774
829
  invalid={!!errors.nationalId}
775
830
  invalidText={errors.nationalId?.message}
776
831
  className={styles.checkboxLabelSingleLine}
@@ -778,6 +833,7 @@ const ManageUserWorkspace: React.FC<Workspace2DefinitionProps<ManageUserWorkspac
778
833
  )}
779
834
  />
780
835
  </ResponsiveWrapper>
836
+
781
837
  <ResponsiveWrapper>
782
838
  <Controller
783
839
  name="passportNumber"
@@ -790,13 +846,14 @@ const ManageUserWorkspace: React.FC<Workspace2DefinitionProps<ManageUserWorkspac
790
846
  type="text"
791
847
  labelText={t('passportNumber', 'Passport number')}
792
848
  placeholder={t('passportNumber', 'Passport number')}
793
- invalid={!!errors.nationalId}
794
- invalidText={errors.nationalId?.message}
849
+ invalid={!!errors.passportNumber}
850
+ invalidText={errors.passportNumber?.message}
795
851
  className={styles.checkboxLabelSingleLine}
796
852
  />
797
853
  )}
798
854
  />
799
855
  </ResponsiveWrapper>
856
+
800
857
  <ResponsiveWrapper>
801
858
  <Controller
802
859
  name="providerLicense"
@@ -806,31 +863,33 @@ const ManageUserWorkspace: React.FC<Workspace2DefinitionProps<ManageUserWorkspac
806
863
  {...field}
807
864
  id="providerLicense"
808
865
  type="text"
809
- disabled={isInitialValuesEmpty}
866
+ disabled
810
867
  labelText={t('providerLicense', 'License Number')}
811
- placeholder={t('providerLicense', 'License Number')}
868
+ placeholder={t('providerLicensePlaceholder', 'e.g. GP/2026/638333')}
812
869
  className={styles.checkboxLabelSingleLine}
813
870
  />
814
871
  )}
815
872
  />
816
873
  </ResponsiveWrapper>
874
+
817
875
  <ResponsiveWrapper>
818
876
  <Controller
819
- name="registrationNumber"
877
+ name="licenseBody"
820
878
  control={userFormMethods.control}
821
879
  render={({ field }) => (
822
880
  <TextInput
823
881
  {...field}
824
- id="registrationNumber"
882
+ id="licenseBody"
825
883
  type="text"
826
- disabled={isInitialValuesEmpty}
827
- labelText={t('registrationNumber', 'Registration Number')}
828
- placeholder={t('registrationNumber', 'Registration Number')}
884
+ disabled
885
+ labelText={t('licenseBody', 'License body')}
886
+ placeholder={t('licenseBodyPlaceholder', 'e.g. KMPDC')}
829
887
  className={styles.checkboxLabelSingleLine}
830
888
  />
831
889
  )}
832
890
  />
833
891
  </ResponsiveWrapper>
892
+
834
893
  <ResponsiveWrapper>
835
894
  <Controller
836
895
  name="qualification"
@@ -839,10 +898,10 @@ const ManageUserWorkspace: React.FC<Workspace2DefinitionProps<ManageUserWorkspac
839
898
  <TextInput
840
899
  {...field}
841
900
  id="qualification"
842
- type="qualification"
901
+ type="text"
843
902
  disabled
844
903
  labelText={t('qualification', 'Qualification')}
845
- placeholder={t('qualification', 'Qualification')}
904
+ placeholder={t('qualificationPlaceholder', 'e.g. MBChB(UON)2009')}
846
905
  invalid={!!errors.qualification}
847
906
  invalidText={errors.qualification?.message}
848
907
  className={styles.checkboxLabelSingleLine}
@@ -851,6 +910,60 @@ const ManageUserWorkspace: React.FC<Workspace2DefinitionProps<ManageUserWorkspac
851
910
  />
852
911
  </ResponsiveWrapper>
853
912
 
913
+ <ResponsiveWrapper>
914
+ <Controller
915
+ name="specialty"
916
+ control={userFormMethods.control}
917
+ render={({ field }) => (
918
+ <TextInput
919
+ {...field}
920
+ id="specialty"
921
+ type="text"
922
+ disabled
923
+ labelText={t('specialty', 'Specialty')}
924
+ placeholder={t('specialtyPlaceholder', 'e.g. MEDICAL DOCTOR')}
925
+ className={styles.checkboxLabelSingleLine}
926
+ />
927
+ )}
928
+ />
929
+ </ResponsiveWrapper>
930
+
931
+ <ResponsiveWrapper>
932
+ <Controller
933
+ name="providerCadre"
934
+ control={userFormMethods.control}
935
+ render={({ field }) => (
936
+ <TextInput
937
+ {...field}
938
+ id="providerCadre"
939
+ type="text"
940
+ disabled
941
+ labelText={t('providerCadre', 'Provider cadre')}
942
+ placeholder={t('providerCadrePlaceholder', 'e.g. MEDICINE')}
943
+ className={styles.checkboxLabelSingleLine}
944
+ />
945
+ )}
946
+ />
947
+ </ResponsiveWrapper>
948
+
949
+ <ResponsiveWrapper>
950
+ <Controller
951
+ name="practiceType"
952
+ control={userFormMethods.control}
953
+ render={({ field }) => (
954
+ <TextInput
955
+ {...field}
956
+ id="practiceType"
957
+ type="text"
958
+ disabled
959
+ labelText={t('practiceType', 'Practice type')}
960
+ placeholder={t('practiceTypePlaceholder', 'e.g. Clinical Practice')}
961
+ className={styles.checkboxLabelSingleLine}
962
+ />
963
+ )}
964
+ />
965
+ </ResponsiveWrapper>
966
+
854
967
  <ResponsiveWrapper>
855
968
  <Controller
856
969
  name="licenseExpiryDate"
@@ -879,69 +992,67 @@ const ManageUserWorkspace: React.FC<Workspace2DefinitionProps<ManageUserWorkspac
879
992
  )}
880
993
  />
881
994
  </ResponsiveWrapper>
995
+
882
996
  {loadingProvider || providerError ? (
883
997
  <InlineLoading status="active" iconDescription="Loading" description="Loading data..." />
884
998
  ) : provider.length > 0 ? (
885
- <>
886
- <ResponsiveWrapper>
887
- <Controller
888
- name="systemId"
889
- control={userFormMethods.control}
890
- render={({ field }) => (
891
- <TextInput
892
- {...field}
893
- id="systemeId"
894
- type="text"
895
- labelText={t('providerId', 'Provider Id')}
896
- placeholder={t('providerId', 'Provider Id')}
897
- invalid={!!errors.systemId}
898
- invalidText={errors.systemId?.message}
899
- className={styles.checkboxLabelSingleLine}
900
- />
901
- )}
902
- />
903
- <Controller
904
- name="isEditProvider"
905
- control={userFormMethods.control}
906
- render={({ field }) => (
907
- <CheckboxGroup
908
- legendText={t('editProvider', 'Edit Provider Details')}
909
- className={styles.multilineCheckboxLabel}>
910
- <Checkbox
911
- className={styles.checkboxLabelSingleLine}
912
- id="isEditProvider"
913
- labelText={t('EditProviderDetails', 'Edit provider details?')}
914
- checked={field.value || false}
915
- onChange={(e) => field.onChange(e.target.checked)}
916
- />
917
- </CheckboxGroup>
918
- )}
919
- />
920
- </ResponsiveWrapper>
921
- </>
922
- ) : (
923
- <>
999
+ <ResponsiveWrapper>
1000
+ <Controller
1001
+ name="systemId"
1002
+ control={userFormMethods.control}
1003
+ render={({ field }) => (
1004
+ <TextInput
1005
+ {...field}
1006
+ id="systemeId"
1007
+ type="text"
1008
+ labelText={t('providerId', 'Provider Id')}
1009
+ placeholder={t('providerId', 'Provider Id')}
1010
+ invalid={!!errors.systemId}
1011
+ invalidText={errors.systemId?.message}
1012
+ className={styles.checkboxLabelSingleLine}
1013
+ />
1014
+ )}
1015
+ />
924
1016
  <Controller
925
- name="providerIdentifiers"
1017
+ name="isEditProvider"
926
1018
  control={userFormMethods.control}
927
1019
  render={({ field }) => (
928
1020
  <CheckboxGroup
929
- legendText={t('providerIdentifiers', 'Provider Details')}
1021
+ legendText={t('editProvider', 'Edit Provider Details')}
930
1022
  className={styles.multilineCheckboxLabel}>
931
1023
  <Checkbox
932
1024
  className={styles.checkboxLabelSingleLine}
933
- id="providerIdentifiersa"
934
- labelText={t('providerIdentifiers', 'Create a Provider account for this user')}
1025
+ id="isEditProvider"
1026
+ labelText={t('EditProviderDetails', 'Edit provider details?')}
935
1027
  checked={field.value || false}
936
1028
  onChange={(e) => field.onChange(e.target.checked)}
937
1029
  />
938
1030
  </CheckboxGroup>
939
1031
  )}
940
1032
  />
941
- </>
1033
+ </ResponsiveWrapper>
1034
+ ) : (
1035
+ <Controller
1036
+ name="providerIdentifiers"
1037
+ control={userFormMethods.control}
1038
+ render={({ field }) => (
1039
+ <CheckboxGroup
1040
+ legendText={t('providerIdentifiers', 'Provider Details')}
1041
+ className={styles.multilineCheckboxLabel}>
1042
+ <Checkbox
1043
+ className={styles.checkboxLabelSingleLine}
1044
+ id="providerIdentifiersa"
1045
+ labelText={t('providerIdentifiers', 'Create a Provider account for this user')}
1046
+ checked={field.value || false}
1047
+ onChange={(e) => field.onChange(e.target.checked)}
1048
+ />
1049
+ </CheckboxGroup>
1050
+ )}
1051
+ />
942
1052
  )}
943
1053
  </ResponsiveWrapper>
944
1054
  )}
1055
+
945
1056
  {hasLoginInfo && (
946
1057
  <ResponsiveWrapper>
947
1058
  <span className={styles.formHeaderSection}>{t('loginInfo', 'Login Info')}</span>
@@ -960,7 +1071,6 @@ const ManageUserWorkspace: React.FC<Workspace2DefinitionProps<ManageUserWorkspac
960
1071
  )}
961
1072
  />
962
1073
  </ResponsiveWrapper>
963
-
964
1074
  <ResponsiveWrapper>
965
1075
  <Controller
966
1076
  name="password"
@@ -1056,7 +1166,6 @@ const ManageUserWorkspace: React.FC<Workspace2DefinitionProps<ManageUserWorkspac
1056
1166
  control={userFormMethods.control}
1057
1167
  render={({ field }) => {
1058
1168
  const selectedRoles = field.value || [];
1059
-
1060
1169
  return (
1061
1170
  <>
1062
1171
  {roles
@@ -1068,7 +1177,6 @@ const ManageUserWorkspace: React.FC<Workspace2DefinitionProps<ManageUserWorkspac
1068
1177
  r.description === role.description &&
1069
1178
  r.uuid === role.uuid,
1070
1179
  );
1071
-
1072
1180
  return (
1073
1181
  <label
1074
1182
  key={role.display}
@@ -1092,7 +1200,6 @@ const ManageUserWorkspace: React.FC<Workspace2DefinitionProps<ManageUserWorkspac
1092
1200
  : selectedRoles.filter(
1093
1201
  (selectedRole) => selectedRole.display !== role.display,
1094
1202
  );
1095
-
1096
1203
  field.onChange(updatedValue);
1097
1204
  }}
1098
1205
  />
@@ -1117,7 +1224,6 @@ const ManageUserWorkspace: React.FC<Workspace2DefinitionProps<ManageUserWorkspac
1117
1224
  <Button kind="secondary" onClick={handleBackClick} className={styles.btn}>
1118
1225
  {t(hasDemographicInfo ? 'cancel' : 'back', hasDemographicInfo ? 'Cancel' : 'Back')}
1119
1226
  </Button>
1120
-
1121
1227
  <Button
1122
1228
  kind="primary"
1123
1229
  type={getSubmitButtonType()}