@kenyaemr/esm-pharmacy-app 5.2.1-pre.1001

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 (93) hide show
  1. package/.turbo/turbo-build.log +35 -0
  2. package/LICENSE.md +401 -0
  3. package/README.md +5 -0
  4. package/dist/130.js +2 -0
  5. package/dist/130.js.LICENSE.txt +3 -0
  6. package/dist/130.js.map +1 -0
  7. package/dist/152.js +1 -0
  8. package/dist/152.js.map +1 -0
  9. package/dist/255.js +2 -0
  10. package/dist/255.js.LICENSE.txt +9 -0
  11. package/dist/255.js.map +1 -0
  12. package/dist/282.js +1 -0
  13. package/dist/282.js.map +1 -0
  14. package/dist/303.js +1 -0
  15. package/dist/303.js.map +1 -0
  16. package/dist/419.js +1 -0
  17. package/dist/419.js.map +1 -0
  18. package/dist/515.js +1 -0
  19. package/dist/515.js.map +1 -0
  20. package/dist/548.js +1 -0
  21. package/dist/548.js.map +1 -0
  22. package/dist/574.js +1 -0
  23. package/dist/591.js +2 -0
  24. package/dist/591.js.LICENSE.txt +32 -0
  25. package/dist/591.js.map +1 -0
  26. package/dist/637.js +1 -0
  27. package/dist/637.js.map +1 -0
  28. package/dist/661.js +1 -0
  29. package/dist/661.js.map +1 -0
  30. package/dist/712.js +1 -0
  31. package/dist/712.js.map +1 -0
  32. package/dist/729.js +1 -0
  33. package/dist/729.js.map +1 -0
  34. package/dist/784.js +2 -0
  35. package/dist/784.js.LICENSE.txt +9 -0
  36. package/dist/784.js.map +1 -0
  37. package/dist/883.js +1 -0
  38. package/dist/883.js.map +1 -0
  39. package/dist/993.js +2 -0
  40. package/dist/993.js.LICENSE.txt +35 -0
  41. package/dist/993.js.map +1 -0
  42. package/dist/kenyaemr-esm-pharmacy-app.js +1 -0
  43. package/dist/kenyaemr-esm-pharmacy-app.js.buildmanifest.json +556 -0
  44. package/dist/kenyaemr-esm-pharmacy-app.js.map +1 -0
  45. package/dist/main.js +1 -0
  46. package/dist/main.js.map +1 -0
  47. package/dist/routes.json +1 -0
  48. package/jest.config.js +8 -0
  49. package/package.json +55 -0
  50. package/src/autosuggest/autosuggest.component.tsx +187 -0
  51. package/src/autosuggest/autosuggest.scss +62 -0
  52. package/src/config-schema.ts +49 -0
  53. package/src/declarations.d.ts +6 -0
  54. package/src/hooks/index.ts +4 -0
  55. package/src/hooks/usePharmacies.ts +20 -0
  56. package/src/hooks/usePharmacy.ts +17 -0
  57. package/src/hooks/usePharmacyPatients.ts +48 -0
  58. package/src/hooks/usePharmacyUsers.ts +43 -0
  59. package/src/hooks/useUserMappedPharmacies.ts +47 -0
  60. package/src/index.ts +41 -0
  61. package/src/pharamacy-forms/delete-confirm-dialog.modal.tsx +26 -0
  62. package/src/pharamacy-forms/pharamacy-assignment-form.workspace.tsx +121 -0
  63. package/src/pharamacy-forms/pharmacy-assignment-form.scss +125 -0
  64. package/src/pharmacy-component/pharmacy.component.tsx +14 -0
  65. package/src/pharmacy-detail/pharmacy-detail.component.tsx +14 -0
  66. package/src/pharmacy-detail-header/pharmacy-detail-header.component.tsx +41 -0
  67. package/src/pharmacy-header/pharmacy-header.component.tsx +36 -0
  68. package/src/pharmacy-header/pharmacy-header.scss +71 -0
  69. package/src/pharmacy-header/pharmacy-illustration.component.tsx +13 -0
  70. package/src/pharmacy-left-panel/pharmacy-left-panel-link.component.tsx +40 -0
  71. package/src/pharmacy-metrics/pharmacy-card.component.tsx +47 -0
  72. package/src/pharmacy-metrics/pharmacy-card.scss +80 -0
  73. package/src/pharmacy-metrics/pharmacy-header.scss +22 -0
  74. package/src/pharmacy-metrics/pharmacy-metrics-header.component.tsx +30 -0
  75. package/src/pharmacy-metrics/pharmacy-metrics.component.tsx +43 -0
  76. package/src/pharmacy-metrics/pharmacy-metrics.scss +11 -0
  77. package/src/pharmacy-tables/pharmacy-patients.component.tsx +228 -0
  78. package/src/pharmacy-tables/pharmacy-table.component.tsx +164 -0
  79. package/src/pharmacy-tables/pharmacy-tables.scss +49 -0
  80. package/src/pharmacy-tables/pharmacy-users.component.tsx +197 -0
  81. package/src/pharmacy-tabs/pharmacy-tabs-component.tsx +28 -0
  82. package/src/pharmacy-tabs/pharmacy-tabs.scss +28 -0
  83. package/src/pharmacy.mock.ts +0 -0
  84. package/src/pharmacy.resources.ts +55 -0
  85. package/src/root.component.tsx +19 -0
  86. package/src/root.scss +15 -0
  87. package/src/root.test.tsx +5 -0
  88. package/src/routes.json +38 -0
  89. package/src/setup-tests.ts +1 -0
  90. package/src/types/index.ts +67 -0
  91. package/translations/en.json +28 -0
  92. package/tsconfig.json +5 -0
  93. package/webpack.config.js +1 -0
