@kenyaemr/esm-admin-app 5.4.1-pre.2021 → 5.4.1-pre.2025

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.
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":"facilitySetupLeftPanelLink","name":"facility-setup-left-panel-link","slot":"admin-left-panel-slot"}],"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"}],"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.1-pre.2021"}
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":"facilitySetupLeftPanelLink","name":"facility-setup-left-panel-link","slot":"admin-left-panel-slot"}],"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"}],"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.1-pre.2025"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kenyaemr/esm-admin-app",
3
- "version": "5.4.1-pre.2021",
3
+ "version": "5.4.1-pre.2025",
4
4
  "description": "Facilitates the management of ETL tables",
5
5
  "browser": "dist/kenyaemr-esm-admin-app.js",
6
6
  "main": "src/index.ts",
@@ -63,7 +63,10 @@ const HWRConfirmModal: React.FC<HWRConfirmModalProps> = ({ close, onConfirm, hea
63
63
  label={t('healthWorkerName', 'Health worker name')}
64
64
  value={practitioner?.name?.[0]?.text}
65
65
  />
66
-
66
+ <HealthWorkerInfo
67
+ label={t('providerUniqueIdentifier', 'Provider unique identifier')}
68
+ value={practitioner?.id}
69
+ />
67
70
  {practitioner?.telecom?.map((telecom, index) => (
68
71
  <HealthWorkerInfo key={index} label={capitalize(telecom?.system)} value={telecom?.value || '--'} />
69
72
  ))}
@@ -30,6 +30,7 @@ const HWRSyncModal: React.FC<HWRSyncModalProps> = ({ close, provider }) => {
30
30
  qualificationUuid,
31
31
  providerAddressUuid,
32
32
  providerHieFhirReference,
33
+ providerUniqueIdentifierAttributeTypeUuid,
33
34
  } = config;
34
35
 
35
36
  const attributeMapping = {
@@ -89,6 +90,7 @@ const HWRSyncModal: React.FC<HWRSyncModalProps> = ({ close, provider }) => {
89
90
  ?.valueCodeableConcept?.coding?.[0]?.display,
90
91
  nationalId: resource?.identifier?.find((id) => id.type?.coding?.some((code) => code.code === 'national-id'))
91
92
  ?.value,
93
+ providerUniqueIdentifier: resource?.id,
92
94
  };
93
95
 
94
96
  const updatableAttributes = [
@@ -100,6 +102,10 @@ const HWRSyncModal: React.FC<HWRSyncModalProps> = ({ close, provider }) => {
100
102
  { attributeType: providerHieFhirReference, value: JSON.stringify(healthWorker) },
101
103
  { attributeType: providerAddressUuid, value: extractedAttributes.email },
102
104
  { attributeType: providerNationalIdUuid, value: extractedAttributes.nationalId },
105
+ {
106
+ attributeType: providerUniqueIdentifierAttributeTypeUuid,
107
+ value: extractedAttributes.providerUniqueIdentifier,
108
+ },
103
109
  ].filter((attr) => attr.value !== undefined && attr.value !== null && attr.value !== '');
104
110
 
105
111
  await Promise.all(
@@ -26,6 +26,7 @@ interface ProviderAttributes {
26
26
  registrationNumber?: ProviderAttribute;
27
27
  emailAddress?: ProviderAttribute;
28
28
  passportNumber: ProviderAttribute;
29
+ providerUniqueIdentifier: ProviderAttribute;
29
30
  }
30
31
 
31
32
  const UserDetails: React.FC<UserDetailsProps> = ({ provider, user }) => {
@@ -41,6 +42,7 @@ const UserDetails: React.FC<UserDetailsProps> = ({ provider, user }) => {
41
42
  registrationNumber: 'License Body',
42
43
  emailAddress: 'Provider Address',
43
44
  passportNumber: 'Provider passport number',
45
+ providerUniqueIdentifier: 'Provider unique identifier',
44
46
  };
45
47
 
46
48
  const attributes: ProviderAttributes = Object.entries(attributeMap).reduce((acc, [key, display]) => {
@@ -60,6 +62,7 @@ const UserDetails: React.FC<UserDetailsProps> = ({ provider, user }) => {
60
62
  registrationNumber,
61
63
  emailAddress,
62
64
  passportNumber,
65
+ providerUniqueIdentifier,
63
66
  } = attributes;
64
67
 
65
68
  const formattedExpiryDate = dateAttr?.value ? dayjs(dateAttr.value).format('YYYY-MM-DD') : null;
@@ -104,6 +107,11 @@ const UserDetails: React.FC<UserDetailsProps> = ({ provider, user }) => {
104
107
  <span className={styles.statusTag}>
105
108
  {qualification?.value && <Tag type="cyan">{capitalize(qualification?.value)}</Tag>}
106
109
  </span>
110
+ <span className={styles.statusTag}>
111
+ {providerUniqueIdentifier?.value && (
112
+ <Tag type="cyan">{capitalize(providerUniqueIdentifier?.value)}</Tag>
113
+ )}
114
+ </span>
107
115
  </div>
108
116
  </div>
109
117
 
@@ -134,9 +142,13 @@ const UserDetails: React.FC<UserDetailsProps> = ({ provider, user }) => {
134
142
  {t('registrationNumber', 'Registration number')}:{' '}
135
143
  {registrationNumber?.value ? registrationNumber.value : '--'}
136
144
  </span>
145
+ <span className={styles.middot}>&middot; </span>
146
+
137
147
  <span className={styles.spanField}>
138
148
  {t('passportNumber', 'Passport number')}: {passportNumber?.value ? passportNumber.value : '--'}
139
149
  </span>
150
+ <span className={styles.middot}>&middot; </span>
151
+
140
152
  <span className={styles.spanField}>
141
153
  {t('licenseExpiryDate', 'License expiry date')}: {formattedExpiryDate ? formattedExpiryDate : '--'}
142
154
  </span>
@@ -42,6 +42,7 @@ import styles from './user-list.scss';
42
42
  import { ConfigObject } from '../../../../config-schema';
43
43
  import { formatDateTime } from '../../../../utils/utils';
44
44
  import UserDetails from '../user-details/user-details.component';
45
+ import upperCase from 'lodash-es/upperCase';
45
46
 
46
47
  type FilterType = 'allUsers' | 'activeLicensed' | 'expiredLicensed' | 'licensedExpiringSoon' | 'unlicensed';
47
48
 
@@ -63,8 +64,14 @@ const UserList: React.FC = () => {
63
64
  const { users, isLoading: isLoadingUsers, error: usersError } = useUsers();
64
65
  const { provider, isLoading, error: providerError } = useProvider();
65
66
  const [syncLoading, setSyncLoading] = useState(false);
66
- const { licenseNumberUuid, licenseExpiryDateUuid, providerNationalIdUuid, licenseBodyUuid, passportNumberUuid } =
67
- useConfig<ConfigObject>();
67
+ const {
68
+ licenseNumberUuid,
69
+ licenseExpiryDateUuid,
70
+ providerNationalIdUuid,
71
+ licenseBodyUuid,
72
+ passportNumberUuid,
73
+ providerUniqueIdentifierAttributeTypeUuid,
74
+ } = useConfig<ConfigObject>();
68
75
 
69
76
  const [pageSize, setPageSize] = useState(10);
70
77
  const [searchQuery, setSearchQuery] = useState('');
@@ -198,6 +205,10 @@ const UserList: React.FC = () => {
198
205
  key: 'licenseNumber',
199
206
  header: t('licenseNumber', 'License Number'),
200
207
  },
208
+ {
209
+ key: 'providerUniqueIdentifier',
210
+ header: t('providerUniqueIdentifier', 'Provider Unique Identifier'),
211
+ },
201
212
  {
202
213
  key: 'licenseExpiryDate',
203
214
  header: t('licenseExpiryDate', 'License Expiry Date'),
@@ -221,6 +232,11 @@ const UserList: React.FC = () => {
221
232
  const licenseNumber = licenseNumberAttribute ? licenseNumberAttribute.value : '--';
222
233
  const licenseExpiryDate = licenseExpiryDateAttribute ? licenseExpiryDateAttribute.value : '--';
223
234
 
235
+ const providerUniqueIdentifierAttribute = userProvider?.attributes.find(
236
+ (attr) => attr?.attributeType?.uuid === providerUniqueIdentifierAttributeTypeUuid,
237
+ );
238
+ const providerUniqueIdentifier = providerUniqueIdentifierAttribute ? providerUniqueIdentifierAttribute.value : '--';
239
+
224
240
  const providerNationalId = userProvider?.attributes.find(
225
241
  (attr) => attr?.attributeType?.uuid === providerNationalIdUuid,
226
242
  );
@@ -232,9 +248,10 @@ const UserList: React.FC = () => {
232
248
  return {
233
249
  id: user.uuid,
234
250
  systemId: user.systemId,
235
- names: capitalize(user.person.display),
251
+ names: upperCase(user.person.display),
236
252
  licenseNumber: licenseNumber,
237
253
  licenseExpiryDate: formatDateTime(new Date(licenseExpiryDate)),
254
+ providerUniqueIdentifier: providerUniqueIdentifier,
238
255
  userProvider,
239
256
  user,
240
257
  actions: (
@@ -101,6 +101,7 @@ const ManageUserWorkspace: React.FC<ManageUserWorkspaceProps> = ({
101
101
  providerNationalIdUuid,
102
102
  licenseNumberUuid,
103
103
  licenseExpiryDateUuid,
104
+ providerUniqueIdentifierAttributeTypeUuid,
104
105
  } = useConfig<ConfigObject>();
105
106
  const [searchHWR, setSearchHWR] = useState({
106
107
  identifierType: identifierTypes[0]?.key ?? '',
@@ -121,6 +122,8 @@ const ManageUserWorkspace: React.FC<ManageUserWorkspaceProps> = ({
121
122
  phoneNumber: providerAttributeType.find((type) => type.uuid === phoneNumberUuid)?.uuid || '',
122
123
  providerAddress: providerAttributeType.find((type) => type.uuid === providerAddressUuid)?.uuid || '',
123
124
  passportNumber: providerAttributeType.find((type) => type.uuid === passportNumberUuid)?.uuid || '',
125
+ providerUniqueIdentifier:
126
+ providerAttributeType.find((type) => type.uuid === providerUniqueIdentifierAttributeTypeUuid)?.uuid || '',
124
127
  };
125
128
  }, [
126
129
  licenseBodyUuid,
@@ -133,6 +136,7 @@ const ManageUserWorkspace: React.FC<ManageUserWorkspaceProps> = ({
133
136
  providerHieFhirReference,
134
137
  providerNationalIdUuid,
135
138
  qualificationUuid,
139
+ providerUniqueIdentifierAttributeTypeUuid,
136
140
  ]);
137
141
 
138
142
  const providerAttributes = useMemo(() => provider.flatMap((item) => item.attributes || []), [provider]);
@@ -176,6 +180,10 @@ const ManageUserWorkspace: React.FC<ManageUserWorkspaceProps> = ({
176
180
  () => getProviderAttributeValue(attributeTypeMapping.providerAddress),
177
181
  [attributeTypeMapping, getProviderAttributeValue],
178
182
  );
183
+ const providerUniqueIdentifier = useMemo(
184
+ () => getProviderAttributeValue(attributeTypeMapping.providerUniqueIdentifier),
185
+ [attributeTypeMapping, getProviderAttributeValue],
186
+ );
179
187
  type UserFormSchema = z.infer<typeof userManagementFormSchema>;
180
188
  const formDefaultValues = useMemo(() => {
181
189
  if (isInitialValuesEmpty) {
@@ -208,6 +216,7 @@ const ManageUserWorkspace: React.FC<ManageUserWorkspaceProps> = ({
208
216
  nationalId: nationalId,
209
217
  passportNumber: passportNumber,
210
218
  registrationNumber: registrationNumber,
219
+ providerUniqueIdentifier: providerUniqueIdentifier,
211
220
  };
212
221
  }, [
213
222
  isInitialValuesEmpty,
@@ -220,6 +229,7 @@ const ManageUserWorkspace: React.FC<ManageUserWorkspaceProps> = ({
220
229
  nationalId,
221
230
  passportNumber,
222
231
  registrationNumber,
232
+ providerUniqueIdentifier,
223
233
  ]);
224
234
 
225
235
  function extractAttributeValue(attributes, prefix: string) {
@@ -330,6 +340,7 @@ const ManageUserWorkspace: React.FC<ManageUserWorkspaceProps> = ({
330
340
  ),
331
341
  );
332
342
  setValue('passportNumber', healthWorkerPassPortNumber);
343
+ setValue('providerUniqueIdentifier', fetchedHealthWorker?.entry[0]?.resource?.id);
333
344
  setHealthWorker(fetchedHealthWorker);
334
345
  },
335
346
  });
@@ -356,6 +367,10 @@ const ManageUserWorkspace: React.FC<ManageUserWorkspaceProps> = ({
356
367
  attributeType: attributeTypeMapping?.providerHieFhirReference,
357
368
  value: JSON.stringify(healthWorker),
358
369
  },
370
+ {
371
+ attributeType: attributeTypeMapping.providerUniqueIdentifier,
372
+ value: data.providerUniqueIdentifier,
373
+ },
359
374
  {
360
375
  attributeType: attributeTypeMapping.providerNationalId,
361
376
  value: data.nationalId,
@@ -446,6 +461,10 @@ const ManageUserWorkspace: React.FC<ManageUserWorkspaceProps> = ({
446
461
  attributeType: passportNumberUuid,
447
462
  value: data?.passportNumber,
448
463
  },
464
+ {
465
+ attributeType: providerUniqueIdentifierAttributeTypeUuid,
466
+ value: data?.providerUniqueIdentifier,
467
+ },
449
468
  ].filter((attr) => attr?.value !== undefined && attr?.value !== null && attr?.value !== '');
450
469
 
451
470
  await Promise.all(
@@ -747,6 +766,26 @@ const ManageUserWorkspace: React.FC<ManageUserWorkspaceProps> = ({
747
766
  {hasProviderAccount && (
748
767
  <ResponsiveWrapper>
749
768
  <span className={styles.formHeaderSection}>{t('providerDetails', 'Provider details')}</span>
769
+ <ResponsiveWrapper>
770
+ <Controller
771
+ name="providerUniqueIdentifier"
772
+ control={userFormMethods.control}
773
+ render={({ field }) => (
774
+ <TextInput
775
+ {...field}
776
+ id="providerUniqueIdentifier"
777
+ type="text"
778
+ labelText={t('providerUniqueIdentifier', 'Provider Unique Identifier')}
779
+ placeholder={t(
780
+ 'providerUniqueIdentifierPlaceholder',
781
+ 'Enter Provider Unqiue Identifier',
782
+ )}
783
+ invalid={!!errors.providerUniqueIdentifier}
784
+ invalidText={errors.providerUniqueIdentifier?.message}
785
+ />
786
+ )}
787
+ />
788
+ </ResponsiveWrapper>
750
789
  <ResponsiveWrapper>
751
790
  <Controller
752
791
  name="nationalId"
@@ -43,6 +43,7 @@ const UserManagementFormSchema = (existingUsernames: Array<string>, isEdit?: boo
43
43
  nationalId: z.string().optional(),
44
44
  passportNumber: z.string().optional(),
45
45
  isEditProvider: z.boolean().optional(),
46
+ providerUniqueIdentifier: z.string().optional(),
46
47
  });
47
48
 
48
49
  return { userManagementFormSchema };
@@ -57,6 +57,11 @@ export const configSchema = {
57
57
  _description: 'UUID for person phone number attribute',
58
58
  _default: 'b2c38640-2603-4629-aebd-3b54f33f1e3a',
59
59
  },
60
+ providerUniqueIdentifierAttributeTypeUuid: {
61
+ _type: Type.String,
62
+ _description: 'UUID for provider unique identifier attribute type',
63
+ _default: 'dace9d99-9f29-4653-9eae-c05929f34a32',
64
+ },
60
65
  identifierTypes: {
61
66
  _type: Type.Array,
62
67
  _elements: {
@@ -84,6 +89,7 @@ export interface ConfigObject {
84
89
  providerNationalIdUuid: string;
85
90
  passportNumberUuid: string;
86
91
  providerHieFhirReference: string;
92
+ providerUniqueIdentifierAttributeTypeUuid: string;
87
93
  phoneNumberUuid: string;
88
94
  providerAddressUuid: string;
89
95
  qualificationUuid: string;
@@ -66,6 +66,7 @@
66
66
  "phoneNumber": "Phone number",
67
67
  "previousPage": "Previous page",
68
68
  "procedure": "Procedure",
69
+ "providerUniqueIdentifier": "Provider Unique Identifier",
69
70
  "recreated": "recreated",
70
71
  "recreateDatatools": "Recreate datatools",
71
72
  "recreateFacilityWideTables": "Recreate facility wide tables",