@kenyaemr/esm-admin-app 5.4.1-pre.1908 → 5.4.1-pre.1912

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 (52) hide show
  1. package/.turbo/turbo-build.log +31 -24
  2. package/dist/102.js +2 -0
  3. package/dist/102.js.map +1 -0
  4. package/dist/{325.js → 137.js} +2 -2
  5. package/dist/{325.js.map → 137.js.map} +1 -1
  6. package/dist/294.js +2 -0
  7. package/dist/294.js.LICENSE.txt +32 -0
  8. package/dist/294.js.map +1 -0
  9. package/dist/300.js +1 -1
  10. package/dist/654.js +1 -0
  11. package/dist/654.js.map +1 -0
  12. package/dist/675.js +2 -0
  13. package/dist/{485.js.map → 675.js.map} +1 -1
  14. package/dist/{493.js → 719.js} +1 -1
  15. package/dist/{493.js.map → 719.js.map} +1 -1
  16. package/dist/893.js +1 -1
  17. package/dist/893.js.map +1 -1
  18. package/dist/913.js +1 -1
  19. package/dist/913.js.LICENSE.txt +4 -27
  20. package/dist/913.js.map +1 -1
  21. package/dist/kenyaemr-esm-admin-app.js +1 -1
  22. package/dist/kenyaemr-esm-admin-app.js.buildmanifest.json +117 -114
  23. package/dist/kenyaemr-esm-admin-app.js.map +1 -1
  24. package/dist/main.js +1 -1
  25. package/dist/main.js.map +1 -1
  26. package/dist/routes.json +1 -1
  27. package/package.json +1 -1
  28. package/src/components/hook/useHWR.ts +22 -0
  29. package/src/components/modal/hwr-confirmation.modal.scss +21 -0
  30. package/src/components/modal/hwr-confirmation.modal.tsx +130 -0
  31. package/src/components/modal/hwr-empty.modal.component.tsx +54 -0
  32. package/src/components/users/manage-users/user-list/user-list.component.tsx +27 -22
  33. package/src/components/users/manage-users/user-management.workspace.scss +36 -0
  34. package/src/components/users/manage-users/user-management.workspace.tsx +388 -193
  35. package/src/components/users/userManagementFormSchema.tsx +5 -1
  36. package/src/config-schema.ts +55 -3
  37. package/src/constants.ts +26 -0
  38. package/src/index.ts +5 -0
  39. package/src/routes.json +8 -0
  40. package/src/types/index.ts +97 -0
  41. package/translations/en.json +7 -0
  42. package/dist/373.js +0 -2
  43. package/dist/373.js.map +0 -1
  44. package/dist/387.js +0 -2
  45. package/dist/387.js.LICENSE.txt +0 -15
  46. package/dist/387.js.map +0 -1
  47. package/dist/485.js +0 -2
  48. package/dist/778.js +0 -1
  49. package/dist/778.js.map +0 -1
  50. /package/dist/{373.js.LICENSE.txt → 102.js.LICENSE.txt} +0 -0
  51. /package/dist/{325.js.LICENSE.txt → 137.js.LICENSE.txt} +0 -0
  52. /package/dist/{485.js.LICENSE.txt → 675.js.LICENSE.txt} +0 -0
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"}],"modals":[{"component":"operationConfirmationModal","name":"operation-confirmation-modal"}],"pages":[{"component":"root","route":"admin"}],"version":"5.4.1-pre.1908"}
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"}],"modals":[{"component":"operationConfirmationModal","name":"operation-confirmation-modal"},{"component":"hwrConfirmationModal","name":"hwr-confirmation-modal"},{"component":"hwrEmptyModal","name":"hwr-empty-modal"}],"pages":[{"component":"root","route":"admin"}],"version":"5.4.1-pre.1912"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kenyaemr/esm-admin-app",
3
- "version": "5.4.1-pre.1908",
3
+ "version": "5.4.1-pre.1912",
4
4
  "description": "Facilitates the management of ETL tables",
5
5
  "browser": "dist/kenyaemr-esm-admin-app.js",
6
6
  "main": "src/index.ts",