@@ -0,0 +1,43 @@
1
+ import React from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import styles from './pharmacy-metrics.scss';
4
+ import MetricsHeader from './pharmacy-metrics-header.component';
5
+ import MetricsCard from './pharmacy-card.component';
6
+
7
+ export interface Service {
8
+ uuid: string;
9
+ display: string;
10
+ }
11
+
12
+ function PharmacyMetrics() {
13
+ const { t } = useTranslation();
14
+
15
+ return (
16
+ <>
17
+ <MetricsHeader />
18
+ <div className={styles.cardContainer} data-testid="clinic-metrics">
19
+ <MetricsCard
20
+ label={t('pharmacies', 'Total community phamacies')}
21
+ value={'0'}
22
+ headerLabel={t('pharmacies', 'Phamacies')}
23
+ />
24
+ <MetricsCard
25
+ label={t('tagged', 'Total tagged pharmacies')}
26
+ value={'0'}
27
+ headerLabel={`${t('tagged', 'Tagged phamacies')}:`}></MetricsCard>
28
+ <MetricsCard
29
+ label={t('patient', 'Total assigned patients')}
30
+ value={'0'}
31
+ headerLabel={t('patient', 'Assigned patients')}
32
+ />
33
+ <MetricsCard
34
+ label={t('users', 'Total assigned users')}
35
+ value={'0'}
36
+ headerLabel={t('users', 'Assigned Users')}
37
+ />
38
+ </div>
39
+ </>
40
+ );
41
+ }
42
+
43
+ export default PharmacyMetrics;
@@ -0,0 +1,11 @@
1
+ @use '@carbon/styles/scss/spacing';
2
+ @import '~@openmrs/esm-styleguide/src/vars';
3
+
4
+ .cardContainer {
5
+ background-color: $ui-02;
6
+ display: flex;
7
+ justify-content: space-between;
8
+ padding: 0 spacing.$spacing-05 spacing.$spacing-07 spacing.$spacing-03;
9
+ flex-flow: row wrap;
10
+ margin-top: -(spacing.$spacing-03);
11
+ }
@@ -0,0 +1,228 @@
1
+ import {
2
+ Button,
3
+ DataTable,
4
+ DataTableSkeleton,
5
+ Layer,
6
+ Pagination,
7
+ Table,
8
+ TableBody,
9
+ TableCell,
10
+ TableContainer,
11
+ TableHead,
12
+ TableHeader,
13
+ TableRow,
14
+ Tile,
15
+ } from '@carbon/react';
16
+ import { Add, TrashCan } from '@carbon/react/icons';
17
+ import {
18
+ ConfigurableLink,
19
+ ErrorState,
20
+ UserHasAccess,
21
+ isDesktop,
22
+ launchWorkspace,
23
+ showModal,
24
+ showSnackbar,
25
+ useLayoutType,
26
+ usePagination,
27
+ } from '@openmrs/esm-framework';
28
+ import { CardHeader, EmptyDataIllustration, usePaginationInfo } from '@openmrs/esm-patient-common-lib';
29
+ import React, { useState } from 'react';
30
+ import { useTranslation } from 'react-i18next';
31
+ import { useParams } from 'react-router-dom';
32
+ import { mutate } from 'swr';
33
+ import { usePharmacyPatients } from '../hooks';
34
+ import { revokePharamacyAssignment } from '../pharmacy.resources';
35
+ import styles from './pharmacy-tables.scss';
36
+
37
+ export const PharmacyPatients: React.FC = () => {
38
+ const { pharmacyUuid } = useParams();
39
+ const { t } = useTranslation();
40
+ const [currentPageSize, setCurrentPageSize] = useState<number>(10);
41
+ const { error, patients, isLoading } = usePharmacyPatients(pharmacyUuid);
42
+ const headerTitle = t('pharmacyPatients', 'Pharmacy Patients');
43
+
44
+ const [pageSize, setPageSize] = useState(10);
45
+
46
+ const { results, totalPages, currentPage, goTo } = usePagination(patients, pageSize);
47
+ const { pageSizes } = usePaginationInfo(pageSize, totalPages, currentPage, results.length);
48
+ const layout = useLayoutType();
49
+
50
+ const launchDeleteModal = (patientUuid: string) => {
51
+ const dispose = showModal('pharmacy-delete-confirm-dialog', {
52
+ onDelete: () => {
53
+ handleRevoke(patientUuid);
54
+ dispose();
55
+ },
56
+ onClose: () => dispose(),
57
+ });
58
+ };
59
+
60
+ const headers = [
61
+ {
62
+ header: t('openmrsId', 'OpenMRS ID'),
63
+ key: 'openmrsId',
64
+ },
65
+ {
66
+ header: t('name', 'Name'),
67
+ key: 'name',
68
+ },
69
+ {
70
+ header: t('age', 'Age'),
71
+ key: 'age',
72
+ },
73
+ {
74
+ header: t('sex', 'Sex'),
75
+ key: 'sex',
76
+ },
77
+ {
78
+ header: t('contact', 'Contact'),
79
+ key: 'contact',
80
+ },
81
+ {
82
+ header: t('dateMapped', 'Date Mapped'),
83
+ key: 'dateMapped',
84
+ },
85
+ {
86
+ header: t('actions', 'Actions'),
87
+ key: 'actions',
88
+ },
89
+ ];
90
+
91
+ const tableRows =
92
+ results?.map((patient) => {
93
+ return {
94
+ id: `${patient.uuid}`,
95
+ openmrsId: patient.openmrsId,
96
+ name: (
97
+ <ConfigurableLink
98
+ style={{ textDecoration: 'none' }}
99
+ to={window.getOpenmrsSpaBase() + `patient/${patient.uuid}/chart/Patient Summary`}>
100
+ {patient.name}
101
+ </ConfigurableLink>
102
+ ),
103
+ age: patient.age ?? '--',
104
+ sex: patient.gender ?? '--',
105
+ contact: patient.telephoneContact ?? '--',
106
+ dateMapped: patient.dateMapped,
107
+ actions: (
108
+ <UserHasAccess privilege={'coreapps.systemAdministration'} fallback={`--`}>
109
+ <Button
110
+ kind="tertiary"
111
+ renderIcon={TrashCan}
112
+ onClick={() => {
113
+ launchDeleteModal(patient.uuid);
114
+ }}>
115
+ Revoke
116
+ </Button>
117
+ </UserHasAccess>
118
+ ),
119
+ };
120
+ }) ?? [];
121
+
122
+ const handdleAssignToPharmacy = () => {
123
+ launchWorkspace('pharmacy-assignment-form', {
124
+ workspaceTitle: 'Pharmacy Assignment Form',
125
+ pharmacyUuid,
126
+ type: 'org.openmrs.Patient',
127
+ });
128
+ };
129
+
130
+ const handleRevoke = async (patientUuid: string) => {
131
+ try {
132
+ const results = await revokePharamacyAssignment({
133
+ entityIdentifier: patientUuid,
134
+ basisIdentifier: pharmacyUuid,
135
+ entityType: 'org.openmrs.Patient',
136
+ basisType: 'org.openmrs.Location',
137
+ });
138
+ showSnackbar({ kind: 'success', subtitle: results, title: 'Success' });
139
+
140
+ mutate((key) => {
141
+ return typeof key === 'string' && key.startsWith(`/ws/rest/v1/datafilter/search?type=org.openmrs.Patient`);
142
+ });
143
+ } catch (error) {
144
+ showSnackbar({ kind: 'error', subtitle: error.message, title: 'Failure' });
145
+ }
146
+ };
147
+
148
+ if (isLoading) {
149
+ return <DataTableSkeleton rowCount={5} />;
150
+ }
151
+ if (error) {
152
+ return <ErrorState headerTitle={headerTitle} error={error} />;
153
+ }
154
+
155
+ if (patients.length === 0) {
156
+ return (
157
+ <Layer>
158
+ <Tile className={styles.tile}>
159
+ <div className={!isDesktop(layout) ? styles.tabletHeading : styles.desktopHeading}>
160
+ <h4>{headerTitle}</h4>
161
+ </div>
162
+ <EmptyDataIllustration />
163
+ <p className={styles.content}>{t('noPharmacyPatients', 'No Pharmacy Patients to list.')}</p>
164
+ <UserHasAccess privilege={'coreapps.systemAdministration'}>
165
+ <Button onClick={handdleAssignToPharmacy} renderIcon={Add} kind="ghost">
166
+ {t('assignPatient', 'Assign Patient')}
167
+ </Button>
168
+ </UserHasAccess>
169
+ </Tile>
170
+ </Layer>
171
+ );
172
+ }
173
+ return (
174
+ <div className={styles.widgetContainer}>
175
+ <CardHeader title={headerTitle}>
176
+ <UserHasAccess privilege={'coreapps.systemAdministration'}>
177
+ <Button onClick={handdleAssignToPharmacy} renderIcon={Add} kind="ghost">
178
+ {t('assignPatient', 'Assign Patient')}
179
+ </Button>
180
+ </UserHasAccess>
181
+ </CardHeader>
182
+ <DataTable
183
+ useZebraStyles
184
+ size="sm"
185
+ rows={tableRows ?? []}
186
+ headers={headers}
187
+ render={({ rows, headers, getHeaderProps, getTableProps, getTableContainerProps }) => (
188
+ <TableContainer {...getTableContainerProps()}>
189
+ <Table {...getTableProps()}>
190
+ <TableHead>
191
+ <TableRow>
192
+ {headers.map((header) => (
193
+ <TableHeader
194
+ {...getHeaderProps({
195
+ header,
196
+ isSortable: header.isSortable,
197
+ })}>
198
+ {header.header?.content ?? header.header}
199
+ </TableHeader>
200
+ ))}
201
+ </TableRow>
202
+ </TableHead>
203
+ <TableBody>
204
+ {rows.map((row) => (
205
+ <TableRow key={row.id}>
206
+ {row.cells.map((cell) => (
207
+ <TableCell key={cell.id}>{cell.value}</TableCell>
208
+ ))}
209
+ </TableRow>
210
+ ))}
211
+ </TableBody>
212
+ </Table>
213
+ </TableContainer>
214
+ )}
215
+ />
216
+ <Pagination
217
+ page={currentPage}
218
+ pageSize={pageSize}
219
+ pageSizes={pageSizes}
220
+ totalItems={patients.length}
221
+ onChange={({ page, pageSize }) => {
222
+ goTo(page);
223
+ setPageSize(pageSize);
224
+ }}
225
+ />
226
+ </div>
227
+ );
228
+ };
@@ -0,0 +1,164 @@
1
+ import {
2
+ Button,
3
+ DataTable,
4
+ DataTableSkeleton,
5
+ Layer,
6
+ Pagination,
7
+ Table,
8
+ TableBody,
9
+ TableCell,
10
+ TableContainer,
11
+ TableHead,
12
+ TableHeader,
13
+ TableRow,
14
+ Tile,
15
+ } from '@carbon/react';
16
+ import { Add } from '@carbon/react/icons';
17
+ import {
18
+ ConfigurableLink,
19
+ ErrorState,
20
+ isDesktop,
21
+ useLayoutType,
22
+ usePagination,
23
+ useSession,
24
+ } from '@openmrs/esm-framework';
25
+ import { CardHeader, EmptyDataIllustration, usePaginationInfo } from '@openmrs/esm-patient-common-lib';
26
+ import React, { useState } from 'react';
27
+ import { useTranslation } from 'react-i18next';
28
+ import { useUserMappedPharmacies } from '../hooks';
29
+ import styles from './pharmacy-tables.scss';
30
+
31
+ const PharmaciesTable = () => {
32
+ const { t } = useTranslation();
33
+ const [currentPageSize, setCurrentPageSize] = useState<number>(10);
34
+ const {
35
+ user: { uuid: sessionUserUuid },
36
+ } = useSession();
37
+ const { error, pharmacies, isLoading } = useUserMappedPharmacies(sessionUserUuid);
38
+
39
+ const headerTitle = t('communityPharmacies', 'Community pharmacies');
40
+
41
+ const [pageSize, setPageSize] = useState(10);
42
+
43
+ const { results, totalPages, currentPage, goTo } = usePagination(pharmacies, pageSize);
44
+ const { pageSizes } = usePaginationInfo(pageSize, totalPages, currentPage, results.length);
45
+ const layout = useLayoutType();
46
+
47
+ const headers = [
48
+ {
49
+ header: t('name', 'Name'),
50
+ key: 'name',
51
+ },
52
+ {
53
+ header: t('dateMapped', 'Date Mapped'),
54
+ key: 'dateMapped',
55
+ },
56
+ {
57
+ header: t('cityVillage', 'City village'),
58
+ key: 'cityVillage',
59
+ },
60
+ {
61
+ header: t('stateProvince', 'State province'),
62
+ key: 'stateProvince',
63
+ },
64
+ {
65
+ header: t('countyDistrict', 'County District'),
66
+ key: 'countyDistrict',
67
+ },
68
+ ];
69
+
70
+ const tableRows =
71
+ results?.map((pharmacy) => {
72
+ return {
73
+ id: `${pharmacy.uuid}`,
74
+ name: (
75
+ <ConfigurableLink
76
+ to={window.getOpenmrsSpaBase() + `home/pharmacy/${pharmacy.uuid}`}
77
+ style={{ textDecoration: 'none' }}>
78
+ {pharmacy.name}
79
+ </ConfigurableLink>
80
+ ),
81
+ description: pharmacy.description ?? '--',
82
+ cityVillage: pharmacy.cityVillage ?? '--',
83
+ stateProvince: pharmacy.stateProvince ?? '--',
84
+ countyDistrict: pharmacy.countyDistrict ?? '--',
85
+ dateMapped: pharmacy.dateMaped,
86
+ };
87
+ }) ?? [];
88
+
89
+ const handdleTagPharmacy = () => {};
90
+
91
+ if (isLoading) {
92
+ return <DataTableSkeleton rowCount={5} />;
93
+ }
94
+ if (error) {
95
+ return <ErrorState headerTitle={headerTitle} error={error} />;
96
+ }
97
+
98
+ if (pharmacies.length === 0) {
99
+ return (
100
+ <Layer>
101
+ <Tile className={styles.tile}>
102
+ <div className={!isDesktop(layout) ? styles.tabletHeading : styles.desktopHeading}>
103
+ <h4>{headerTitle}</h4>
104
+ </div>
105
+ <EmptyDataIllustration />
106
+ <p className={styles.content}>{t('noCommunityPharmacyToList', 'No Community Pharmacies to list.')}</p>
107
+ </Tile>
108
+ </Layer>
109
+ );
110
+ }
111
+ return (
112
+ <div className={styles.widgetContainer}>
113
+ <CardHeader title={`Community Pharmacies (${pharmacies.length})`}>
114
+ <div />
115
+ </CardHeader>
116
+ <DataTable
117
+ useZebraStyles
118
+ size="sm"
119
+ rows={tableRows ?? []}
120
+ headers={headers}
121
+ render={({ rows, headers, getHeaderProps, getTableProps, getTableContainerProps }) => (
122
+ <TableContainer {...getTableContainerProps()}>
123
+ <Table {...getTableProps()}>
124
+ <TableHead>
125
+ <TableRow>
126
+ {headers.map((header) => (
127
+ <TableHeader
128
+ {...getHeaderProps({
129
+ header,
130
+ isSortable: header.isSortable,
131
+ })}>
132
+ {header.header?.content ?? header.header}
133
+ </TableHeader>
134
+ ))}
135
+ </TableRow>
136
+ </TableHead>
137
+ <TableBody>
138
+ {rows.map((row) => (
139
+ <TableRow key={row.id}>
140
+ {row.cells.map((cell) => (
141
+ <TableCell key={cell.id}>{cell.value}</TableCell>
142
+ ))}
143
+ </TableRow>
144
+ ))}
145
+ </TableBody>
146
+ </Table>
147
+ </TableContainer>
148
+ )}
149
+ />
150
+ <Pagination
151
+ page={currentPage}
152
+ pageSize={pageSize}
153
+ pageSizes={pageSizes}
154
+ totalItems={pharmacies.length}
155
+ onChange={({ page, pageSize }) => {
156
+ goTo(page);
157
+ setPageSize(pageSize);
158
+ }}
159
+ />
160
+ </div>
161
+ );
162
+ };
163
+
164
+ export default PharmaciesTable;
@@ -0,0 +1,49 @@
1
+ @use '@carbon/colors';
2
+ @use '@carbon/layout';
3
+ @use '@carbon/type';
4
+ @import '~@openmrs/esm-styleguide/src/vars';
5
+
6
+ .widgetContainer {
7
+ background-color: colors.$white-0;
8
+ border: 1px solid colors.$gray-20;
9
+ margin-bottom: 1rem;
10
+ }
11
+
12
+ .widgetContainer :global(.cds--data-table) thead th button span {
13
+ height: unset !important;
14
+ }
15
+
16
+ .tile {
17
+ text-align: center;
18
+ }
19
+
20
+ .emptyStateContainer {
21
+ margin: 2rem 0;
22
+ }
23
+
24
+ .content {
25
+ @include type.type-style('heading-compact-01');
26
+ color: colors.$gray-70;
27
+ margin-top: layout.$layout-05;
28
+ margin-bottom: layout.$spacing-03;
29
+ }
30
+
31
+ .desktopHeading,
32
+ .tabletHeading {
33
+ text-align: left;
34
+ text-transform: capitalize;
35
+
36
+ h4 {
37
+ @include type.type-style('heading-compact-02');
38
+ color: colors.$gray-70;
39
+
40
+ &:after {
41
+ content: '';
42
+ display: block;
43
+ width: 2rem;
44
+ padding-top: 3px;
45
+ border-bottom: 0.375rem solid;
46
+ @include brand-03(border-bottom-color);
47
+ }
48
+ }
49
+ }
@@ -0,0 +1,197 @@
1
+ import {
2
+ Button,
3
+ DataTable,
4
+ DataTableSkeleton,
5
+ Layer,
6
+ Pagination,
7
+ Table,
8
+ TableBody,
9
+ TableCell,
10
+ TableContainer,
11
+ TableHead,
12
+ TableHeader,
13
+ TableRow,
14
+ Tile,
15
+ } from '@carbon/react';
16
+ import { Add, TrashCan } from '@carbon/react/icons';
17
+ import {
18
+ ErrorState,
19
+ UserHasAccess,
20
+ isDesktop,
21
+ launchWorkspace,
22
+ showModal,
23
+ showSnackbar,
24
+ useLayoutType,
25
+ usePagination,
26
+ } from '@openmrs/esm-framework';
27
+ import { CardHeader, EmptyDataIllustration, usePaginationInfo } from '@openmrs/esm-patient-common-lib';
28
+ import React, { useState } from 'react';
29
+ import { useTranslation } from 'react-i18next';
30
+ import { useParams } from 'react-router-dom';
31
+ import { mutate } from 'swr';
32
+ import { usePharmacyUsers } from '../hooks';
33
+ import { revokePharamacyAssignment } from '../pharmacy.resources';
34
+ import styles from './pharmacy-tables.scss';
35
+
36
+ export const PharmacyUsers: React.FC = () => {
37
+ const { pharmacyUuid } = useParams();
38
+ const { t } = useTranslation();
39
+ const [currentPageSize, setCurrentPageSize] = useState<number>(10);
40
+ const { error, users, isLoading } = usePharmacyUsers(pharmacyUuid);
41
+ const headerTitle = t('pharmacyUsers', 'Pharmacy Users');
42
+
43
+ const [pageSize, setPageSize] = useState(10);
44
+
45
+ const { results, totalPages, currentPage, goTo } = usePagination(users, pageSize);
46
+ const { pageSizes } = usePaginationInfo(pageSize, totalPages, currentPage, results.length);
47
+ const layout = useLayoutType();
48
+
49
+ const headers = [
50
+ {
51
+ header: t('name', 'Name'),
52
+ key: 'name',
53
+ },
54
+ {
55
+ header: t('dateMapped', 'Date Mapped'),
56
+ key: 'dateMapped',
57
+ },
58
+ {
59
+ header: t('actions', 'Actions'),
60
+ key: 'actions',
61
+ },
62
+ ];
63
+
64
+ const tableRows =
65
+ results?.map((user) => {
66
+ return {
67
+ id: `${user.uuid}`,
68
+ name: user.name,
69
+ dateMapped: user.dateMapped,
70
+ actions: (
71
+ <UserHasAccess privilege={'coreapps.systemAdministration'} fallback="--">
72
+ <Button
73
+ kind="tertiary"
74
+ renderIcon={TrashCan}
75
+ onClick={() => {
76
+ const dispose = showModal('pharmacy-delete-confirm-dialog', {
77
+ onDelete: () => {
78
+ handleRevoke(user.uuid);
79
+ dispose();
80
+ },
81
+ onClose: () => dispose(),
82
+ });
83
+ }}>
84
+ Revoke
85
+ </Button>
86
+ </UserHasAccess>
87
+ ),
88
+ };
89
+ }) ?? [];
90
+
91
+ const handdleAssignToPharmacy = () => {
92
+ launchWorkspace('pharmacy-assignment-form', {
93
+ workspaceTitle: 'Pharmacy Assignment Form',
94
+ pharmacyUuid,
95
+ type: 'org.openmrs.User',
96
+ });
97
+ };
98
+
99
+ const handleRevoke = async (userUuid: string) => {
100
+ try {
101
+ const results = await revokePharamacyAssignment({
102
+ entityIdentifier: userUuid,
103
+ basisIdentifier: pharmacyUuid,
104
+ entityType: 'org.openmrs.User',
105
+ basisType: 'org.openmrs.Location',
106
+ });
107
+ showSnackbar({ kind: 'success', subtitle: results, title: 'Success' });
108
+
109
+ mutate((key) => {
110
+ return typeof key === 'string' && key.startsWith(`/ws/rest/v1/datafilter/search?type=org.openmrs.User`);
111
+ });
112
+ } catch (error) {
113
+ showSnackbar({ kind: 'error', subtitle: error.message, title: 'Failure' });
114
+ }
115
+ };
116
+
117
+ if (isLoading) {
118
+ return <DataTableSkeleton rowCount={5} />;
119
+ }
120
+ if (error) {
121
+ return <ErrorState headerTitle={headerTitle} error={error} />;
122
+ }
123
+
124
+ if (users.length === 0) {
125
+ return (
126
+ <Layer>
127
+ <Tile className={styles.tile}>
128
+ <div className={!isDesktop(layout) ? styles.tabletHeading : styles.desktopHeading}>
129
+ <h4>{headerTitle}</h4>
130
+ </div>
131
+ <EmptyDataIllustration />
132
+ <p className={styles.content}>{t('noPharmacyUsers', 'No Pharmacy users to list.')}</p>
133
+ <UserHasAccess privilege={'coreapps.systemAdministration'}>
134
+ <Button onClick={handdleAssignToPharmacy} renderIcon={Add} kind="ghost">
135
+ {t('assignUser', 'Assign User')}
136
+ </Button>
137
+ </UserHasAccess>
138
+ </Tile>
139
+ </Layer>
140
+ );
141
+ }
142
+ return (
143
+ <div className={styles.widgetContainer}>
144
+ <CardHeader title={headerTitle}>
145
+ <UserHasAccess privilege={`coreapps.systemAdministration`}>
146
+ <Button onClick={handdleAssignToPharmacy} renderIcon={Add} kind="ghost">
147
+ {t('assignUser', 'Assign User')}
148
+ </Button>
149
+ </UserHasAccess>
150
+ </CardHeader>
151
+ <DataTable
152
+ useZebraStyles
153
+ size="sm"
154
+ rows={tableRows ?? []}
155
+ headers={headers}
156
+ render={({ rows, headers, getHeaderProps, getTableProps, getTableContainerProps }) => (
157
+ <TableContainer {...getTableContainerProps()}>
158
+ <Table {...getTableProps()}>
159
+ <TableHead>
160
+ <TableRow>
161
+ {headers.map((header) => (
162
+ <TableHeader
163
+ {...getHeaderProps({
164
+ header,
165
+ isSortable: header.isSortable,
166
+ })}>
167
+ {header.header?.content ?? header.header}
168
+ </TableHeader>
169
+ ))}
170
+ </TableRow>
171
+ </TableHead>
172
+ <TableBody>
173
+ {rows.map((row) => (
174
+ <TableRow key={row.id}>
175
+ {row.cells.map((cell) => (
176
+ <TableCell key={cell.id}>{cell.value}</TableCell>
177
+ ))}
178
+ </TableRow>
179
+ ))}
180
+ </TableBody>
181
+ </Table>
182
+ </TableContainer>
183
+ )}
184
+ />
185
+ <Pagination
186
+ page={currentPage}
187
+ pageSize={pageSize}
188
+ pageSizes={pageSizes}
189
+ totalItems={users.length}
190
+ onChange={({ page, pageSize }) => {
191
+ goTo(page);
192
+ setPageSize(pageSize);
193
+ }}
194
+ />
195
+ </div>
196
+ );
197
+ };