@palladium-ethiopia/esm-admin-app 5.4.2-pre.100

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 (149) hide show
  1. package/.turbo/turbo-build.log +15 -0
  2. package/README.md +12 -0
  3. package/dist/117.js +1 -0
  4. package/dist/152.js +1 -0
  5. package/dist/152.js.map +1 -0
  6. package/dist/209.js +1 -0
  7. package/dist/209.js.map +1 -0
  8. package/dist/41.js +1 -0
  9. package/dist/41.js.map +1 -0
  10. package/dist/442.js +1 -0
  11. package/dist/442.js.map +1 -0
  12. package/dist/466.js +1 -0
  13. package/dist/466.js.map +1 -0
  14. package/dist/555.js +1 -0
  15. package/dist/555.js.map +1 -0
  16. package/dist/61.js +1 -0
  17. package/dist/61.js.map +1 -0
  18. package/dist/672.js +15 -0
  19. package/dist/672.js.map +1 -0
  20. package/dist/689.js +1 -0
  21. package/dist/689.js.map +1 -0
  22. package/dist/710.js +1 -0
  23. package/dist/710.js.map +1 -0
  24. package/dist/712.js +1 -0
  25. package/dist/712.js.map +1 -0
  26. package/dist/771.js +1 -0
  27. package/dist/771.js.map +1 -0
  28. package/dist/789.js +1 -0
  29. package/dist/789.js.map +1 -0
  30. package/dist/806.js +1 -0
  31. package/dist/826.js +1 -0
  32. package/dist/826.js.map +1 -0
  33. package/dist/914.js +27 -0
  34. package/dist/914.js.map +1 -0
  35. package/dist/926.js +17 -0
  36. package/dist/926.js.map +1 -0
  37. package/dist/ethiopia-esm-admin-app.js +6 -0
  38. package/dist/ethiopia-esm-admin-app.js.buildmanifest.json +556 -0
  39. package/dist/ethiopia-esm-admin-app.js.map +1 -0
  40. package/dist/main.js +32 -0
  41. package/dist/main.js.map +1 -0
  42. package/dist/routes.json +1 -0
  43. package/jest.config.js +8 -0
  44. package/package.json +52 -0
  45. package/rspack.config.js +1 -0
  46. package/src/components/confirm-modal/confirmation-operation-modal.component.tsx +43 -0
  47. package/src/components/confirm-modal/confirmation-operation.test.tsx +69 -0
  48. package/src/components/dashboard/dashboard.component.tsx +131 -0
  49. package/src/components/dashboard/dashboard.scss +38 -0
  50. package/src/components/dashboard/etl-dashboard.component.tsx +11 -0
  51. package/src/components/empty-state/empty-state-log.components.tsx +20 -0
  52. package/src/components/empty-state/empty-state-log.scss +28 -0
  53. package/src/components/empty-state/empty-state-log.test.tsx +24 -0
  54. package/src/components/facility-setup/card.component.tsx +16 -0
  55. package/src/components/facility-setup/facility-info.component.tsx +142 -0
  56. package/src/components/facility-setup/facility-info.scss +87 -0
  57. package/src/components/facility-setup/facility-setup.component.tsx +21 -0
  58. package/src/components/facility-setup/facility-setup.resource.tsx +7 -0
  59. package/src/components/facility-setup/facility-setup.scss +38 -0
  60. package/src/components/facility-setup/header/header.component.tsx +23 -0
  61. package/src/components/facility-setup/header/header.scss +19 -0
  62. package/src/components/header/header-illustration.component.tsx +13 -0
  63. package/src/components/header/header.component.tsx +28 -0
  64. package/src/components/header/header.scss +19 -0
  65. package/src/components/hook/healthWorkerAdapter.ts +213 -0
  66. package/src/components/hook/useFacilityInfo.tsx +37 -0
  67. package/src/components/hook/useSystemRoleSetting.tsx +33 -0
  68. package/src/components/locations/auto-suggest/autosuggest.component.tsx +149 -0
  69. package/src/components/locations/auto-suggest/autosuggest.scss +61 -0
  70. package/src/components/locations/auto-suggest/location-autosuggest.component.tsx +94 -0
  71. package/src/components/locations/auto-suggest/location-autosuggest.scss +48 -0
  72. package/src/components/locations/common/results-tile.component.tsx +45 -0
  73. package/src/components/locations/common/results-tile.scss +86 -0
  74. package/src/components/locations/forms/add-location/add-location.workspace.scss +34 -0
  75. package/src/components/locations/forms/add-location/add-location.workspace.tsx +200 -0
  76. package/src/components/locations/forms/search-location/search-location.workspace.scss +79 -0
  77. package/src/components/locations/forms/search-location/search-location.workspace.tsx +215 -0
  78. package/src/components/locations/header/header.component.tsx +48 -0
  79. package/src/components/locations/header/header.scss +58 -0
  80. package/src/components/locations/helpers/index.ts +16 -0
  81. package/src/components/locations/home/home-locations.component.tsx +18 -0
  82. package/src/components/locations/home/home-locations.scss +8 -0
  83. package/src/components/locations/hooks/UseFacilityLocations.ts +12 -0
  84. package/src/components/locations/hooks/useLocation.ts +18 -0
  85. package/src/components/locations/hooks/useLocationTags.ts +15 -0
  86. package/src/components/locations/tables/locations-table.component.tsx +243 -0
  87. package/src/components/locations/tables/locations-table.resource.ts +26 -0
  88. package/src/components/locations/tables/locations-table.scss +115 -0
  89. package/src/components/locations/types/index.ts +120 -0
  90. package/src/components/locations/utils/index.ts +5 -0
  91. package/src/components/logs-table/operation-log-resource.ts +41 -0
  92. package/src/components/logs-table/operation-log-table.component.tsx +120 -0
  93. package/src/components/logs-table/operation-log.scss +10 -0
  94. package/src/components/logs-table/operation-log.test.tsx +47 -0
  95. package/src/components/modal/hwr-confirmation.modal.scss +21 -0
  96. package/src/components/modal/hwr-confirmation.modal.tsx +170 -0
  97. package/src/components/modal/hwr-empty.modal.component.tsx +54 -0
  98. package/src/components/modal/hwr-sync.modal.scss +30 -0
  99. package/src/components/modal/hwr-sync.modal.tsx +209 -0
  100. package/src/components/modal/hwr-sync.resource.ts +23 -0
  101. package/src/components/provider-banner/provider-banner.component.tsx +106 -0
  102. package/src/components/provider-banner/provider-banner.module.scss +51 -0
  103. package/src/components/provider-banner/provider-banner.resource.ts +29 -0
  104. package/src/components/side-menu/left-panel.scss +42 -0
  105. package/src/components/side-menu/left-pannel.component.tsx +22 -0
  106. package/src/components/users/header/header.scss +90 -0
  107. package/src/components/users/header/user-management-header.component.tsx +42 -0
  108. package/src/components/users/manage-users/hooks/useProviderAttributeMapping.ts +110 -0
  109. package/src/components/users/manage-users/hooks/useUserFormSteps.ts +119 -0
  110. package/src/components/users/manage-users/hooks/useUserFormSubmission.ts +264 -0
  111. package/src/components/users/manage-users/hooks/useUserManagementForm.ts +122 -0
  112. package/src/components/users/manage-users/manage-user-role-scope/user-role-scope-list/user-role-scope-list.component.tsx +177 -0
  113. package/src/components/users/manage-users/manage-user-role-scope/user-role-scope-workspace/user-role-fields.scss +117 -0
  114. package/src/components/users/manage-users/manage-user-role-scope/user-role-scope-workspace/user-role-scope-fields.component.tsx +290 -0
  115. package/src/components/users/manage-users/manage-user-role-scope/user-role-scope-workspace/user-role-scope.workspace.tsx +316 -0
  116. package/src/components/users/manage-users/manage-user-role-scope/user-role-scope-workspace/userRoleScopeFormSchema.tsx +43 -0
  117. package/src/components/users/manage-users/manage-user.component.tsx +19 -0
  118. package/src/components/users/manage-users/manage-user.scss +31 -0
  119. package/src/components/users/manage-users/provider-autosuggest.component.tsx +117 -0
  120. package/src/components/users/manage-users/provider-search.resource.ts +34 -0
  121. package/src/components/users/manage-users/sections/demographic-section.component.tsx +156 -0
  122. package/src/components/users/manage-users/sections/login-section.component.tsx +88 -0
  123. package/src/components/users/manage-users/sections/provider-section.component.tsx +270 -0
  124. package/src/components/users/manage-users/sections/roles-section.component.tsx +88 -0
  125. package/src/components/users/manage-users/user-details/user-detail.scss +75 -0
  126. package/src/components/users/manage-users/user-details/user-details.component.tsx +182 -0
  127. package/src/components/users/manage-users/user-list/user-list.component.tsx +378 -0
  128. package/src/components/users/manage-users/user-list/user-list.resource.ts +30 -0
  129. package/src/components/users/manage-users/user-list/user-list.scss +37 -0
  130. package/src/components/users/manage-users/user-management.constants.ts +20 -0
  131. package/src/components/users/manage-users/user-management.utils.ts +100 -0
  132. package/src/components/users/manage-users/user-management.workspace.scss +172 -0
  133. package/src/components/users/manage-users/user-management.workspace.tsx +334 -0
  134. package/src/components/users/userManagementFormSchema.tsx +179 -0
  135. package/src/config-schema.ts +142 -0
  136. package/src/constants.ts +50 -0
  137. package/src/declarations.d.ts +2 -0
  138. package/src/index.ts +55 -0
  139. package/src/left-pannel-link.component.tsx +40 -0
  140. package/src/root.component.tsx +39 -0
  141. package/src/root.scss +12 -0
  142. package/src/routes.json +100 -0
  143. package/src/setup-tests.ts +1 -0
  144. package/src/types/index.ts +385 -0
  145. package/src/user-management.resources.ts +232 -0
  146. package/src/utils/utils.ts +20 -0
  147. package/translations/am.json +159 -0
  148. package/translations/en.json +159 -0
  149. package/tsconfig.json +5 -0
