@ampath/esm-dispensing-app 1.10.0-next.1

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 (277) hide show
  1. package/.editorconfig +12 -0
  2. package/.eslintignore +2 -0
  3. package/.eslintrc +80 -0
  4. package/.husky/pre-commit +7 -0
  5. package/.husky/pre-push +6 -0
  6. package/.prettierignore +12 -0
  7. package/.tx/config +11 -0
  8. package/.yarn/versions/1c40b9b6.yml +0 -0
  9. package/.yarn/versions/ff162597.yml +0 -0
  10. package/LICENSE +401 -0
  11. package/README.md +124 -0
  12. package/__mocks__/react-i18next.js +51 -0
  13. package/dist/1043.js +1 -0
  14. package/dist/1043.js.map +1 -0
  15. package/dist/1119.js +1 -0
  16. package/dist/1197.js +1 -0
  17. package/dist/2146.js +1 -0
  18. package/dist/2177.js +2 -0
  19. package/dist/2177.js.LICENSE.txt +9 -0
  20. package/dist/2177.js.map +1 -0
  21. package/dist/2690.js +1 -0
  22. package/dist/2890.js +2 -0
  23. package/dist/2890.js.LICENSE.txt +14 -0
  24. package/dist/2890.js.map +1 -0
  25. package/dist/2898.js +1 -0
  26. package/dist/2898.js.map +1 -0
  27. package/dist/3041.js +1 -0
  28. package/dist/3041.js.map +1 -0
  29. package/dist/3099.js +1 -0
  30. package/dist/3184.js +2 -0
  31. package/dist/3184.js.LICENSE.txt +14 -0
  32. package/dist/3184.js.map +1 -0
  33. package/dist/3568.js +1 -0
  34. package/dist/3568.js.map +1 -0
  35. package/dist/3584.js +1 -0
  36. package/dist/4055.js +1 -0
  37. package/dist/4099.js +1 -0
  38. package/dist/4099.js.map +1 -0
  39. package/dist/4132.js +1 -0
  40. package/dist/4225.js +1 -0
  41. package/dist/4225.js.map +1 -0
  42. package/dist/4300.js +1 -0
  43. package/dist/4335.js +1 -0
  44. package/dist/4353.js +1 -0
  45. package/dist/4353.js.map +1 -0
  46. package/dist/439.js +1 -0
  47. package/dist/4618.js +1 -0
  48. package/dist/4652.js +1 -0
  49. package/dist/4944.js +1 -0
  50. package/dist/5173.js +1 -0
  51. package/dist/5241.js +1 -0
  52. package/dist/5422.js +1 -0
  53. package/dist/5422.js.map +1 -0
  54. package/dist/5442.js +1 -0
  55. package/dist/5661.js +1 -0
  56. package/dist/5897.js +1 -0
  57. package/dist/5897.js.map +1 -0
  58. package/dist/6022.js +1 -0
  59. package/dist/609.js +1 -0
  60. package/dist/609.js.map +1 -0
  61. package/dist/6468.js +1 -0
  62. package/dist/6540.js +2 -0
  63. package/dist/6540.js.LICENSE.txt +9 -0
  64. package/dist/6540.js.map +1 -0
  65. package/dist/6589.js +1 -0
  66. package/dist/6606.js +1 -0
  67. package/dist/6606.js.map +1 -0
  68. package/dist/6679.js +1 -0
  69. package/dist/6825.js +1 -0
  70. package/dist/6825.js.map +1 -0
  71. package/dist/6840.js +1 -0
  72. package/dist/6841.js +1 -0
  73. package/dist/6841.js.map +1 -0
  74. package/dist/6859.js +1 -0
  75. package/dist/7097.js +1 -0
  76. package/dist/7159.js +1 -0
  77. package/dist/723.js +1 -0
  78. package/dist/7240.js +1 -0
  79. package/dist/7240.js.map +1 -0
  80. package/dist/7255.js +1 -0
  81. package/dist/7255.js.map +1 -0
  82. package/dist/7617.js +1 -0
  83. package/dist/795.js +1 -0
  84. package/dist/8163.js +1 -0
  85. package/dist/8349.js +1 -0
  86. package/dist/8371.js +1 -0
  87. package/dist/8569.js +2 -0
  88. package/dist/8569.js.LICENSE.txt +41 -0
  89. package/dist/8569.js.map +1 -0
  90. package/dist/8600.js +1 -0
  91. package/dist/8600.js.map +1 -0
  92. package/dist/8618.js +1 -0
  93. package/dist/8885.js +1 -0
  94. package/dist/8885.js.map +1 -0
  95. package/dist/890.js +1 -0
  96. package/dist/9214.js +1 -0
  97. package/dist/9538.js +1 -0
  98. package/dist/9569.js +1 -0
  99. package/dist/961.js +2 -0
  100. package/dist/961.js.LICENSE.txt +19 -0
  101. package/dist/961.js.map +1 -0
  102. package/dist/963.js +1 -0
  103. package/dist/963.js.map +1 -0
  104. package/dist/986.js +1 -0
  105. package/dist/9879.js +1 -0
  106. package/dist/9895.js +1 -0
  107. package/dist/9900.js +1 -0
  108. package/dist/9913.js +1 -0
  109. package/dist/main.js +2 -0
  110. package/dist/main.js.LICENSE.txt +51 -0
  111. package/dist/main.js.map +1 -0
  112. package/dist/openmrs-esm-dispensing-app.js +1 -0
  113. package/dist/openmrs-esm-dispensing-app.js.buildmanifest.json +1645 -0
  114. package/dist/openmrs-esm-dispensing-app.js.map +1 -0
  115. package/dist/routes.json +1 -0
  116. package/e2e/README.md +119 -0
  117. package/e2e/commands/drug-order-operations.ts +43 -0
  118. package/e2e/commands/encounter-operations.ts +60 -0
  119. package/e2e/commands/index.ts +5 -0
  120. package/e2e/commands/patient-operations.ts +109 -0
  121. package/e2e/commands/provider-operations.ts +9 -0
  122. package/e2e/commands/types/index.ts +157 -0
  123. package/e2e/commands/visit-operations.ts +38 -0
  124. package/e2e/core/global-setup.ts +32 -0
  125. package/e2e/core/index.ts +1 -0
  126. package/e2e/core/test.ts +31 -0
  127. package/e2e/fixtures/api.ts +27 -0
  128. package/e2e/fixtures/index.ts +1 -0
  129. package/e2e/pages/dispensing-page.ts +9 -0
  130. package/e2e/pages/index.ts +1 -0
  131. package/e2e/specs/active-prescriptions.spec.ts +72 -0
  132. package/e2e/specs/close-prescription.spec.ts +71 -0
  133. package/e2e/specs/dispense-medication.spec.ts +71 -0
  134. package/e2e/specs/pause-prescription.spec.ts +72 -0
  135. package/e2e/support/github/Dockerfile +34 -0
  136. package/e2e/support/github/docker-compose.yml +24 -0
  137. package/e2e/support/github/run-e2e-docker-env.sh +42 -0
  138. package/e2e/types/index.ts +157 -0
  139. package/example.env +7 -0
  140. package/jest.config.js +24 -0
  141. package/package.json +110 -0
  142. package/playwright.config.ts +36 -0
  143. package/prettier.config.js +8 -0
  144. package/src/components/action-buttons.component.tsx +87 -0
  145. package/src/components/action-buttons.scss +16 -0
  146. package/src/components/action-buttons.test.tsx +217 -0
  147. package/src/components/medication-card.component.tsx +37 -0
  148. package/src/components/medication-card.scss +20 -0
  149. package/src/components/medication-card.test.tsx +36 -0
  150. package/src/components/medication-dispense-review.scss +108 -0
  151. package/src/components/medication-event.component.tsx +96 -0
  152. package/src/components/medication-event.scss +44 -0
  153. package/src/components/medication-event.test.tsx +212 -0
  154. package/src/components/prescription-actions/close-action-button.component.tsx +50 -0
  155. package/src/components/prescription-actions/dispense-action-button.component.tsx +57 -0
  156. package/src/components/prescription-actions/pause-action-button.component.tsx +49 -0
  157. package/src/conditions/conditions.component.tsx +118 -0
  158. package/src/conditions/conditions.resource.ts +100 -0
  159. package/src/conditions/conditions.scss +26 -0
  160. package/src/conditions/conditions.test.tsx +200 -0
  161. package/src/config-schema.ts +192 -0
  162. package/src/constants.ts +22 -0
  163. package/src/dashboard/dispensing-dashboard-link.component.tsx +36 -0
  164. package/src/dashboard/dispensing-dashboard.component.tsx +36 -0
  165. package/src/declarations.d.ts +2 -0
  166. package/src/diagnoses/diagnoses.component.tsx +111 -0
  167. package/src/diagnoses/diagnoses.resource.ts +30 -0
  168. package/src/diagnoses/diagnoses.scss +31 -0
  169. package/src/dispensing-link.component.tsx +9 -0
  170. package/src/dispensing-tiles/dispensing-tile.component.tsx +42 -0
  171. package/src/dispensing-tiles/dispensing-tile.scss +43 -0
  172. package/src/dispensing-tiles/dispensing-tiles.component.tsx +39 -0
  173. package/src/dispensing-tiles/dispensing-tiles.resource.tsx +30 -0
  174. package/src/dispensing-tiles/dispensing-tiles.scss +11 -0
  175. package/src/dispensing.component.tsx +31 -0
  176. package/src/dispensing.scss +5 -0
  177. package/src/dispensing.test.tsx +9 -0
  178. package/src/fill-prescription/fill-prescription-button.component.tsx +103 -0
  179. package/src/fill-prescription/fill-prescription-button.scss +8 -0
  180. package/src/fill-prescription/on-prescription-filled.modal.tsx +140 -0
  181. package/src/fill-prescription/on-prescription-filled.scss +7 -0
  182. package/src/forms/close-dispense-form.workspace.tsx +194 -0
  183. package/src/forms/dispense-form.workspace.test.tsx +334 -0
  184. package/src/forms/dispense-form.workspace.tsx +324 -0
  185. package/src/forms/forms.scss +152 -0
  186. package/src/forms/medication-dispense-review.component.tsx +649 -0
  187. package/src/forms/medication-dispense-review.test.tsx +158 -0
  188. package/src/forms/pause-dispense-form.workspace.tsx +196 -0
  189. package/src/forms/stock-dispense/stock-dispense.component.tsx +126 -0
  190. package/src/forms/stock-dispense/stock.resource.tsx +67 -0
  191. package/src/history/delete-confirm.modal.tsx +35 -0
  192. package/src/history/history-and-comments.component.tsx +338 -0
  193. package/src/history/history-and-comments.scss +54 -0
  194. package/src/index.ts +57 -0
  195. package/src/location/location.resource.test.tsx +108 -0
  196. package/src/location/location.resource.tsx +32 -0
  197. package/src/medication/medication.resource.test.tsx +156 -0
  198. package/src/medication/medication.resource.tsx +45 -0
  199. package/src/medication-dispense/medication-dispense.resource.test.tsx +243 -0
  200. package/src/medication-dispense/medication-dispense.resource.tsx +178 -0
  201. package/src/medication-request/medication-request.resource.test.tsx +1333 -0
  202. package/src/medication-request/medication-request.resource.tsx +257 -0
  203. package/src/patient/patient-info-cell.component.tsx +26 -0
  204. package/src/patient/patient.resources.ts +14 -0
  205. package/src/pharmacy-header/pharmacy-header.component.tsx +35 -0
  206. package/src/pharmacy-header/pharmacy-header.scss +55 -0
  207. package/src/pharmacy-header/pharmacy-illustration.component.tsx +30 -0
  208. package/src/prescriptions/patient-search-tab-panel.component.tsx +58 -0
  209. package/src/prescriptions/patient-search-tab-panel.scss +26 -0
  210. package/src/prescriptions/prescription-actions.component.tsx +24 -0
  211. package/src/prescriptions/prescription-actions.scss +14 -0
  212. package/src/prescriptions/prescription-details.component.tsx +152 -0
  213. package/src/prescriptions/prescription-details.scss +87 -0
  214. package/src/prescriptions/prescription-details.test.tsx +267 -0
  215. package/src/prescriptions/prescription-expanded.component.tsx +58 -0
  216. package/src/prescriptions/prescription-expanded.scss +56 -0
  217. package/src/prescriptions/prescription-tab-lists.component.tsx +70 -0
  218. package/src/prescriptions/prescription-tab-panel.component.tsx +83 -0
  219. package/src/prescriptions/prescriptions-table.component.tsx +189 -0
  220. package/src/prescriptions/prescriptions.scss +152 -0
  221. package/src/print-prescription/prescription-print-action.component.tsx +30 -0
  222. package/src/print-prescription/prescription-print-preview.modal.tsx +92 -0
  223. package/src/print-prescription/prescription-printout.component.tsx +154 -0
  224. package/src/print-prescription/print-prescription.scss +75 -0
  225. package/src/print-prescription/printable-prescriptions.component.tsx +57 -0
  226. package/src/routes.json +137 -0
  227. package/src/types.ts +530 -0
  228. package/src/utils.test.ts +2947 -0
  229. package/src/utils.ts +637 -0
  230. package/tools/i18next-parser.config.js +89 -0
  231. package/tools/setup-tests.ts +8 -0
  232. package/tools/update-openmrs-deps.mjs +42 -0
  233. package/translations/am.json +133 -0
  234. package/translations/ar.json +133 -0
  235. package/translations/ar_SY.json +133 -0
  236. package/translations/bn.json +133 -0
  237. package/translations/cs.json +133 -0
  238. package/translations/de.json +133 -0
  239. package/translations/en.json +133 -0
  240. package/translations/en_US.json +133 -0
  241. package/translations/es.json +133 -0
  242. package/translations/es_MX.json +133 -0
  243. package/translations/fr.json +133 -0
  244. package/translations/he.json +133 -0
  245. package/translations/hi.json +133 -0
  246. package/translations/hi_IN.json +133 -0
  247. package/translations/id.json +133 -0
  248. package/translations/it.json +133 -0
  249. package/translations/ka.json +133 -0
  250. package/translations/km.json +133 -0
  251. package/translations/ku.json +133 -0
  252. package/translations/ky.json +133 -0
  253. package/translations/lg.json +133 -0
  254. package/translations/ne.json +133 -0
  255. package/translations/pl.json +133 -0
  256. package/translations/pt.json +133 -0
  257. package/translations/pt_BR.json +133 -0
  258. package/translations/qu.json +133 -0
  259. package/translations/ro_RO.json +133 -0
  260. package/translations/ru_RU.json +133 -0
  261. package/translations/si.json +133 -0
  262. package/translations/sq.json +133 -0
  263. package/translations/sw.json +133 -0
  264. package/translations/sw_KE.json +133 -0
  265. package/translations/tr.json +133 -0
  266. package/translations/tr_TR.json +133 -0
  267. package/translations/uk.json +133 -0
  268. package/translations/uz.json +133 -0
  269. package/translations/uz@Latn.json +133 -0
  270. package/translations/uz_UZ.json +133 -0
  271. package/translations/vi.json +133 -0
  272. package/translations/zh.json +133 -0
  273. package/translations/zh_CN.json +133 -0
  274. package/translations/zh_TW.json +133 -0
  275. package/tsconfig.json +23 -0
  276. package/turbo.json +41 -0
  277. package/webpack.config.js +1 -0
