@openmrs/esm-dispensing-app 1.9.2-pre.883 → 1.9.2-pre.885

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.
@@ -1,165 +1,62 @@
1
- import {
2
- DataTable,
3
- DataTableSkeleton,
4
- Layer,
5
- Pagination,
6
- Table,
7
- TableBody,
8
- TableCell,
9
- TableContainer,
10
- TableExpandedRow,
11
- TableExpandHeader,
12
- TableExpandRow,
13
- TableHead,
14
- TableHeader,
15
- TableRow,
16
- TabPanel,
17
- Tile,
18
- } from '@carbon/react';
19
- import { formatDatetime, parseDate, useConfig } from '@openmrs/esm-framework';
20
- import React, { useEffect, useState } from 'react';
1
+ import React, { useState } from 'react';
21
2
  import { useTranslation } from 'react-i18next';
3
+ import { ComboBox, Search, TabPanel } from '@carbon/react';
4
+ import { useConfig, useDebounce } from '@openmrs/esm-framework';
22
5
  import { type PharmacyConfig } from '../config-schema';
23
- import { usePrescriptionsTable } from '../medication-request/medication-request.resource';
24
- import PatientInfoCell from '../patient/patient-info-cell.component';
25
- import PrescriptionExpanded from './prescription-expanded.component';
6
+ import PrescriptionsTable from './prescriptions-table.component';
26
7
  import styles from './prescriptions.scss';
8
+ import { useLocationForFiltering } from '../location/location.resource';
9
+ import { type SimpleLocation } from '../types';
27
10
 
28
11
  interface PrescriptionTabPanelProps {
29
- searchTerm: string;
30
- location: string;
31
12
  status: string;
13
+ isTabActive: boolean;
32
14
  }
33
15
 