@@ -0,0 +1,22 @@
1
+ import { FetchResponse, makeUrl, openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
2
+ import { HWR_API_NO_CREDENTIALS, PROVIDER_NOT_FOUND, RESOURCE_NOT_FOUND, UNKNOWN } from '../../constants';
3
+ import useSWR from 'swr';
4
+ import { useState } from 'react';
5
+
6
+ export const searchHealthCareWork = async (identifierType: string, identifierNumber: string) => {
7
+ const url = `${restBaseUrl}/kenyaemr/practitionersearch?identifierType=${identifierType}&identifierNumber=${identifierNumber}`;
8
+ const response = await fetch(makeUrl(url));
9
+ if (response.ok) {
10
+ const responseData = await response.json();
11
+ if (responseData?.issue) {
12
+ throw new Error(PROVIDER_NOT_FOUND);
13
+ }
14
+ return responseData;
15
+ }
16
+ if (response.status === 401) {
17
+ throw new Error(HWR_API_NO_CREDENTIALS);
18
+ } else if (response.status === 404) {
19
+ throw new Error(RESOURCE_NOT_FOUND);
20
+ }
21
+ throw new Error(UNKNOWN);
22
+ };
@@ -0,0 +1,21 @@
1
+ @use '@carbon/colors';
2
+ @use '@carbon/layout';
3
+ @use '@carbon/type';
4
+
5
+ .healthWorkerInfoContainer {
6
+ display: grid;
7
+ grid-template-columns: 0.25fr 0.75fr;
8
+ margin: layout.$spacing-02;
9
+ }
10
+ .healthWorkerInfoLabel {
11
+ min-width: layout.$spacing-11;
12
+ font-weight: bold;
13
+ }
14
+ .healthWorkerOverview {
15
+ display: flex;
16
+ margin: layout.$spacing-05;
17
+ }
18
+ .healthWorkerPhoto {
19
+ display: flex;
20
+ align-items: center;
21
+ }
@@ -0,0 +1,130 @@
1
+ import { Button, Tag } from '@carbon/react';
2
+ import { ExtensionSlot } from '@openmrs/esm-framework';
3
+ import capitalize from 'lodash-es/capitalize';
4
+ import React from 'react';
5
+ import { useTranslation } from 'react-i18next';
6
+ import { type PractitionerResponse } from '../../types';
7
+ import styles from './hwr-confirmation.modal.scss';
8
+
9
+ interface HealthWorkerInfoProps {
10
+ label: string;
11
+ value: string | boolean | React.ReactNode;
12
+ }
13
+
14
+ const HealthWorkerInfo: React.FC<HealthWorkerInfoProps> = ({ label, value }) => {
15
+ return (
16
+ <div className={styles.healthWorkerInfoContainer}>
17
+ <span className={styles.healthWorkerInfoLabel}>{label}</span>
18
+ <span>{value}</span>
19
+ </div>
20
+ );
21
+ };
22
+
23
+ interface HWRConfirmModalProps {
24
+ onConfirm: () => void;
25
+ close: () => void;
26
+ healthWorker: PractitionerResponse;
27
+ }
28
+
29
+ const HWRConfirmModal: React.FC<HWRConfirmModalProps> = ({ close, onConfirm, healthWorker }) => {
30
+ const { t } = useTranslation();
31
+ const passportNumber = healthWorker?.link
32
+ ?.find(
33
+ (link: { relation: string; url: string }) =>
34
+ link.relation === 'self' && link.url.includes('identifierType=Passport'),
35
+ )
36
+ ?.url.split('identifierNumber=')[1]
37
+ ?.split('&')[0];
38
+
39
+ const practitioner = healthWorker?.entry?.[0]?.resource;
40
+
41
+ return (
42
+ <>
43
+ <div className="cds--modal-header">
44
+ <h3 className="cds--modal-header__heading">{t('healthWorkerRegistry', 'Health worker registry')}</h3>
45
+ </div>
46
+ <div className="cds--modal-content">
47
+ <p>
48
+ {t(
49
+ 'healthWorkerDetailsFound',
50
+ 'Health worker information found in the registry, do you want to use the information to continue with registration?',
51
+ )}
52
+ </p>
53
+ <div className={styles.healthWorkerOverview}>
54
+ <ExtensionSlot
55
+ className={styles.healthWorkerPhoto}
56
+ name="patient-photo-slot"
57
+ state={{
58
+ patientName: practitioner?.name?.[0]?.text || '',
59
+ }}
60
+ />
61
+ <div style={{ width: '100%', marginLeft: '0.625rem' }}>
62
+ <HealthWorkerInfo
63
+ label={t('healthWorkerName', 'Health worker name')}
64
+ value={practitioner?.name?.[0]?.text}
65
+ />
66
+
67
+ {practitioner?.telecom?.map((telecom, index) => (
68
+ <HealthWorkerInfo key={index} label={capitalize(telecom?.system)} value={telecom?.value || '--'} />
69
+ ))}
70
+
71
+ {practitioner?.identifier?.map((identifier, index) => (
72
+ <HealthWorkerInfo
73
+ key={index}
74
+ label={identifier.type?.coding?.map((code) => code.display).join(' ') || '--'}
75
+ value={identifier.value || '--'}
76
+ />
77
+ ))}
78
+
79
+ {passportNumber && (
80
+ <HealthWorkerInfo label={t('passportNumber', 'Passport Number')} value={passportNumber} />
81
+ )}
82
+
83
+ <HealthWorkerInfo
84
+ label={t('renewalDate', 'Renewal Date')}
85
+ value={
86
+ practitioner?.identifier?.find((id) => id.type?.coding?.some((code) => code.code === 'license-number'))
87
+ ?.period?.end || '--'
88
+ }
89
+ />
90
+
91
+ <HealthWorkerInfo
92
+ label={t('licensingBody', 'Licensing Body')}
93
+ value={
94
+ practitioner?.qualification?.[0]?.extension?.find(
95
+ (ext) => ext.url === 'https://hwr-kenyahie/StructureDefinition/licensing-body',
96
+ )?.valueCodeableConcept?.coding?.[0]?.display || '--'
97
+ }
98
+ />
99
+ <HealthWorkerInfo
100
+ label={t('qualification', 'Qualification')}
101
+ value={
102
+ practitioner?.qualification?.[0]?.code?.coding?.[0]?.display ||
103
+ practitioner?.extension?.find((ext) => ext.url === 'https://ts.kenya-hie.health/Codesystem/specialty')
104
+ ?.valueCodeableConcept?.coding?.[0]?.display ||
105
+ '--'
106
+ }
107
+ />
108
+
109
+ <HealthWorkerInfo
110
+ label={t('licenseValid', 'License Validity')}
111
+ value={
112
+ <Tag type={practitioner?.active ? 'green' : 'red'}>
113
+ {practitioner?.active ? t('licenseValid', 'License Valid') : t('licenseExpired', 'License Expired')}
114
+ </Tag>
115
+ }
116
+ />
117
+ </div>
118
+ </div>
119
+ </div>
120
+ <div className="cds--modal-footer">
121
+ <Button kind="secondary" onClick={close}>
122
+ {t('cancel', 'Cancel')}
123
+ </Button>
124
+ <Button onClick={onConfirm}>{t('useValues', 'Use values')}</Button>
125
+ </div>
126
+ </>
127
+ );
128
+ };
129
+
130
+ export default HWRConfirmModal;
@@ -0,0 +1,54 @@
1
+ import React from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import { Button } from '@carbon/react';
4
+ import { HWR_API_NO_CREDENTIALS, RESOURCE_NOT_FOUND, UNKNOWN } from '../../constants';
5
+
6
+ interface HWREmptyModalProps {
7
+ close: () => void;
8
+ errorCode?: string;
9
+ }
10
+
11
+ const HWREmptyModal: React.FC<HWREmptyModalProps> = ({ close, errorCode }) => {
12
+ const { t } = useTranslation();
13
+
14
+ const errorMessages = {
15
+ [RESOURCE_NOT_FOUND]: t(
16
+ 'ResourceNotFound',
17
+ 'The Health Work Registry is not reachable, kindly confirm your internet connectivity and try again. Do you want to continue to create an account',
18
+ ),
19
+ [HWR_API_NO_CREDENTIALS]: t(
20
+ 'noHwrApi',
21
+ 'Health Care Worker Registry API credentials not configured, Kindly contact system admin. Do you want to continue to create an account',
22
+ ),
23
+ [UNKNOWN]: t(
24
+ 'unknownError',
25
+ 'An error occurred while searching Health Worker Registry, kindly contact system admin. Do you want to continue to create an account',
26
+ ),
27
+ };
28
+
29
+ const defaultMessage = t(
30
+ 'HealthworkerNotFound',
31
+ 'The health worker records could not be found in Health Worker registry, do you want to continue to create an account',
32
+ );
33
+
34
+ const message = errorMessages[errorCode] || defaultMessage;
35
+
36
+ return (
37
+ <>
38
+ <div className="cds--modal-header">
39
+ <h3 className="cds--modal-header__heading">{t('healthWorkerRegistryEmpty', 'Create an Account')}</h3>
40
+ </div>
41
+ <div className="cds--modal-content">
42
+ <p>{message}</p>
43
+ </div>
44
+ <div className="cds--modal-footer">
45
+ <Button kind="secondary" onClick={close}>
46
+ {t('cancel', 'Cancel')}
47
+ </Button>
48
+ <Button onClick={close}>{t('continue', 'Continue to registration')}</Button>
49
+ </div>
50
+ </>
51
+ );
52
+ };
53
+
54
+ export default HWREmptyModal;
@@ -15,10 +15,13 @@ import {
15
15
  Button,
16
16
  Pagination,
17
17
  ButtonSet,
18
+ OverflowMenuItem,
19
+ MenuItemDivider,
20
+ OverflowMenu,
18
21
  } from '@carbon/react';
19
22
  import { Edit, UserFollow } from '@carbon/react/icons';
20
23
  import styles from './user-list.scss';
21
- import { launchWorkspace, useDebounce, WorkspaceContainer } from '@openmrs/esm-framework';
24
+ import { launchWorkspace, showModal, useDebounce, WorkspaceContainer } from '@openmrs/esm-framework';
22
25
  import { useUser } from '../../../../user-management.resources';
23
26
 
24
27
  const UserList: React.FC = () => {
@@ -28,6 +31,7 @@ const UserList: React.FC = () => {
28
31
  const [searchTerm, setSearchTerm] = useState('');
29
32
  const [currentPage, setCurrentPage] = useState(1);
30
33
  const [pageSize, setPageSize] = useState(10);
34
+ const [syncLoading, setSyncLoading] = useState(false);
31
35
 
32
36
  const debouncedSearchTerm = useDebounce(searchTerm, 500);
33
37
 
@@ -59,7 +63,6 @@ const UserList: React.FC = () => {
59
63
  setSearchTerm(searchTerm);
60
64
  setCurrentPage(1);
61
65
  };
62
-
63
66
  if (isLoading) {
64
67
  return <DataTableSkeleton />;
65
68
  }
@@ -118,26 +121,28 @@ const UserList: React.FC = () => {
118
121
  familyName: familyName,
119
122
  roles: rolesDisplay,
120
123
  actions: (
121
- <ButtonSet className={styles.btnSet}>
122
- <Button
123
- className={styles.btn}
124
- renderIcon={Edit}
125
- hasIconOnly
126
- kind="ghost"
127
- iconDescription={t('edit', 'Edit')}
128
- onClick={() => {
129
- const selectedUser = users.find((u) => u.uuid === user.uuid);
130
- if (selectedUser) {
131
- launchWorkspace('manage-user-workspace', {
132
- workspaceTitle: t('editUser', 'Edit User'),
133
- initialUserValue: selectedUser,
134
- });
135
- } else {
136
- console.error('User not found:', user.uuid);
137
- }
138
- }}
139
- />
140
- </ButtonSet>
124
+ <>
125
+ <ButtonSet className={styles.btnSet}>
126
+ <Button
127
+ className={styles.btn}
128
+ renderIcon={Edit}
129
+ hasIconOnly
130
+ kind="ghost"
131
+ iconDescription={t('edit', 'Edit')}
132
+ onClick={() => {
133
+ const selectedUser = users.find((u) => u.uuid === user.uuid);
134
+ if (selectedUser) {
135
+ launchWorkspace('manage-user-workspace', {
136
+ workspaceTitle: t('editUser', 'Edit User'),
137
+ initialUserValue: selectedUser,
138
+ });
139
+ } else {
140
+ console.error('User not found:', user.uuid);
141
+ }
142
+ }}
143
+ />
144
+ </ButtonSet>
145
+ </>
141
146
  ),
142
147
  };
143
148
  });
@@ -136,3 +136,39 @@
136
136
  word-wrap: break-word;
137
137
  line-height: 1.4;
138
138
  }
139
+
140
+ .formHeaderSection {
141
+ @include type.type-style('heading-02');
142
+ display: flex;
143
+ align-items: center;
144
+ justify-content: space-between;
145
+ row-gap: layout.$spacing-06;
146
+ position: relative;
147
+
148
+ &::after {
149
+ content: '';
150
+ display: block;
151
+ width: layout.$spacing-07;
152
+ border-bottom: 0.375rem solid var(--brand-03);
153
+ position: absolute;
154
+ bottom: -0.75rem;
155
+ left: 0;
156
+ }
157
+
158
+ & > span {
159
+ @include type.type-style('body-01');
160
+ }
161
+ }
162
+ .formIdentifierType {
163
+ color: colors.$gray-70;
164
+ display: inline-block;
165
+ font-size: layout.$spacing-04;
166
+ line-height: layout.$spacing-05;
167
+ line-height: layout.$spacing-05;
168
+ margin-bottom: layout.$spacing-03;
169
+ vertical-align: baseline;
170
+ }
171
+ .formRow {
172
+ display: flex;
173
+ align-items: center;
174
+ }