@@ -0,0 +1,83 @@
1
+ import React, { useEffect, useRef, useState } from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import { MultiSelect, Search, TabPanel } from '@carbon/react';
4
+ import { useConfig, useDebounce, useSession } from '@openmrs/esm-framework';
5
+ import { type PharmacyConfig } from '../config-schema';
6
+ import PrescriptionsTable from './prescriptions-table.component';
7
+ import styles from './prescriptions.scss';
8
+ import { useLocations } from '../location/location.resource';
9
+ import { type SimpleLocation } from '../types';
10
+
11
+ interface PrescriptionTabPanelProps {
12
+ isTabActive: boolean;
13
+ status?: string;
14
+ customPrescriptionsTableEndpoint?: string;
15
+ }
16
+
17
+ const PrescriptionTabPanel: React.FC<PrescriptionTabPanelProps> = ({
18
+ status,
19
+ isTabActive,
20
+ customPrescriptionsTableEndpoint,
21
+ }) => {
22
+ const { t } = useTranslation();
23
+ const config = useConfig<PharmacyConfig>();
24
+ const isInitialized = useRef(false);
25
+ const { sessionLocation } = useSession();
26
+ const { locations, isLoading: isFilterLocationsLoading } = useLocations(config);
27
+ const [searchTerm, setSearchTerm] = useState('');
28
+ const debouncedSearchTerm = useDebounce(searchTerm, 500);
29
+ const [filterLocations, setFilterLocations] = useState<SimpleLocation[]>([]);
30
+
31
+ // set any initially selected locations
32
+ useEffect(() => {
33
+ if (!isInitialized.current && !isFilterLocationsLoading && sessionLocation?.uuid) {
34
+ setFilterLocations(locations?.filter((l) => sessionLocation?.uuid === l.associatedPharmacyLocation) || []);
35
+ isInitialized.current = true; // we only want to run when the component is first mounted so we don't override user changes
36
+ }
37
+ }, [isFilterLocationsLoading, sessionLocation, locations]);
38
+
39
+ return (
40
+ <TabPanel>
41
+ <div className={styles.searchContainer}>
42
+ {config.locationBehavior?.locationFilter?.enabled &&
43
+ !isFilterLocationsLoading &&
44
+ isInitialized.current &&
45
+ locations?.length > 1 && (
46
+ <MultiSelect
47
+ hideLabel
48
+ id="locationFilter"
49
+ label={t('filterByLocations', 'Filter by locations')}
50
+ initialSelectedItems={filterLocations}
51
+ items={locations}
52
+ itemToString={(item: SimpleLocation) => item?.name}
53
+ onChange={({ selectedItems }) => {
54
+ setFilterLocations(selectedItems);
55
+ }}
56
+ className={styles.locationFilter}
57
+ />
58
+ )}
59
+ <Search
60
+ closeButtonLabelText={t('clearSearchInput', 'Clear search input')}
61
+ defaultValue={searchTerm}
62
+ placeholder={t('searchByPatientIdOrName', 'Search by patient ID or name')}
63
+ labelText={t('searchByPatientIdOrName', 'Search by patient ID or name')}
64
+ onChange={(e) => {
65
+ e.preventDefault();
66
+ setSearchTerm(e.target.value);
67
+ }}
68
+ size="md"
69
+ className={styles.patientSearch}
70
+ />
71
+ </div>
72
+ <PrescriptionsTable
73
+ loadData={isTabActive}
74
+ status={status}
75
+ customPrescriptionsTableEndpoint={customPrescriptionsTableEndpoint}
76
+ debouncedSearchTerm={debouncedSearchTerm}
77
+ locations={filterLocations}
78
+ />
79
+ </TabPanel>
80
+ );
81
+ };
82
+
83
+ export default PrescriptionTabPanel;
@@ -0,0 +1,189 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import {
3
+ DataTable,
4
+ DataTableSkeleton,
5
+ Layer,
6
+ Pagination,
7
+ Table,
8
+ TableBody,
9
+ TableCell,
10
+ TableContainer,
11
+ TableExpandedRow,
12
+ TableExpandHeader,
13
+ TableExpandRow,
14
+ TableHead,
15
+ TableHeader,
16
+ TableRow,
17
+ Tile,
18
+ } from '@carbon/react';
19
+ import { useTranslation } from 'react-i18next';
20
+ import { formatDatetime, parseDate, useConfig } from '@openmrs/esm-framework';
21
+ import { usePrescriptionsTable } from '../medication-request/medication-request.resource';
22
+ import { type PharmacyConfig } from '../config-schema';
23
+ import { type SimpleLocation } from '../types';
24
+ import PatientInfoCell from '../patient/patient-info-cell.component';
25
+ import PrescriptionExpanded from './prescription-expanded.component';
26
+ import styles from './prescriptions.scss';
27
+
28
+ interface PrescriptionsTableProps {
29
+ loadData: boolean;
30
+ debouncedSearchTerm: string;
31
+ locations: SimpleLocation[];
32
+ status?: string;
33
+ customPrescriptionsTableEndpoint?: string;
34
+ }
35
+
36
+ const PrescriptionsTable: React.FC<PrescriptionsTableProps> = ({
37
+ loadData,
38
+ debouncedSearchTerm,
39
+ locations,
40
+ status,
41
+ customPrescriptionsTableEndpoint,
42
+ }) => {
43
+ const { t } = useTranslation();
44
+ const config = useConfig<PharmacyConfig>();
45
+ const [page, setPage] = useState(1);
46
+ const [pageSize, setPageSize] = useState(10);
47
+ const nextOffSet = (page - 1) * pageSize;
48
+ const { prescriptionsTableRows, error, isLoading, totalOrders } = usePrescriptionsTable(
49
+ loadData,
50
+ customPrescriptionsTableEndpoint,
51
+ status,
52
+ pageSize,
53
+ nextOffSet,
54
+ debouncedSearchTerm,
55
+ locations,
56
+ config.medicationRequestExpirationPeriodInDays,
57
+ config.refreshInterval,
58
+ );
59
+
60
+ // reset back to page 1 whenever search term changes
61
+ useEffect(() => {
62
+ setPage(1);
63
+ }, [debouncedSearchTerm]);
64
+
65
+ // dynamic status keys we need to maintain
66
+ // t('active', 'Active')
67
+ // t('paused', 'Paused')
68
+ // t('closed', 'Closed')
69
+ // t('completed', 'Completed')
70
+ // t('expired', 'Expired')
71
+ // t('cancelled', 'Cancelled')
72
+
73
+ let columns = [
74
+ { header: t('created', 'Created'), key: 'created' },
75
+ { header: t('patientName', 'Patient name'), key: 'patient' },
76
+ { header: t('prescriber', 'Prescriber'), key: 'prescriber' },
77
+ { header: t('drugs', 'Drugs'), key: 'drugs' },
78
+ { header: t('lastDispenser', 'Last dispenser'), key: 'lastDispenser' },
79
+ { header: t('status', 'Status'), key: 'status' },
80
+ ];
81
+
82
+ // add the locations column, if enabled
83
+ if (config.locationBehavior?.locationColumn?.enabled) {
84
+ columns = [...columns.slice(0, 3), { header: t('location', 'Location'), key: 'location' }, ...columns.slice(3)];
85
+ }
86
+
87
+ return (
88
+ <div className={styles.patientListTableContainer}>
89
+ {isLoading && <DataTableSkeleton role="progressbar" />}
90
+ {error && (
91
+ <div className={styles.filterEmptyState}>
92
+ <Layer>
93
+ <Tile className={styles.filterEmptyStateTile}>
94
+ <p className={styles.filterEmptyStateContent}>
95
+ {t('errorLoadingPrescriptions', 'Error loading prescriptions')}
96
+ </p>
97
+ <p className={styles.filterEmptyStateHelper}>{error?.message}</p>
98
+ </Tile>
99
+ </Layer>
100
+ </div>
101
+ )}
102
+ {prescriptionsTableRows && (
103
+ <>
104
+ <DataTable rows={prescriptionsTableRows} headers={columns} isSortable>
105
+ {({ rows, headers, getExpandHeaderProps, getHeaderProps, getRowProps, getTableProps }) => (
106
+ <TableContainer>
107
+ <Table {...getTableProps()} useZebraStyles>
108
+ <TableHead>
109
+ <TableRow>
110
+ <TableExpandHeader {...getExpandHeaderProps()} />
111
+ {headers.map((header) => (
112
+ <TableHeader {...getHeaderProps({ header })}>{header.header}</TableHeader>
113
+ ))}
114
+ </TableRow>
115
+ </TableHead>
116
+ <TableBody>
117
+ {rows.map((row) => (
118
+ <React.Fragment key={row.id}>
119
+ <TableExpandRow {...getRowProps({ row })}>
120
+ {row.cells.map((cell) => (
121
+ <TableCell key={cell.id}>
122
+ {cell.id.endsWith('created') ? (
123
+ formatDatetime(parseDate(cell.value))
124
+ ) : cell.id.endsWith('patient') ? (
125
+ <PatientInfoCell patient={cell.value} />
126
+ ) : cell.id.endsWith('status') ? (
127
+ t(cell.value)
128
+ ) : (
129
+ cell.value
130
+ )}
131
+ </TableCell>
132
+ ))}
133
+ </TableExpandRow>
134
+ {row.isExpanded ? (
135
+ <TableExpandedRow colSpan={headers.length + 1}>
136
+ {row.cells.find((cell) => cell.id.endsWith('patient'))?.value?.uuid && (
137
+ <PrescriptionExpanded
138
+ encounterUuid={row.id}
139
+ patientUuid={row.cells.find((cell) => cell.id.endsWith('patient')).value.uuid}
140
+ />
141
+ )}
142
+ </TableExpandedRow>
143
+ ) : (
144
+ <TableExpandedRow className={styles.hiddenRow} colSpan={headers.length + 1} />
145
+ )}
146
+ </React.Fragment>
147
+ ))}
148
+ </TableBody>
149
+ </Table>
150
+ </TableContainer>
151
+ )}
152
+ </DataTable>
153
+ {prescriptionsTableRows?.length === 0 && (
154
+ <div className={styles.filterEmptyState}>
155
+ <Layer>
156
+ <Tile className={styles.filterEmptyStateTile}>
157
+ <p className={styles.filterEmptyStateContent}>
158
+ {t('noPrescriptionsToDisplay', 'No prescriptions to display')}
159
+ </p>
160
+ <p className={styles.filterEmptyStateHelper}>{t('checkFilters', 'Check the filters above')}</p>
161
+ </Tile>
162
+ </Layer>
163
+ </div>
164
+ )}
165
+ {prescriptionsTableRows?.length > 0 && (
166
+ <div className={styles.paginationContainer}>
167
+ <Pagination
168
+ page={page}
169
+ pageSize={pageSize}
170
+ pageSizes={[10, 20, 30, 40, 50, 100]}
171
+ totalItems={totalOrders}
172
+ onChange={({ page: newPage, pageSize: newPageSize }) => {
173
+ if (newPageSize !== pageSize) {
174
+ setPage(1);
175
+ } else {
176
+ setPage(newPage);
177
+ }
178
+ setPageSize(newPageSize);
179
+ }}
180
+ />
181
+ </div>
182
+ )}
183
+ </>
184
+ )}
185
+ </div>
186
+ );
187
+ };
188
+
189
+ export default PrescriptionsTable;
@@ -0,0 +1,152 @@
1
+ @use '@carbon/layout';
2
+ @use '@carbon/type';
3
+ @use '@openmrs/esm-styleguide/src/vars' as *;
4
+
5
+ .link {
6
+ text-decoration: none;
7
+ }
8
+
9
+ .breadcrumbsSlot {
10
+ grid-row: 1 / 2;
11
+ grid-column: 1 / 2;
12
+ }
13
+
14
+ .prescriptionTabsContainer {
15
+ height: 100%;
16
+ width: 100%;
17
+
18
+ :global(.cds--tab-content) {
19
+ padding: 0 !important;
20
+ }
21
+ }
22
+
23
+ .newListButton {
24
+ width: fit-content;
25
+ justify-self: end;
26
+ align-self: center;
27
+ }
28
+
29
+ .tabsContainer {
30
+ background-color: $ui-02;
31
+ padding: 0 layout.$spacing-05;
32
+
33
+ :global(.cds--tabs__nav-item--selected) {
34
+ box-shadow: inset 0 layout.$spacing-01 0 0 var(--brand-03) !important;
35
+ }
36
+
37
+ :global(.cds--tab--list) button {
38
+ max-width: 12rem !important;
39
+ }
40
+ }
41
+
42
+ .patientListTableContainer {
43
+ grid-row: 3 / 4;
44
+ grid-column: 1 / 2;
45
+ height: 100%;
46
+ margin: 0 layout.$spacing-05;
47
+ background-color: $ui-01;
48
+ border: 0.5px solid #e0e0e0;
49
+
50
+ :global(.cds--data-table-container) {
51
+ padding-top: 0 !important;
52
+ }
53
+
54
+ tbody > tr > :nth-child(2) {
55
+ white-space: nowrap;
56
+ }
57
+
58
+ :global(.cds--data-table td) {
59
+ height: unset !important;
60
+ }
61
+
62
+ :global(.cds--data-table--zebra) tbody tr[data-parent-row]:nth-child(4n + 1) td {
63
+ background-color: $ui-02;
64
+ border-bottom: 1px solid $ui-03;
65
+ border-top: 1px solid $ui-03;
66
+ }
67
+
68
+ :global(.cds--data-table--zebra) tbody tr[data-parent-row]:nth-child(4n + 3) td {
69
+ background-color: $ui-01;
70
+ border-bottom: 1px solid $ui-03;
71
+ }
72
+
73
+ :global(.cds--pagination) {
74
+ border-top: none !important;
75
+ }
76
+
77
+ :global(.cds--table-expand__button) {
78
+ height: layout.$spacing-07 !important;
79
+ }
80
+ }
81
+
82
+ .searchContainer {
83
+ display: flex;
84
+ align-items: center;
85
+ justify-content: flex-end;
86
+ gap: layout.$spacing-05;
87
+ padding: layout.$spacing-05 layout.$spacing-05 0;
88
+
89
+ :global(.cds--search-magnifier-icon) {
90
+ z-index: 0 !important;
91
+ }
92
+
93
+ input {
94
+ background-color: #fff;
95
+ }
96
+ }
97
+
98
+ .addPrescriptionBtn {
99
+ width: layout.$spacing-13 !important;
100
+ padding: layout.$spacing-05 !important;
101
+ margin-left: layout.$spacing-05;
102
+ margin-right: layout.$spacing-05;
103
+ }
104
+
105
+ .patientSearch {
106
+ width: 25rem;
107
+ border-bottom-color: $ui-03;
108
+ }
109
+
110
+ .locationFilter {
111
+ width: 25rem;
112
+ }
113
+
114
+ .search {
115
+ width: 100%;
116
+ max-width: 16rem;
117
+ background-color: $ui-02;
118
+ border-bottom-color: $ui-03;
119
+ }
120
+
121
+ .hiddenRow {
122
+ display: none;
123
+ }
124
+
125
+ .filterEmptyState {
126
+ display: flex;
127
+ justify-content: center;
128
+ align-items: center;
129
+ padding: layout.$spacing-09;
130
+ text-align: center;
131
+ background-color: $ui-02;
132
+ }
133
+
134
+ .filterEmptyStateTile {
135
+ margin: auto;
136
+ background-color: $ui-01;
137
+ }
138
+
139
+ .filterEmptyStateContent {
140
+ @include type.type-style('heading-compact-02');
141
+ color: $text-02;
142
+ margin-bottom: layout.$spacing-03;
143
+ }
144
+
145
+ .filterEmptyStateHelper {
146
+ @include type.type-style('body-compact-01');
147
+ color: $text-02;
148
+ }
149
+
150
+ .paginationContainer {
151
+ width: 100%;
152
+ }
@@ -0,0 +1,30 @@
1
+ import React, { useCallback } from 'react';
2
+ import { Button } from '@carbon/react';
3
+ import { Printer } from '@carbon/react/icons';
4
+ import { useTranslation } from 'react-i18next';
5
+ import { showModal } from '@openmrs/esm-framework';
6
+
7
+ type PrescriptionPrintActionProps = {
8
+ encounterUuid: string;
9
+ patientUuid: string;
10
+ };
11
+
12
+ const PrescriptionPrintAction: React.FC<PrescriptionPrintActionProps> = ({ encounterUuid, patientUuid }) => {
13
+ const { t } = useTranslation();
14
+
15
+ const handleClick = useCallback(() => {
16
+ const dispose = showModal('prescription-print-preview-modal', {
17
+ onClose: () => dispose(),
18
+ encounterUuid,
19
+ patientUuid,
20
+ });
21
+ }, [encounterUuid, patientUuid]);
22
+
23
+ return (
24
+ <Button renderIcon={Printer} iconDescription={t('print', 'Print')} onClick={handleClick} kind="ghost">
25
+ {t('printPrescriptions', 'Print prescriptions')}
26
+ </Button>
27
+ );
28
+ };
29
+
30
+ export default PrescriptionPrintAction;
@@ -0,0 +1,92 @@
1
+ import React, { useRef, useState } from 'react';
2
+ import { useReactToPrint } from 'react-to-print';
3
+ import { useTranslation } from 'react-i18next';
4
+ import {
5
+ Button,
6
+ ButtonSet,
7
+ InlineLoading,
8
+ InlineNotification,
9
+ ModalBody,
10
+ ModalFooter,
11
+ ModalHeader,
12
+ } from '@carbon/react';
13
+ import { ErrorState, getCoreTranslation } from '@openmrs/esm-framework';
14
+ import { usePrescriptionDetails } from '../medication-request/medication-request.resource';
15
+ import PrescriptionsPrintout from './prescription-printout.component';
16
+ import PrintablePrescriptionsSelector from './printable-prescriptions.component';
17
+ import styles from './print-prescription.scss';
18
+
19
+ type PrescriptionPrintPreviewModalProps = {
20
+ onClose: () => void;
21
+ encounterUuid: string;
22
+ patientUuid: string;
23
+ status: string;
24
+ };
25
+
26
+ const PrescriptionPrintPreviewModal: React.FC<PrescriptionPrintPreviewModalProps> = ({ onClose, encounterUuid }) => {
27
+ const { t } = useTranslation();
28
+ const { medicationRequestBundles, error, isLoading } = usePrescriptionDetails(encounterUuid);
29
+
30
+ const [excludedPrescriptions, setExcludedPrescriptions] = useState<string[]>([]);
31
+ const [printError, setPrintError] = useState<string | null>(null);
32
+ const componentRef = useRef<HTMLDivElement>(null);
33
+
34
+ const handlePrint = useReactToPrint({
35
+ content: () => componentRef.current,
36
+ onBeforeGetContent: () => {
37
+ setPrintError(null);
38
+ },
39
+ onPrintError: (error) => {
40
+ setPrintError(t('printError', 'An error occurred while printing. Please try again.'));
41
+ },
42
+ copyStyles: true,
43
+ });
44
+
45
+ return (
46
+ <>
47
+ <ModalHeader closeModal={onClose} className={styles.title}>
48
+ {t('printPrescriptions', 'Print prescriptions')}
49
+ </ModalHeader>
50
+ <ModalBody>
51
+ {isLoading && (
52
+ <InlineLoading
53
+ status="active"
54
+ iconDescription="Loading"
55
+ description={t('loading', 'Loading prescriptions') + '....'}
56
+ />
57
+ )}
58
+ {error && <ErrorState error={error} headerTitle={t('error', 'Error')} />}
59
+ {!isLoading && medicationRequestBundles?.length > 0 && (
60
+ <div className={styles.printoutSelectorRow}>
61
+ <PrintablePrescriptionsSelector
62
+ medicationRequests={medicationRequestBundles}
63
+ excludedPrescription={excludedPrescriptions}
64
+ onExcludedPrescriptionChange={setExcludedPrescriptions}
65
+ />
66
+ <div ref={componentRef}>
67
+ <PrescriptionsPrintout
68
+ excludedPrescription={excludedPrescriptions}
69
+ medicationRequests={medicationRequestBundles}
70
+ />
71
+ </div>
72
+ </div>
73
+ )}
74
+ {printError && (
75
+ <InlineNotification kind="error" title={t('printErrorTitle', 'Print Error')} subtitle={printError} />
76
+ )}
77
+ </ModalBody>
78
+ <ModalFooter>
79
+ <ButtonSet className={styles.btnSet}>
80
+ <Button kind="secondary" onClick={onClose} type="button">
81
+ {getCoreTranslation('cancel', 'Cancel')}
82
+ </Button>
83
+ <Button kind="primary" onClick={handlePrint} type="button" disabled={isLoading}>
84
+ {t('print', 'Print')}
85
+ </Button>
86
+ </ButtonSet>
87
+ </ModalFooter>
88
+ </>
89
+ );
90
+ };
91
+
92
+ export default PrescriptionPrintPreviewModal;