34
- const PrescriptionTabPanel: React.FC<PrescriptionTabPanelProps> = ({ searchTerm, location, status }) => {
16
+ const PrescriptionTabPanel: React.FC<PrescriptionTabPanelProps> = ({ status, isTabActive }) => {
35
17
  const { t } = useTranslation();
36
18
  const config = useConfig<PharmacyConfig>();
37
- const [page, setPage] = useState(1);
38
- const [pageSize, setPageSize] = useState(10);
39
- const [nextOffSet, setNextOffSet] = useState(0);
40
- const { prescriptionsTableRows, error, isLoading, totalOrders } = usePrescriptionsTable(
41
- pageSize,
42
- nextOffSet,
43
- searchTerm,
44
- location,
45
- status,
46
- config.medicationRequestExpirationPeriodInDays,
47
- config.refreshInterval,
48
- );
49
-
50
- // dynamic status keys we need to maintain
51
- // t('active', 'Active')
52
- // t('paused', 'Paused')
53
- // t('closed', 'Closed')
54
- // t('completed', 'Completed')
55
- // t('expired', 'Expired')
56
- // t('cancelled', 'Cancelled')
57
-
58
- let columns = [
59
- { header: t('created', 'Created'), key: 'created' },
60
- { header: t('patientName', 'Patient name'), key: 'patient' },
61
- { header: t('prescriber', 'Prescriber'), key: 'prescriber' },
62
- { header: t('drugs', 'Drugs'), key: 'drugs' },
63
- { header: t('lastDispenser', 'Last dispenser'), key: 'lastDispenser' },
64
- { header: t('status', 'Status'), key: 'status' },
65
- ];
66
-
67
- // add the locations column, if enabled
68
- if (config.locationBehavior?.locationColumn?.enabled) {
69
- columns = [...columns.slice(0, 3), { header: t('location', 'Location'), key: 'location' }, ...columns.slice(3)];
70
- }
71
-
72
- // reset back to page 1 whenever search term changes
73
- useEffect(() => {
74
- setPage(1);
75
- setNextOffSet(0);
76
- }, [searchTerm]);
19
+ const { filterLocations, isLoading: isFilterLocationsLoading } = useLocationForFiltering(config);
20
+ const [searchTerm, setSearchTerm] = useState('');
21
+ const debouncedSearchTerm = useDebounce(searchTerm, 500);
22
+ const [location, setLocation] = useState('');
77
23
 
78
24
  return (
79
25
  <TabPanel>
80
- <div className={styles.patientListTableContainer}>
81
- {isLoading && <DataTableSkeleton role="progressbar" />}
82
- {error && <p>Error</p>}
83
- {prescriptionsTableRows && (
84
- <>
85
- <DataTable rows={prescriptionsTableRows} headers={columns} isSortable>
86
- {({ rows, headers, getExpandHeaderProps, getHeaderProps, getRowProps, getTableProps }) => (
87
- <TableContainer>
88
- <Table {...getTableProps()} useZebraStyles>
89
- <TableHead>
90
- <TableRow>
91
- <TableExpandHeader {...getExpandHeaderProps()} />
92
- {headers.map((header) => (
93
- <TableHeader {...getHeaderProps({ header })}>{header.header}</TableHeader>
94
- ))}
95
- </TableRow>
96
- </TableHead>
97
- <TableBody>
98
- {rows.map((row) => (
99
- <React.Fragment key={row.id}>
100
- <TableExpandRow {...getRowProps({ row })}>
101
- {row.cells.map((cell) => (
102
- <TableCell key={cell.id}>
103
- {cell.id.endsWith('created') ? (
104
- formatDatetime(parseDate(cell.value))
105
- ) : cell.id.endsWith('patient') ? (
106
- <PatientInfoCell patient={cell.value} />
107
- ) : cell.id.endsWith('status') ? (
108
- t(cell.value)
109
- ) : (
110
- cell.value
111
- )}
112
- </TableCell>
113
- ))}
114
- </TableExpandRow>
115
- {row.isExpanded ? (
116
- <TableExpandedRow colSpan={headers.length + 1}>
117
- <PrescriptionExpanded
118
- encounterUuid={row.id}
119
- patientUuid={row.cells.find((cell) => cell.id.endsWith('patient')).value.uuid}
120
- status={row.cells.find((cell) => cell.id.endsWith('status')).value}
121
- />
122
- </TableExpandedRow>
123
- ) : (
124
- <TableExpandedRow className={styles.hiddenRow} colSpan={headers.length + 2} />
125
- )}
126
- </React.Fragment>
127
- ))}
128
- </TableBody>
129
- </Table>
130
- </TableContainer>
131
- )}
132
- </DataTable>
133
- {prescriptionsTableRows?.length === 0 && (
134
- <div className={styles.filterEmptyState}>
135
- <Layer>
136
- <Tile className={styles.filterEmptyStateTile}>
137
- <p className={styles.filterEmptyStateContent}>
138
- {t('noPrescriptionsToDisplay', 'No prescriptions to display')}
139
- </p>
140
- <p className={styles.filterEmptyStateHelper}>{t('checkFilters', 'Check the filters above')}</p>
141
- </Tile>
142
- </Layer>
143
- </div>
144
- )}
145
- {prescriptionsTableRows?.length > 0 && (
146
- <div style={{ width: '100%' }}>
147
- <Pagination
148
- page={page}
149
- pageSize={pageSize}
150
- pageSizes={[10, 20, 30, 40, 50, 100]}
151
- totalItems={totalOrders}
152
- onChange={({ page, pageSize }) => {
153
- setPage(page);
154
- setNextOffSet((page - 1) * pageSize);
155
- setPageSize(pageSize);
156
- }}
157
- />
158
- </div>
159
- )}
160
- </>
161
- )}
26
+ <div className={styles.searchContainer}>
27
+ <Search
28
+ closeButtonLabelText={t('clearSearchInput', 'Clear search input')}
29
+ defaultValue={searchTerm}
30
+ placeholder={t('searchByPatientIdOrName', 'Search by patient ID or name')}
31
+ labelText={t('searchByPatientIdOrName', 'Search by patient ID or name')}
32
+ onChange={(e) => {
33
+ e.preventDefault();
34
+ setSearchTerm(e.target.value);
35
+ }}
36
+ size="md"
37
+ className={styles.patientSearch}
38
+ />
39
+ {config.locationBehavior?.locationFilter?.enabled &&
40
+ !isFilterLocationsLoading &&
41
+ filterLocations?.length > 1 && (
42
+ <ComboBox
43
+ id="locationFilter"
44
+ placeholder={t('filterByLocation', 'Filter by location')}
45
+ items={isFilterLocationsLoading ? [] : filterLocations}
46
+ itemToString={(item: SimpleLocation) => item?.name}
47
+ onChange={({ selectedItem }) => {
48
+ setLocation(selectedItem?.id);
49
+ }}
50
+ className={styles.locationFilter}
51
+ />
52
+ )}
162
53
  </div>
54
+ <PrescriptionsTable
55
+ loadData={isTabActive}
56
+ status={status}
57
+ debouncedSearchTerm={debouncedSearchTerm}
58
+ location={location}
59
+ />
163
60
  </TabPanel>
164
61
  );
165
62
  };
@@ -0,0 +1,164 @@
1
+ import {
2
+ DataTable,
3
+ DataTableSkeleton,
4
+ Layer,
5
+ Pagination,
6
+ Table,
7
+ TableBody,
8
+ TableCell,
9
+ TableContainer,
10
+ TableExpandedRow,
11
+ TableExpandHeader,
12
+ TableExpandRow,
13
+ TableHead,
14
+ TableHeader,
15
+ TableRow,
16
+ Tile,
17
+ } from '@carbon/react';
18
+ import { formatDatetime, parseDate, useConfig } from '@openmrs/esm-framework';
19
+ import React, { useEffect, useState } from 'react';
20
+ import { useTranslation } from 'react-i18next';
21
+ import PatientInfoCell from '../patient/patient-info-cell.component';
22
+ import PrescriptionExpanded from './prescription-expanded.component';
23
+ import styles from './prescriptions.scss';
24
+ import { usePrescriptionsTable } from '../medication-request/medication-request.resource';
25
+ import { type PharmacyConfig } from '../config-schema';
26
+
27
+ interface PrescriptionsTableProps {
28
+ loadData: boolean;
29
+ debouncedSearchTerm: string;
30
+ location: string;
31
+ status: string;
32
+ }
33
+
34
+ const PrescriptionsTable: React.FC<PrescriptionsTableProps> = ({ loadData, debouncedSearchTerm, location, status }) => {
35
+ const { t } = useTranslation();
36
+ const config = useConfig<PharmacyConfig>();
37
+ const [page, setPage] = useState(1);
38
+ const [pageSize, setPageSize] = useState(10);
39
+ const nextOffSet = (page - 1) * pageSize;
40
+ const { prescriptionsTableRows, error, isLoading, totalOrders } = usePrescriptionsTable(
41
+ loadData,
42
+ pageSize,
43
+ nextOffSet,
44
+ debouncedSearchTerm,
45
+ location,
46
+ status,
47
+ config.medicationRequestExpirationPeriodInDays,
48
+ config.refreshInterval,
49
+ );
50
+
51
+ // reset back to page 1 whenever search term changes
52
+ useEffect(() => {
53
+ setPage(1);
54
+ }, [debouncedSearchTerm]);
55
+
56
+ // dynamic status keys we need to maintain
57
+ // t('active', 'Active')
58
+ // t('paused', 'Paused')
59
+ // t('closed', 'Closed')
60
+ // t('completed', 'Completed')
61
+ // t('expired', 'Expired')
62
+ // t('cancelled', 'Cancelled')
63
+
64
+ let columns = [
65
+ { header: t('created', 'Created'), key: 'created' },
66
+ { header: t('patientName', 'Patient name'), key: 'patient' },
67
+ { header: t('prescriber', 'Prescriber'), key: 'prescriber' },
68
+ { header: t('drugs', 'Drugs'), key: 'drugs' },
69
+ { header: t('lastDispenser', 'Last dispenser'), key: 'lastDispenser' },
70
+ { header: t('status', 'Status'), key: 'status' },
71
+ ];
72
+
73
+ // add the locations column, if enabled
74
+ if (config.locationBehavior?.locationColumn?.enabled) {
75
+ columns = [...columns.slice(0, 3), { header: t('location', 'Location'), key: 'location' }, ...columns.slice(3)];
76
+ }
77
+
78
+ return (
79
+ <div className={styles.patientListTableContainer}>
80
+ {isLoading && <DataTableSkeleton role="progressbar" />}
81
+ {error && <p>Error</p>}
82
+ {prescriptionsTableRows && (
83
+ <>
84
+ <DataTable rows={prescriptionsTableRows} headers={columns} isSortable>
85
+ {({ rows, headers, getExpandHeaderProps, getHeaderProps, getRowProps, getTableProps }) => (
86
+ <TableContainer>
87
+ <Table {...getTableProps()} useZebraStyles>
88
+ <TableHead>
89
+ <TableRow>
90
+ <TableExpandHeader {...getExpandHeaderProps()} />
91
+ {headers.map((header) => (
92
+ <TableHeader {...getHeaderProps({ header })}>{header.header}</TableHeader>
93
+ ))}
94
+ </TableRow>
95
+ </TableHead>
96
+ <TableBody>
97
+ {rows.map((row) => (
98
+ <React.Fragment key={row.id}>
99
+ <TableExpandRow {...getRowProps({ row })}>
100
+ {row.cells.map((cell) => (
101
+ <TableCell key={cell.id}>
102
+ {cell.id.endsWith('created') ? (
103
+ formatDatetime(parseDate(cell.value))
104
+ ) : cell.id.endsWith('patient') ? (
105
+ <PatientInfoCell patient={cell.value} />
106
+ ) : cell.id.endsWith('status') ? (
107
+ t(cell.value)
108
+ ) : (
109
+ cell.value
110
+ )}
111
+ </TableCell>
112
+ ))}
113
+ </TableExpandRow>
114
+ {row.isExpanded ? (
115
+ <TableExpandedRow colSpan={headers.length + 1}>
116
+ <PrescriptionExpanded
117
+ encounterUuid={row.id}
118
+ patientUuid={row.cells.find((cell) => cell.id.endsWith('patient')).value.uuid}
119
+ status={row.cells.find((cell) => cell.id.endsWith('status')).value}
120
+ />
121
+ </TableExpandedRow>
122
+ ) : (
123
+ <TableExpandedRow className={styles.hiddenRow} colSpan={headers.length + 2} />
124
+ )}
125
+ </React.Fragment>
126
+ ))}
127
+ </TableBody>
128
+ </Table>
129
+ </TableContainer>
130
+ )}
131
+ </DataTable>
132
+ {prescriptionsTableRows?.length === 0 && (
133
+ <div className={styles.filterEmptyState}>
134
+ <Layer>
135
+ <Tile className={styles.filterEmptyStateTile}>
136
+ <p className={styles.filterEmptyStateContent}>
137
+ {t('noPrescriptionsToDisplay', 'No prescriptions to display')}
138
+ </p>
139
+ <p className={styles.filterEmptyStateHelper}>{t('checkFilters', 'Check the filters above')}</p>
140
+ </Tile>
141
+ </Layer>
142
+ </div>
143
+ )}
144
+ {prescriptionsTableRows?.length > 0 && (
145
+ <div style={{ width: '100%' }}>
146
+ <Pagination
147
+ page={page}
148
+ pageSize={pageSize}
149
+ pageSizes={[10, 20, 30, 40, 50, 100]}
150
+ totalItems={totalOrders}
151
+ onChange={({ page, pageSize }) => {
152
+ setPage(page);
153
+ setPageSize(pageSize);
154
+ }}
155
+ />
156
+ </div>
157
+ )}
158
+ </>
159
+ )}
160
+ </div>
161
+ );
162
+ };
163
+
164
+ export default PrescriptionsTable;
@@ -96,7 +96,10 @@
96
96
  "reasonForPause": "Reason for pause",
97
97
  "refills": "Refills",
98
98
  "route": "Route",
99
+ "search": "Search",
99
100
  "searchByPatientIdOrName": "Search by patient ID or name",
101
+ "searchForPatient": "Search for a patient by name or identifier number",
102
+ "searchForPatientHeader": "Search for a patient",
100
103
  "selectPrescriptions": "Check prescriptions to print",
101
104
  "selectStockDispense": "Select stock to dispense from",
102
105
  "status": "Status",
@@ -1 +0,0 @@
1
- {"hash":"aa05468322fdf99e","duration":43765}