@openmrs/esm-dispensing-app 1.9.2-pre.883 → 1.9.2-pre.889
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.
- package/.turbo/cache/adf051de4b68cd85-meta.json +1 -0
- package/.turbo/cache/adf051de4b68cd85.tar.zst +0 -0
- package/.turbo/turbo-build.log +8 -8
- package/dist/1119.js +1 -1
- package/dist/1197.js +1 -1
- package/dist/1282.js +1 -0
- package/dist/1282.js.map +1 -0
- package/dist/2146.js +1 -1
- package/dist/2690.js +1 -1
- package/dist/3099.js +1 -1
- package/dist/3584.js +1 -1
- package/dist/4055.js +1 -1
- package/dist/4132.js +1 -1
- package/dist/4300.js +1 -1
- package/dist/4335.js +1 -1
- package/dist/4618.js +1 -1
- package/dist/4652.js +1 -1
- package/dist/4944.js +1 -1
- package/dist/5173.js +1 -1
- package/dist/5241.js +1 -1
- package/dist/5442.js +1 -1
- package/dist/5661.js +1 -1
- package/dist/6022.js +1 -1
- package/dist/6468.js +1 -1
- package/dist/6679.js +1 -1
- package/dist/6840.js +1 -1
- package/dist/6859.js +1 -1
- package/dist/7097.js +1 -1
- package/dist/7159.js +1 -1
- package/dist/723.js +1 -1
- package/dist/7617.js +1 -1
- package/dist/795.js +1 -1
- package/dist/8163.js +1 -1
- package/dist/8349.js +1 -1
- package/dist/8618.js +1 -1
- package/dist/890.js +1 -1
- package/dist/9214.js +1 -1
- package/dist/9538.js +1 -1
- package/dist/9569.js +1 -1
- package/dist/986.js +1 -1
- package/dist/9879.js +1 -1
- package/dist/9895.js +1 -1
- package/dist/9900.js +1 -1
- package/dist/9913.js +1 -1
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/openmrs-esm-dispensing-app.js +1 -1
- package/dist/openmrs-esm-dispensing-app.js.buildmanifest.json +144 -144
- package/dist/routes.json +1 -1
- package/e2e/commands/encounter-operations.ts +1 -4
- package/e2e/specs/close-prescription.spec.ts +5 -0
- package/e2e/specs/dispense-medication.spec.ts +4 -0
- package/e2e/specs/pause-prescription.spec.ts +5 -0
- package/package.json +1 -1
- package/src/medication-request/medication-request.resource.test.tsx +3 -3
- package/src/medication-request/medication-request.resource.tsx +12 -9
- package/src/prescriptions/patient-search-tab-panel.component.tsx +58 -0
- package/src/prescriptions/patient-search-tab-panel.scss +26 -0
- package/src/prescriptions/prescription-tab-lists.component.tsx +18 -84
- package/src/prescriptions/prescription-tab-panel.component.tsx +45 -148
- package/src/prescriptions/prescriptions-table.component.tsx +164 -0
- package/translations/am.json +3 -0
- package/translations/ar.json +3 -0
- package/translations/ar_SY.json +3 -0
- package/translations/bn.json +3 -0
- package/translations/de.json +3 -0
- package/translations/en.json +3 -0
- package/translations/en_US.json +3 -0
- package/translations/es.json +3 -0
- package/translations/es_MX.json +3 -0
- package/translations/fr.json +3 -0
- package/translations/he.json +3 -0
- package/translations/hi.json +3 -0
- package/translations/hi_IN.json +3 -0
- package/translations/id.json +3 -0
- package/translations/it.json +3 -0
- package/translations/ka.json +3 -0
- package/translations/km.json +3 -0
- package/translations/ku.json +3 -0
- package/translations/ky.json +3 -0
- package/translations/lg.json +3 -0
- package/translations/ne.json +3 -0
- package/translations/pl.json +3 -0
- package/translations/pt.json +3 -0
- package/translations/pt_BR.json +3 -0
- package/translations/qu.json +3 -0
- package/translations/ro_RO.json +3 -0
- package/translations/ru_RU.json +3 -0
- package/translations/si.json +3 -0
- package/translations/sw.json +3 -0
- package/translations/sw_KE.json +3 -0
- package/translations/tr.json +3 -0
- package/translations/tr_TR.json +3 -0
- package/translations/uk.json +3 -0
- package/translations/uz.json +3 -0
- package/translations/uz@Latn.json +3 -0
- package/translations/uz_UZ.json +3 -0
- package/translations/vi.json +3 -0
- package/translations/zh.json +3 -0
- package/translations/zh_CN.json +3 -0
- package/.turbo/cache/aa05468322fdf99e-meta.json +0 -1
- package/.turbo/cache/aa05468322fdf99e.tar.zst +0 -0
- package/dist/6411.js +0 -1
- package/dist/6411.js.map +0 -1
package/dist/routes.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"$schema":"https://json.openmrs.org/routes.schema.json","backendDependencies":{"fhir2":">=1.2","webservices.rest":"^2.2.0"},"pages":[{"component":"dispensing","route":"dispensing","online":true,"offline":true}],"extensions":[{"name":"dispensing-link","slot":"app-menu-slot","component":"dispensingLink","online":true,"offline":true},{"name":"patient-diagnoses","component":"patientDiagnoses","slot":"dispensing-condition-and-diagnoses","online":true,"offline":true},{"name":"patient-conditions","component":"patientConditions","slot":"dispensing-condition-and-diagnoses","online":true,"offline":true},{"name":"dispensing-dashboard","slot":"dispensing-dashboard-slot","component":"dispensingDashboard","online":true,"offline":true},{"name":"dispensing-dashboard-link","component":"dispensingDashboardLink","meta":{"name":"dispensing","slot":"dispensing-dashboard-slot","title":"Dispensing"}},{"name":"dispense-action-button","slot":"prescription-action-button-slot","component":"dispenseActionButton"},{"name":"pause-action-button","slot":"prescription-action-button-slot","component":"pauseActionButton"},{"name":"close-action-button","slot":"prescription-action-button-slot","component":"closeActionButton"}],"workspaces":[{"name":"close-dispense-workspace","component":"closeDispenseWorkspace","type":"dispense","title":"Close prescription"},{"name":"pause-dispense-workspace","component":"pauseDispenseWorkspace","type":"dispense","title":"Pause prescription"},{"name":"dispense-workspace","component":"dispenseWorkspace","type":"dispense","title":"Dispense prescription","width":"wider"}],"modals":[{"name":"prescription-print-preview-modal","component":"printPrescriptionPreviewModal"},{"name":"delete-confirm-modal","component":"deleteConfirmModal"}],"version":"1.9.2-pre.
|
|
1
|
+
{"$schema":"https://json.openmrs.org/routes.schema.json","backendDependencies":{"fhir2":">=1.2","webservices.rest":"^2.2.0"},"pages":[{"component":"dispensing","route":"dispensing","online":true,"offline":true}],"extensions":[{"name":"dispensing-link","slot":"app-menu-slot","component":"dispensingLink","online":true,"offline":true},{"name":"patient-diagnoses","component":"patientDiagnoses","slot":"dispensing-condition-and-diagnoses","online":true,"offline":true},{"name":"patient-conditions","component":"patientConditions","slot":"dispensing-condition-and-diagnoses","online":true,"offline":true},{"name":"dispensing-dashboard","slot":"dispensing-dashboard-slot","component":"dispensingDashboard","online":true,"offline":true},{"name":"dispensing-dashboard-link","component":"dispensingDashboardLink","meta":{"name":"dispensing","slot":"dispensing-dashboard-slot","title":"Dispensing"}},{"name":"dispense-action-button","slot":"prescription-action-button-slot","component":"dispenseActionButton"},{"name":"pause-action-button","slot":"prescription-action-button-slot","component":"pauseActionButton"},{"name":"close-action-button","slot":"prescription-action-button-slot","component":"closeActionButton"}],"workspaces":[{"name":"close-dispense-workspace","component":"closeDispenseWorkspace","type":"dispense","title":"Close prescription"},{"name":"pause-dispense-workspace","component":"pauseDispenseWorkspace","type":"dispense","title":"Pause prescription"},{"name":"dispense-workspace","component":"dispenseWorkspace","type":"dispense","title":"Dispense prescription","width":"wider"}],"modals":[{"name":"prescription-print-preview-modal","component":"printPrescriptionPreviewModal"},{"name":"delete-confirm-modal","component":"deleteConfirmModal"}],"version":"1.9.2-pre.889"}
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import { type APIRequestContext, expect } from '@playwright/test';
|
|
3
3
|
import { type Encounter } from './types';
|
|
4
4
|
import { type Visit } from '@openmrs/esm-framework';
|
|
5
|
-
import dayjs from 'dayjs';
|
|
6
5
|
|
|
7
6
|
export interface Observation {
|
|
8
7
|
uuid: string;
|
|
@@ -36,9 +35,7 @@ export const createEncounter = async (
|
|
|
36
35
|
providerId: string,
|
|
37
36
|
visit: Visit,
|
|
38
37
|
): Promise<Encounter> => {
|
|
39
|
-
const
|
|
40
|
-
const now = dayjs().subtract(1, 'second');
|
|
41
|
-
const encounterDatetime = encounterAfterVisit.isBefore(now) ? encounterAfterVisit.format() : now.format();
|
|
38
|
+
const encounterDatetime = visit.startDatetime;
|
|
42
39
|
const encounterRes = await api.post('encounter', {
|
|
43
40
|
data: {
|
|
44
41
|
encounterDatetime,
|
|
@@ -32,6 +32,11 @@ test('Close prescription', async ({ page, patient }) => {
|
|
|
32
32
|
await dispensingPage.goTo();
|
|
33
33
|
});
|
|
34
34
|
|
|
35
|
+
await test.step('And I click on the Active prescriptions tab', async () => {
|
|
36
|
+
await page.getByRole('tab', { name: 'Active prescriptions' }).click();
|
|
37
|
+
await expect(page.getByRole('tab', { name: 'Active prescriptions' })).toHaveAttribute('aria-selected', 'true');
|
|
38
|
+
});
|
|
39
|
+
|
|
35
40
|
await test.step('And I expand a table row in the prescriptions table corresponding to an active prescription', async () => {
|
|
36
41
|
const rowText = new RegExp(`Expand current row`);
|
|
37
42
|
await page.getByRole('row', { name: rowText }).getByLabel('Expand current row').nth(0).click();
|
|
@@ -33,6 +33,10 @@ test('Dispense prescription', async ({ page, patient }) => {
|
|
|
33
33
|
await expect(page).toHaveURL(process.env.E2E_BASE_URL + `/spa/dispensing`);
|
|
34
34
|
});
|
|
35
35
|
|
|
36
|
+
await test.step('And I click on the Active prescriptions tab', async () => {
|
|
37
|
+
await page.getByRole('tab', { name: 'Active prescriptions' }).click();
|
|
38
|
+
});
|
|
39
|
+
|
|
36
40
|
await test.step('And I expand a table row in the Prescriptions table corresponding to an active prescription', async () => {
|
|
37
41
|
const rowText = new RegExp(`Expand current row`);
|
|
38
42
|
await page.getByRole('row', { name: rowText }).getByLabel('Expand current row').nth(0).click();
|
|
@@ -33,6 +33,11 @@ test('Pause prescription', async ({ page, patient }) => {
|
|
|
33
33
|
await dispensingPage.goTo();
|
|
34
34
|
});
|
|
35
35
|
|
|
36
|
+
await test.step('And I click on the Active prescriptions tab', async () => {
|
|
37
|
+
await page.getByRole('tab', { name: 'Active prescriptions' }).click();
|
|
38
|
+
await expect(page.getByRole('tab', { name: 'Active prescriptions' })).toHaveAttribute('aria-selected', 'true');
|
|
39
|
+
});
|
|
40
|
+
|
|
36
41
|
await test.step('And I expand a table row in the prescriptions table corresponding to an active prescription', async () => {
|
|
37
42
|
const rowText = new RegExp(`Expand current row`);
|
|
38
43
|
await page.getByRole('row', { name: rowText }).getByLabel('Expand current row').nth(0).click();
|
package/package.json
CHANGED
|
@@ -18,7 +18,7 @@ describe('Medication Request Resource Test', () => {
|
|
|
18
18
|
test('usePrescriptionsTable should call active endpoint and proper date based on expiration period if status parameter is active', () => {
|
|
19
19
|
// @ts-ignore
|
|
20
20
|
useSWR.mockImplementation(() => ({ data: { data: 'mockedReturnData' } }));
|
|
21
|
-
usePrescriptionsTable(5, 5, 'bob', null, 'ACTIVE', 10, 10000);
|
|
21
|
+
usePrescriptionsTable(true, 5, 5, 'bob', null, 'ACTIVE', 10, 10000);
|
|
22
22
|
expect(useSWR).toHaveBeenCalledWith(
|
|
23
23
|
`/ws/fhir2/R4/Encounter?_query=encountersWithMedicationRequests&_getpagesoffset=5&_count=5&date=ge${dayjs()
|
|
24
24
|
.startOf('day')
|
|
@@ -32,7 +32,7 @@ describe('Medication Request Resource Test', () => {
|
|
|
32
32
|
test('usePrescriptionsTable should call all endpoint if status parameter is not active', () => {
|
|
33
33
|
// @ts-ignore
|
|
34
34
|
useSWR.mockImplementation(() => ({ data: { data: 'mockedReturnData' } }));
|
|
35
|
-
usePrescriptionsTable(5, 5, 'bob', null, null, 10, 10000);
|
|
35
|
+
usePrescriptionsTable(true, 5, 5, 'bob', null, null, 10, 10000);
|
|
36
36
|
expect(useSWR).toHaveBeenCalledWith(
|
|
37
37
|
`/ws/fhir2/R4/Encounter?_query=encountersWithMedicationRequests&_getpagesoffset=5&_count=5&patientSearchTerm=bob`,
|
|
38
38
|
openmrsFetch,
|
|
@@ -587,7 +587,7 @@ describe('Medication Request Resource Test', () => {
|
|
|
587
587
|
|
|
588
588
|
// @ts-ignore
|
|
589
589
|
useSWR.mockImplementation(() => ({ data: { data: queryResultsBundle } }));
|
|
590
|
-
const { prescriptionsTableRows, totalOrders } = usePrescriptionsTable(2, 0, 'bob', 'ACTIVE', null, 90, 10000);
|
|
590
|
+
const { prescriptionsTableRows, totalOrders } = usePrescriptionsTable(true, 2, 0, 'bob', 'ACTIVE', null, 90, 10000);
|
|
591
591
|
expect(totalOrders).toBe(26);
|
|
592
592
|
expect(prescriptionsTableRows.length).toBe(2);
|
|
593
593
|
expect(prescriptionsTableRows[0].id).toBe('7aee7123-9e50-4f72-a636-895d77a63e98');
|
|
@@ -25,6 +25,7 @@ import dayjs from 'dayjs';
|
|
|
25
25
|
import { JSON_MERGE_PATH_MIME_TYPE, OPENMRS_FHIR_EXT_REQUEST_FULFILLER_STATUS } from '../constants';
|
|
26
26
|
|
|
27
27
|
export function usePrescriptionsTable(
|
|
28
|
+
loadData: boolean,
|
|
28
29
|
pageSize: number = 10,
|
|
29
30
|
pageOffset: number = 0,
|
|
30
31
|
patientSearchTerm: string = '',
|
|
@@ -34,15 +35,17 @@ export function usePrescriptionsTable(
|
|
|
34
35
|
refreshInterval: number,
|
|
35
36
|
) {
|
|
36
37
|
const { data, error } = useSWR<{ data: EncounterResponse }, Error>(
|
|
37
|
-
|
|
38
|
-
?
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
38
|
+
loadData
|
|
39
|
+
? status === 'ACTIVE'
|
|
40
|
+
? getPrescriptionTableActiveMedicationRequestsEndpoint(
|
|
41
|
+
pageOffset,
|
|
42
|
+
pageSize,
|
|
43
|
+
dayjs(new Date()).startOf('day').subtract(medicationRequestExpirationPeriodInDays, 'day').toISOString(),
|
|
44
|
+
patientSearchTerm,
|
|
45
|
+
location,
|
|
46
|
+
)
|
|
47
|
+
: getPrescriptionTableAllMedicationRequestsEndpoint(pageOffset, pageSize, patientSearchTerm, location)
|
|
48
|
+
: null,
|
|
46
49
|
openmrsFetch,
|
|
47
50
|
{ refreshInterval: refreshInterval },
|
|
48
51
|
);
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { Button, Search, TabPanel } from '@carbon/react';
|
|
3
|
+
import { useTranslation } from 'react-i18next';
|
|
4
|
+
import { PatientSearchPictogram } from '@openmrs/esm-framework';
|
|
5
|
+
import PrescriptionsTable from './prescriptions-table.component';
|
|
6
|
+
import styles from './patient-search-tab-panel.scss';
|
|
7
|
+
|
|
8
|
+
const PatientSearchTabPanel: React.FC = () => {
|
|
9
|
+
const { t } = useTranslation();
|
|
10
|
+
const [searchTerm, setSearchTerm] = useState('');
|
|
11
|
+
const [submittedSearchTerm, setSubmittedSearchTerm] = useState('');
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<TabPanel>
|
|
15
|
+
<div className={styles.searchTabPanel}>
|
|
16
|
+
<form
|
|
17
|
+
className={styles.searchBar}
|
|
18
|
+
onSubmit={(e) => {
|
|
19
|
+
e.preventDefault();
|
|
20
|
+
setSubmittedSearchTerm(searchTerm);
|
|
21
|
+
}}>
|
|
22
|
+
<Search
|
|
23
|
+
closeButtonLabelText={t('clearSearchInput', 'Clear search input')}
|
|
24
|
+
defaultValue={searchTerm}
|
|
25
|
+
placeholder={t('searchForPatient', 'Search for a patient by name or identifier number')}
|
|
26
|
+
labelText={t('searchForPatient', 'Search for a patient by name or identifier number')}
|
|
27
|
+
onChange={(e) => {
|
|
28
|
+
setSearchTerm(e.target.value);
|
|
29
|
+
}}
|
|
30
|
+
onClear={() => setSubmittedSearchTerm('')}
|
|
31
|
+
size="lg"
|
|
32
|
+
/>
|
|
33
|
+
<Button kind="secondary" type="submit">
|
|
34
|
+
{t('search', 'Search')}
|
|
35
|
+
</Button>
|
|
36
|
+
</form>
|
|
37
|
+
{submittedSearchTerm ? (
|
|
38
|
+
<PrescriptionsTable
|
|
39
|
+
loadData={true}
|
|
40
|
+
status={'ACTIVE'}
|
|
41
|
+
debouncedSearchTerm={submittedSearchTerm}
|
|
42
|
+
location={''}
|
|
43
|
+
/>
|
|
44
|
+
) : (
|
|
45
|
+
<div className={styles.searchForPatientPlaceholder}>
|
|
46
|
+
<div>
|
|
47
|
+
<PatientSearchPictogram />
|
|
48
|
+
<h5>{t('searchForPatientHeader', 'Search for a patient')}</h5>
|
|
49
|
+
<div>{t('searchForPatient', 'Search for a patient by name or identifier number')}</div>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
)}
|
|
53
|
+
</div>
|
|
54
|
+
</TabPanel>
|
|
55
|
+
);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export default PatientSearchTabPanel;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
@use '@carbon/layout';
|
|
2
|
+
@use '~@openmrs/esm-styleguide/src/vars' as *;
|
|
3
|
+
|
|
4
|
+
.searchTabPanel {
|
|
5
|
+
display: flex;
|
|
6
|
+
flex-direction: column;
|
|
7
|
+
margin: layout.$spacing-05 layout.$spacing-04;
|
|
8
|
+
gap: layout.$spacing-05;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.searchForPatientPlaceholder {
|
|
12
|
+
display: flex;
|
|
13
|
+
justify-content: center;
|
|
14
|
+
align-items: center;
|
|
15
|
+
text-align: center;
|
|
16
|
+
min-height: 400px;
|
|
17
|
+
flex: 1;
|
|
18
|
+
border: solid 1px $grey-2;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.searchBar {
|
|
22
|
+
input {
|
|
23
|
+
background-color: $ui-02;
|
|
24
|
+
}
|
|
25
|
+
display: flex;
|
|
26
|
+
}
|
|
@@ -1,45 +1,14 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
import {
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { Tab, Tabs, TabList, TabPanels } from '@carbon/react';
|
|
3
3
|
import { useTranslation } from 'react-i18next';
|
|
4
|
-
import { useConfig } from '@openmrs/esm-framework';
|
|
5
|
-
import { useLocationForFiltering } from '../location/location.resource';
|
|
6
|
-
import { type SimpleLocation } from '../types';
|
|
7
|
-
import { type PharmacyConfig } from '../config-schema';
|
|
8
4
|
import PrescriptionTabPanel from './prescription-tab-panel.component';
|
|
9
5
|
import styles from './prescriptions.scss';
|
|
6
|
+
import PatientSearchTabPanel from './patient-search-tab-panel.component';
|
|
10
7
|
|
|
11
8
|
const PrescriptionTabLists: React.FC = () => {
|
|
12
9
|
const { t } = useTranslation();
|
|
13
|
-
const config = useConfig<PharmacyConfig>();
|
|
14
|
-
const { filterLocations, isLoading: isFilterLocationsLoading } = useLocationForFiltering(config);
|
|
15
|
-
const [searchTermUserInput, setSearchTermUserInput] = useState(''); // we have a separate "searchTermUserInput" and "searchTerm" in order to debounce
|
|
16
|
-
const [searchTerm, setSearchTerm] = useState('');
|
|
17
|
-
const [location, setLocation] = useState('');
|
|
18
10
|
const [selectedTab, setSelectedTab] = useState(0);
|
|
19
11
|
|
|
20
|
-
const tabs = [
|
|
21
|
-
{
|
|
22
|
-
key: 'activePrescriptions',
|
|
23
|
-
header: t('activePrescriptions', 'Active Prescriptions'),
|
|
24
|
-
status: 'ACTIVE',
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
key: 'allPrescriptions',
|
|
28
|
-
header: t('allPrescriptions', 'All Prescriptions'),
|
|
29
|
-
status: '',
|
|
30
|
-
},
|
|
31
|
-
];
|
|
32
|
-
|
|
33
|
-
// debounce: delay the search term update so that a search isn't triggered on every single keystroke
|
|
34
|
-
useEffect(() => {
|
|
35
|
-
const debounceFn = setTimeout(() => {
|
|
36
|
-
setSearchTerm(searchTermUserInput);
|
|
37
|
-
}, 500);
|
|
38
|
-
|
|
39
|
-
return () => clearTimeout(debounceFn);
|
|
40
|
-
}, [searchTermUserInput]);
|
|
41
|
-
|
|
42
|
-
// we use this to only render the tab panel that is currently selected, see O3-2777
|
|
43
12
|
const handleTabChange = (event) => {
|
|
44
13
|
setSelectedTab(event.selectedIndex);
|
|
45
14
|
};
|
|
@@ -49,58 +18,23 @@ const PrescriptionTabLists: React.FC = () => {
|
|
|
49
18
|
<section className={styles.prescriptionTabsContainer}>
|
|
50
19
|
<Tabs onChange={handleTabChange}>
|
|
51
20
|
<TabList aria-label={t('tabList', 'Tab List')} contained className={styles.tabsContainer}>
|
|
52
|
-
{
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
21
|
+
<Tab title={t('search', 'Search')} id={'tab-search'} className={styles.tab}>
|
|
22
|
+
{t('search', 'Search')}
|
|
23
|
+
</Tab>
|
|
24
|
+
<Tab
|
|
25
|
+
title={t('activePrescriptions', 'Active Prescriptions')}
|
|
26
|
+
id={'tab-active-prescription'}
|
|
27
|
+
className={styles.tab}>
|
|
28
|
+
{t('activePrescriptions', 'Active Prescriptions')}
|
|
29
|
+
</Tab>
|
|
30
|
+
<Tab title={t('allPrescriptions', 'All Prescriptions')} id={'tab-all-prescription'} className={styles.tab}>
|
|
31
|
+
{t('allPrescriptions', 'All Prescriptions')}
|
|
32
|
+
</Tab>
|
|
59
33
|
</TabList>
|
|
60
|
-
<div className={styles.searchContainer}>
|
|
61
|
-
{/* <Button
|
|
62
|
-
kind="primary"
|
|
63
|
-
renderIcon={(props) => <Add size={24} />}
|
|
64
|
-
className={styles.addPrescriptionBtn}
|
|
65
|
-
size="sm"
|
|
66
|
-
>
|
|
67
|
-
{t("fillPrescription", "Fill prescription")}
|
|
68
|
-
</Button>*/}
|
|
69
|
-
<Search
|
|
70
|
-
closeButtonLabelText={t('clearSearchInput', 'Clear search input')}
|
|
71
|
-
defaultValue={searchTermUserInput}
|
|
72
|
-
placeholder={t('searchByPatientIdOrName', 'Search by patient ID or name')}
|
|
73
|
-
labelText={t('searchByPatientIdOrName', 'Search by patient ID or name')}
|
|
74
|
-
onChange={(e) => {
|
|
75
|
-
e.preventDefault();
|
|
76
|
-
setSearchTermUserInput(e.target.value);
|
|
77
|
-
}}
|
|
78
|
-
size="md"
|
|
79
|
-
className={styles.patientSearch}
|
|
80
|
-
/>
|
|
81
|
-
{config.locationBehavior?.locationFilter?.enabled &&
|
|
82
|
-
!isFilterLocationsLoading &&
|
|
83
|
-
filterLocations?.length > 1 && (
|
|
84
|
-
<ComboBox
|
|
85
|
-
id="locationFilter"
|
|
86
|
-
placeholder={t('filterByLocation', 'Filter by location')}
|
|
87
|
-
items={isFilterLocationsLoading ? [] : filterLocations}
|
|
88
|
-
itemToString={(item: SimpleLocation) => item?.name}
|
|
89
|
-
onChange={({ selectedItem }) => {
|
|
90
|
-
setLocation(selectedItem?.id);
|
|
91
|
-
}}
|
|
92
|
-
className={styles.locationFilter}
|
|
93
|
-
/>
|
|
94
|
-
)}
|
|
95
|
-
</div>
|
|
96
34
|
<TabPanels>
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
) : (
|
|
101
|
-
<></>
|
|
102
|
-
);
|
|
103
|
-
})}
|
|
35
|
+
<PatientSearchTabPanel />
|
|
36
|
+
<PrescriptionTabPanel isTabActive={selectedTab === 1} status={'ACTIVE'} />
|
|
37
|
+
<PrescriptionTabPanel isTabActive={selectedTab === 2} status={''} />
|
|
104
38
|
</TabPanels>
|
|
105
39
|
</Tabs>
|
|
106
40
|
</section>
|
|
@@ -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
|
|
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> = ({
|
|
16
|
+
const PrescriptionTabPanel: React.FC<PrescriptionTabPanelProps> = ({ status, isTabActive }) => {
|
|
35
17
|
const { t } = useTranslation();
|
|
36
18
|
const config = useConfig<PharmacyConfig>();
|
|
37
|
-
const
|
|
38
|
-
const [
|
|
39
|
-
const
|
|
40
|
-
const
|
|
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.
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
};
|