@@ -0,0 +1,115 @@
1
+ @use '@carbon/colors';
2
+ @use '@carbon/layout';
3
+ @use '@carbon/type';
4
+
5
+ .widgetCard {
6
+ background-color: colors.$white-0;
7
+ border: 1px solid colors.$gray-20;
8
+ margin-bottom: layout.$spacing-06;
9
+ margin-right: layout.$spacing-03;
10
+ margin-left: layout.$spacing-05;
11
+ }
12
+ .expandedRow {
13
+ background-color: colors.$white-0;
14
+ border: 1px solid colors.$gray-20;
15
+ margin-bottom: layout.$spacing-06;
16
+ margin-right: layout.$spacing-03;
17
+ margin-left: layout.$spacing-05;
18
+ }
19
+ .flexContainer {
20
+ display: flex;
21
+ justify-content: space-between;
22
+ align-items: center;
23
+ padding: layout.$spacing-05;
24
+ }
25
+
26
+ .filterContainer {
27
+ display: flex;
28
+ align-items: center;
29
+ gap: layout.$spacing-05;
30
+ }
31
+
32
+ .headerActions {
33
+ display: flex;
34
+ gap: layout.$spacing-03;
35
+ align-items: center;
36
+ }
37
+
38
+ .filterContainer {
39
+ flex: 1;
40
+
41
+ :global(.cds--dropdown__wrapper--inline) {
42
+ gap: 0;
43
+ }
44
+
45
+ :global(.cds--list-box__menu-icon) {
46
+ height: layout.$spacing-05;
47
+ }
48
+ }
49
+
50
+ .backgroundDataFetchingIndicator {
51
+ align-items: center;
52
+ display: flex;
53
+ justify-content: flex-end;
54
+ }
55
+
56
+ .tileContainer {
57
+ background-color: colors.$white-0;
58
+ border-top: 1px solid colors.$gray-20;
59
+ padding: layout.$spacing-09 0;
60
+ }
61
+
62
+ .tile {
63
+ margin: auto;
64
+ width: fit-content;
65
+ display: flex;
66
+ flex-direction: column;
67
+ align-items: center;
68
+ }
69
+
70
+ .tileContent {
71
+ display: flex;
72
+ flex-direction: column;
73
+ align-items: center;
74
+ }
75
+
76
+ .content {
77
+ @include type.type-style('heading-compact-02');
78
+ color: colors.$gray-70;
79
+ margin-bottom: layout.$spacing-03;
80
+ }
81
+
82
+ .helper {
83
+ @include type.type-style('body-compact-01');
84
+ color: colors.$gray-70;
85
+ }
86
+
87
+ .separator {
88
+ @include type.type-style('body-compact-02');
89
+ color: colors.$gray-70;
90
+ width: 80%;
91
+ margin: layout.$spacing-06 auto;
92
+ overflow: hidden;
93
+ text-align: center;
94
+
95
+ &::before,
96
+ &::after {
97
+ background-color: colors.$gray-40;
98
+ content: '';
99
+ display: inline-block;
100
+ height: 1px;
101
+ position: relative;
102
+ vertical-align: middle;
103
+ width: 50%;
104
+ }
105
+
106
+ &::before {
107
+ right: layout.$spacing-03;
108
+ margin-left: -50%;
109
+ }
110
+
111
+ &::after {
112
+ left: layout.$spacing-03;
113
+ margin-right: -50%;
114
+ }
115
+ }
@@ -0,0 +1,120 @@
1
+ export interface AdmissionLocationResponse {
2
+ uuid: string;
3
+ wardName: string;
4
+ totalBeds: number;
5
+ occupiedBeds: number;
6
+ availableBeds: number;
7
+ ward: {
8
+ uuid: string;
9
+ display: string;
10
+ name: string;
11
+ tags: Array<{
12
+ uuid: string;
13
+ display: string;
14
+ }>;
15
+ parentLocation: {
16
+ uuid: string;
17
+ display: string;
18
+ };
19
+ };
20
+ }
21
+
22
+ export interface LocationTagsResponse {
23
+ results: Array<{
24
+ uuid: string;
25
+ display: string;
26
+ name: string;
27
+ description: string;
28
+ }>;
29
+ }
30
+
31
+ // OpenMRS format (legacy)
32
+ export interface LocationResponse {
33
+ uuid: string;
34
+ display: string;
35
+ name: string;
36
+ description: string;
37
+ stateProvince: string;
38
+ country: string;
39
+ countyDistrict: string;
40
+ address5: string;
41
+ address6: string;
42
+ tags: Array<{
43
+ uuid: string;
44
+ display: string;
45
+ name: string;
46
+ description: string;
47
+ }>;
48
+ attributes: Array<{
49
+ display: string;
50
+ uuid: string;
51
+ attributeType: {
52
+ uuid: string;
53
+ display: string;
54
+ };
55
+ value: string;
56
+ }>;
57
+ }
58
+
59
+ export interface FHIRLocation {
60
+ resourceType: 'Location';
61
+ id: string;
62
+ meta?: {
63
+ versionId?: string;
64
+ lastUpdated?: string;
65
+ tag?: Array<{
66
+ system: string;
67
+ code: string;
68
+ display: string;
69
+ }>;
70
+ };
71
+ status: string;
72
+ name: string;
73
+ description?: string;
74
+ address?: {
75
+ extension?: Array<{
76
+ url: string;
77
+ extension?: Array<{
78
+ url: string;
79
+ valueString: string;
80
+ }>;
81
+ }>;
82
+ line?: string[];
83
+ city?: string;
84
+ district?: string;
85
+ state?: string;
86
+ country?: string;
87
+ };
88
+ identifier?: Array<{
89
+ system: string;
90
+ value: string;
91
+ }>;
92
+ partOf?: {
93
+ reference: string;
94
+ type: string;
95
+ display: string;
96
+ };
97
+ }
98
+
99
+ export interface FHIRBundle {
100
+ resourceType: 'Bundle';
101
+ id: string;
102
+ meta?: {
103
+ lastUpdated?: string;
104
+ tag?: Array<{
105
+ system: string;
106
+ code: string;
107
+ display: string;
108
+ }>;
109
+ };
110
+ type: string;
111
+ total?: number;
112
+ link?: Array<{
113
+ relation: string;
114
+ url: string;
115
+ }>;
116
+ entry?: Array<{
117
+ fullUrl: string;
118
+ resource: FHIRLocation;
119
+ }>;
120
+ }
@@ -0,0 +1,5 @@
1
+ import { makeUrl } from '@openmrs/esm-framework';
2
+
3
+ export function makeUrlUrl(path: string) {
4
+ return new URL(makeUrl(path), window.location.toString());
5
+ }
@@ -0,0 +1,41 @@
1
+ import { restBaseUrl, openmrsFetch } from '@openmrs/esm-framework';
2
+ import { ETLResponse } from '../../types';
3
+
4
+ export const recreateTables = async () => {
5
+ const url = `${restBaseUrl}/kemrchart/recreateTables`;
6
+ const response = await openmrsFetch<{
7
+ data: Array<ETLResponse>;
8
+ }>(url);
9
+ return response.data.data;
10
+ };
11
+
12
+ export const refreshTables = async () => {
13
+ const url = `${restBaseUrl}/kemrchart/refreshTables`;
14
+ const response = await openmrsFetch<{
15
+ data: Array<ETLResponse>;
16
+ }>(url);
17
+ return response.data.data;
18
+ };
19
+
20
+ export const recreateDatatools = async () => {
21
+ const url = `${restBaseUrl}/kemrchart/recreateDatatoolsTables`;
22
+ const response = await openmrsFetch<{
23
+ data: Array<ETLResponse>;
24
+ }>(url);
25
+ return response.data.data;
26
+ };
27
+
28
+ export const refreshDwapi = async () => {
29
+ const url = `${restBaseUrl}/kemrchart/recreateDwapiTables`;
30
+ const response = await openmrsFetch<{
31
+ data: Array<ETLResponse>;
32
+ }>(url);
33
+ return response.data.data;
34
+ };
35
+ export const recreateFacilityWideTables = async () => {
36
+ const url = `${restBaseUrl}/kemrchart/recreateFacilitywideTables`;
37
+ const response = await openmrsFetch<{
38
+ data: Array<ETLResponse>;
39
+ }>(url);
40
+ return response.data.data;
41
+ };
@@ -0,0 +1,120 @@
1
+ import React from 'react';
2
+ import classNames from 'classnames';
3
+ import { useTranslation } from 'react-i18next';
4
+ import {
5
+ DataTable,
6
+ Table,
7
+ TableCell,
8
+ TableContainer,
9
+ TableBody,
10
+ TableHead,
11
+ TableHeader,
12
+ TableRow,
13
+ Tag,
14
+ DataTableSkeleton,
15
+ InlineLoading,
16
+ } from '@carbon/react';
17
+ import { CardHeader, PatientChartPagination } from '@openmrs/esm-patient-common-lib';
18
+ import { useLayoutType, usePagination, formatDate } from '@openmrs/esm-framework';
19
+ import styles from './operation-log.scss';
20
+ import { ETLResponse } from '../../types';
21
+ import EmptyState from '../empty-state/empty-state-log.components';
22
+
23
+ interface LogTableProps {
24
+ logData: ETLResponse[];
25
+ isLoading: boolean;
26
+ }
27
+
28
+ const LogTable: React.FC<LogTableProps> = ({ logData, isLoading }) => {
29
+ const { t } = useTranslation();
30
+ const pageSize = 10;
31
+ const isTablet = useLayoutType() === 'tablet';
32
+
33
+ const headers = [
34
+ { header: t('procedure', 'Procedure'), key: 'script_name' },
35
+ { header: t('startTime', 'Start time'), key: 'start_time' },
36
+ { header: t('endTime', 'End time'), key: 'stop_time' },
37
+ { header: t('completionStatus', 'Completion status'), key: 'status' },
38
+ ];
39
+
40
+ const rows = logData?.map((item, index) => ({
41
+ id: index.toString(),
42
+ script_name: item.script_name,
43
+ start_time: item?.start_time ? formatDate(new Date(item?.start_time)) : '--',
44
+ stop_time: item?.stop_time ? formatDate(new Date(item?.stop_time)) : '--',
45
+ status: item.status,
46
+ }));
47
+
48
+ const { results: paginatedData, currentPage, goTo } = usePagination(rows, pageSize);
49
+
50
+ return (
51
+ <div className={styles.table}>
52
+ <CardHeader title={t('facilityInfo', 'Facility Info')} children={''} />
53
+ <div className={styles.logTable}>
54
+ {isLoading && logData.length === 0 ? (
55
+ <DataTableSkeleton
56
+ headers={headers}
57
+ aria-label="etl table"
58
+ showToolbar={false}
59
+ showHeader={false}
60
+ rowCount={4}
61
+ zebra
62
+ columnCount={3}
63
+ className={styles.dataTableSkeleton}
64
+ />
65
+ ) : logData.length === 0 ? (
66
+ <EmptyState subTitle={t('noRecordsFound', 'No ETL Operation logs found')} />
67
+ ) : (
68
+ <>
69
+ <DataTable rows={paginatedData} headers={headers} isSortable size={isTablet ? 'lg' : 'sm'} useZebraStyles>
70
+ {({ rows, headers, getHeaderProps, getTableProps }) => (
71
+ <TableContainer className={styles.tableContainer}>
72
+ <Table {...getTableProps()}>
73
+ <TableHead>
74
+ <TableRow>
75
+ {headers.map((header) => (
76
+ <TableHeader
77
+ key={header.key}
78
+ className={classNames(styles.productiveHeading01, styles.text02)}
79
+ {...getHeaderProps({ header })}>
80
+ {header.header}
81
+ </TableHeader>
82
+ ))}
83
+ </TableRow>
84
+ </TableHead>
85
+ <TableBody>
86
+ {rows.map((row) => (
87
+ <TableRow key={row.id}>
88
+ {row.cells.map((cell) => (
89
+ <TableCell key={cell.id}>
90
+ {cell.info.header === 'status' ? (
91
+ <Tag size="md" type={cell.value === 'Success' ? 'green' : 'red'}>
92
+ {cell.value}
93
+ </Tag>
94
+ ) : (
95
+ cell.value
96
+ )}
97
+ </TableCell>
98
+ ))}
99
+ </TableRow>
100
+ ))}
101
+ </TableBody>
102
+ </Table>
103
+ </TableContainer>
104
+ )}
105
+ </DataTable>
106
+ <PatientChartPagination
107
+ currentItems={paginatedData.length}
108
+ totalItems={rows.length}
109
+ onPageNumberChange={({ page }) => goTo(page)}
110
+ pageNumber={currentPage}
111
+ pageSize={pageSize}
112
+ />
113
+ </>
114
+ )}
115
+ </div>
116
+ </div>
117
+ );
118
+ };
119
+
120
+ export default LogTable;
@@ -0,0 +1,10 @@
1
+ @use '@carbon/layout';
2
+ @use '@carbon/type';
3
+ @use '@carbon/colors';
4
+
5
+ .logTable {
6
+ margin: 0 auto;
7
+ }
8
+ .table {
9
+ border: 1px solid colors.$gray-30;
10
+ }
@@ -0,0 +1,47 @@
1
+ import React from 'react';
2
+ import { render, screen } from '@testing-library/react';
3
+ import '@testing-library/jest-dom';
4
+ import LogTable from './operation-log-table.component';
5
+ import { ETLResponse } from '../../types';
6
+
7
+ describe('LogTable Component', () => {
8
+ const mockLogData: ETLResponse[] = [
9
+ {
10
+ script_name: 'Initial Population of Tables',
11
+ start_time: '2024-11-28T09:30:12',
12
+ stop_time: '2024-11-28T09:33:15',
13
+ status: 'Success',
14
+ },
15
+ {
16
+ script_name: 'Initial Creation of Tables',
17
+ start_time: '2024-11-28T09:30:12',
18
+ stop_time: '2024-11-28T09:33:15',
19
+ status: 'Failed',
20
+ },
21
+ ];
22
+
23
+ it('renders the table with correct headers and rows', () => {
24
+ render(<LogTable logData={mockLogData} isLoading={false} />);
25
+
26
+ const headers = ['Procedure', 'Start time', 'End time', 'Completion status'];
27
+ headers.forEach((header) => {
28
+ expect(screen.getByText(header)).toBeInTheDocument();
29
+ });
30
+
31
+ const rows = screen.getAllByRole('row');
32
+ expect(rows.length).toBe(3);
33
+ });
34
+
35
+ it('displays "No ETL Operation logs found" when logData is empty', () => {
36
+ render(<LogTable logData={[]} isLoading={false} />);
37
+
38
+ expect(screen.getByText('No ETL Operation logs found')).toBeInTheDocument();
39
+ });
40
+
41
+ it('shows skeleton loading state when isLoading is true', () => {
42
+ render(<LogTable logData={[]} isLoading={true} />);
43
+
44
+ const skeleton = screen.getByLabelText('etl table');
45
+ expect(skeleton).toBeInTheDocument();
46
+ });
47
+ });
@@ -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,170 @@
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 { CustomHIEPractitionerResponse, type PractitionerResponse } from '../../types';
7
+ import { NormalizedPractitioner } from '../hook/healthWorkerAdapter';
8
+ import styles from './hwr-confirmation.modal.scss';
9
+ import { formatDateTime } from '../../utils/utils';
10
+
11
+ interface HealthWorkerInfoProps {
12
+ label: string;
13
+ value: string | boolean | React.ReactNode;
14
+ }
15
+
16
+ const HealthWorkerInfo: React.FC<HealthWorkerInfoProps> = ({ label, value }) => {
17
+ return (
18
+ <div className={styles.healthWorkerInfoContainer}>
19
+ <span className={styles.healthWorkerInfoLabel}>{label}</span>
20
+ <span>{value}</span>
21
+ </div>
22
+ );
23
+ };
24
+
25
+ interface HWRConfirmModalProps {
26
+ onConfirm: () => void;
27
+ close: () => void;
28
+ healthWorker: CustomHIEPractitionerResponse | PractitionerResponse;
29
+ normalizedData: NormalizedPractitioner;
30
+ fhirFormat: boolean;
31
+ }
32
+
33
+ const HWRConfirmModal: React.FC<HWRConfirmModalProps> = ({
34
+ close,
35
+ onConfirm,
36
+ healthWorker,
37
+ normalizedData,
38
+ fhirFormat,
39
+ }) => {
40
+ const { t } = useTranslation();
41
+
42
+ const isLicenseValid = normalizedData.licenseEndDate ? new Date(normalizedData.licenseEndDate) > new Date() : false;
43
+
44
+ return (
45
+ <>
46
+ <div className="cds--modal-header">
47
+ <h3 className="cds--modal-header__heading">{t('healthWorkerRegistry', 'Health worker registry')}</h3>
48
+ </div>
49
+ <div className="cds--modal-content">
50
+ <p>
51
+ {t(
52
+ 'healthWorkerDetailsFound',
53
+ 'Health worker information found in the registry, do you want to use the information to continue with registration?',
54
+ )}
55
+ </p>
56
+ <div className={styles.healthWorkerOverview}>
57
+ <ExtensionSlot
58
+ className={styles.healthWorkerPhoto}
59
+ name="patient-photo-slot"
60
+ state={{
61
+ patientName: normalizedData.fullName || '',
62
+ }}
63
+ />
64
+ <div style={{ width: '100%', marginLeft: '0.625rem' }}>
65
+ <HealthWorkerInfo
66
+ label={t('healthWorkerName', 'Health worker name')}
67
+ value={normalizedData.fullName || '--'}
68
+ />
69
+
70
+ <HealthWorkerInfo
71
+ label={t('providerUniqueIdentifier', 'Provider unique identifier')}
72
+ value={normalizedData.providerUniqueIdentifier || '--'}
73
+ />
74
+
75
+ <HealthWorkerInfo
76
+ label={t('registrationId', 'Registration ID')}
77
+ value={normalizedData.registrationId || '--'}
78
+ />
79
+
80
+ <HealthWorkerInfo
81
+ label={t('externalReferenceId', 'External Reference ID')}
82
+ value={normalizedData.externalReferenceId || '--'}
83
+ />
84
+
85
+ <HealthWorkerInfo label={t('gender', 'Gender')} value={normalizedData.gender || '--'} />
86
+
87
+ <HealthWorkerInfo label={t('status', 'Status')} value={normalizedData.status || '--'} />
88
+
89
+ {normalizedData.phoneNumber && (
90
+ <HealthWorkerInfo label={t('phone', 'Phone')} value={normalizedData.phoneNumber} />
91
+ )}
92
+
93
+ {normalizedData.email && <HealthWorkerInfo label={t('email', 'Email')} value={normalizedData.email} />}
94
+
95
+ {normalizedData.nationalId && (
96
+ <HealthWorkerInfo
97
+ label={t('identificationNumber', 'Identification Number')}
98
+ value={normalizedData.nationalId}
99
+ />
100
+ )}
101
+
102
+ <HealthWorkerInfo
103
+ label={t('licensingBody', 'Licensing Body')}
104
+ value={normalizedData.licensingBody || '--'}
105
+ />
106
+
107
+ <HealthWorkerInfo label={t('specialty', 'Specialty')} value={normalizedData.specialty || '--'} />
108
+
109
+ {normalizedData.professionalCadre && (
110
+ <HealthWorkerInfo
111
+ label={t('professionalCadre', 'Professional Cadre')}
112
+ value={normalizedData.professionalCadre}
113
+ />
114
+ )}
115
+
116
+ {normalizedData.practiceType && (
117
+ <HealthWorkerInfo label={t('practiceType', 'Practice Type')} value={normalizedData.practiceType} />
118
+ )}
119
+
120
+ {normalizedData.licenses && normalizedData.licenses.length > 0 && (
121
+ <>
122
+ <div style={{ marginTop: '1rem', marginBottom: '0.5rem' }}>
123
+ <strong>{t('licenses', 'Licenses')}</strong>
124
+ </div>
125
+ {normalizedData.licenses.map((license, index) => (
126
+ <div key={index} style={{ marginBottom: '0.5rem', paddingLeft: '1rem' }}>
127
+ <HealthWorkerInfo
128
+ label={`${license.license_type} ${t('license', 'License')}`}
129
+ value={license.external_reference_id || '--'}
130
+ />
131
+ <HealthWorkerInfo
132
+ label={t('validity', 'Validity')}
133
+ value={`${formatDateTime(license.license_start)} - ${formatDateTime(license.license_end)}`}
134
+ />
135
+ </div>
136
+ ))}
137
+ </>
138
+ )}
139
+
140
+ {normalizedData.licenseStartDate && normalizedData.licenseEndDate && (
141
+ <HealthWorkerInfo
142
+ label={t('licenseValidity', 'License Validity Period')}
143
+ value={`${formatDateTime(normalizedData.licenseStartDate)} - ${formatDateTime(
144
+ normalizedData.licenseEndDate,
145
+ )}`}
146
+ />
147
+ )}
148
+
149
+ <HealthWorkerInfo
150
+ label={t('licenseValid', 'License Validity')}
151
+ value={
152
+ <Tag type={isLicenseValid ? 'green' : 'red'}>
153
+ {isLicenseValid ? t('licenseValid', 'License Valid') : t('licenseExpired', 'License Expired')}
154
+ </Tag>
155
+ }
156
+ />
157
+ </div>
158
+ </div>
159
+ </div>
160
+ <div className="cds--modal-footer">
161
+ <Button kind="secondary" onClick={close}>
162
+ {t('cancel', 'Cancel')}
163
+ </Button>
164
+ <Button onClick={onConfirm}>{t('useValues', 'Use values')}</Button>
165
+ </div>
166
+ </>
167
+ );
168
+ };
169
+
170
+ export default HWRConfirmModal;