@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.
Files changed (104) hide show
  1. package/.turbo/cache/adf051de4b68cd85-meta.json +1 -0
  2. package/.turbo/cache/adf051de4b68cd85.tar.zst +0 -0
  3. package/.turbo/turbo-build.log +8 -8
  4. package/dist/1119.js +1 -1
  5. package/dist/1197.js +1 -1
  6. package/dist/1282.js +1 -0
  7. package/dist/1282.js.map +1 -0
  8. package/dist/2146.js +1 -1
  9. package/dist/2690.js +1 -1
  10. package/dist/3099.js +1 -1
  11. package/dist/3584.js +1 -1
  12. package/dist/4055.js +1 -1
  13. package/dist/4132.js +1 -1
  14. package/dist/4300.js +1 -1
  15. package/dist/4335.js +1 -1
  16. package/dist/4618.js +1 -1
  17. package/dist/4652.js +1 -1
  18. package/dist/4944.js +1 -1
  19. package/dist/5173.js +1 -1
  20. package/dist/5241.js +1 -1
  21. package/dist/5442.js +1 -1
  22. package/dist/5661.js +1 -1
  23. package/dist/6022.js +1 -1
  24. package/dist/6468.js +1 -1
  25. package/dist/6679.js +1 -1
  26. package/dist/6840.js +1 -1
  27. package/dist/6859.js +1 -1
  28. package/dist/7097.js +1 -1
  29. package/dist/7159.js +1 -1
  30. package/dist/723.js +1 -1
  31. package/dist/7617.js +1 -1
  32. package/dist/795.js +1 -1
  33. package/dist/8163.js +1 -1
  34. package/dist/8349.js +1 -1
  35. package/dist/8618.js +1 -1
  36. package/dist/890.js +1 -1
  37. package/dist/9214.js +1 -1
  38. package/dist/9538.js +1 -1
  39. package/dist/9569.js +1 -1
  40. package/dist/986.js +1 -1
  41. package/dist/9879.js +1 -1
  42. package/dist/9895.js +1 -1
  43. package/dist/9900.js +1 -1
  44. package/dist/9913.js +1 -1
  45. package/dist/main.js +1 -1
  46. package/dist/main.js.map +1 -1
  47. package/dist/openmrs-esm-dispensing-app.js +1 -1
  48. package/dist/openmrs-esm-dispensing-app.js.buildmanifest.json +144 -144
  49. package/dist/routes.json +1 -1
  50. package/e2e/commands/encounter-operations.ts +1 -4
  51. package/e2e/specs/close-prescription.spec.ts +5 -0
  52. package/e2e/specs/dispense-medication.spec.ts +4 -0
  53. package/e2e/specs/pause-prescription.spec.ts +5 -0
  54. package/package.json +1 -1
  55. package/src/medication-request/medication-request.resource.test.tsx +3 -3
  56. package/src/medication-request/medication-request.resource.tsx +12 -9
  57. package/src/prescriptions/patient-search-tab-panel.component.tsx +58 -0
  58. package/src/prescriptions/patient-search-tab-panel.scss +26 -0
  59. package/src/prescriptions/prescription-tab-lists.component.tsx +18 -84
  60. package/src/prescriptions/prescription-tab-panel.component.tsx +45 -148
  61. package/src/prescriptions/prescriptions-table.component.tsx +164 -0
  62. package/translations/am.json +3 -0
  63. package/translations/ar.json +3 -0
  64. package/translations/ar_SY.json +3 -0
  65. package/translations/bn.json +3 -0
  66. package/translations/de.json +3 -0
  67. package/translations/en.json +3 -0
  68. package/translations/en_US.json +3 -0
  69. package/translations/es.json +3 -0
  70. package/translations/es_MX.json +3 -0
  71. package/translations/fr.json +3 -0
  72. package/translations/he.json +3 -0
  73. package/translations/hi.json +3 -0
  74. package/translations/hi_IN.json +3 -0
  75. package/translations/id.json +3 -0
  76. package/translations/it.json +3 -0
  77. package/translations/ka.json +3 -0
  78. package/translations/km.json +3 -0
  79. package/translations/ku.json +3 -0
  80. package/translations/ky.json +3 -0
  81. package/translations/lg.json +3 -0
  82. package/translations/ne.json +3 -0
  83. package/translations/pl.json +3 -0
  84. package/translations/pt.json +3 -0
  85. package/translations/pt_BR.json +3 -0
  86. package/translations/qu.json +3 -0
  87. package/translations/ro_RO.json +3 -0
  88. package/translations/ru_RU.json +3 -0
  89. package/translations/si.json +3 -0
  90. package/translations/sw.json +3 -0
  91. package/translations/sw_KE.json +3 -0
  92. package/translations/tr.json +3 -0
  93. package/translations/tr_TR.json +3 -0
  94. package/translations/uk.json +3 -0
  95. package/translations/uz.json +3 -0
  96. package/translations/uz@Latn.json +3 -0
  97. package/translations/uz_UZ.json +3 -0
  98. package/translations/vi.json +3 -0
  99. package/translations/zh.json +3 -0
  100. package/translations/zh_CN.json +3 -0
  101. package/.turbo/cache/aa05468322fdf99e-meta.json +0 -1
  102. package/.turbo/cache/aa05468322fdf99e.tar.zst +0 -0
  103. package/dist/6411.js +0 -1
  104. 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.883"}
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 encounterAfterVisit = dayjs(visit.startDatetime).add(1, 'minute');
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openmrs/esm-dispensing-app",
3
- "version": "1.9.2-pre.883",
3
+ "version": "1.9.2-pre.889",
4
4
  "license": "MPL-2.0",
5
5
  "description": "Medication dispensing application",
6
6
  "browser": "dist/openmrs-esm-dispensing-app.js",
@@ -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
- status === 'ACTIVE'
38
- ? getPrescriptionTableActiveMedicationRequestsEndpoint(
39
- pageOffset,
40
- pageSize,
41
- dayjs(new Date()).startOf('day').subtract(medicationRequestExpirationPeriodInDays, 'day').toISOString(),
42
- patientSearchTerm,
43
- location,
44
- )
45
- : getPrescriptionTableAllMedicationRequestsEndpoint(pageOffset, pageSize, patientSearchTerm, location),
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, { useEffect, useState } from 'react';
2
- import { ComboBox, Search, Tab, Tabs, TabList, TabPanels } from '@carbon/react';
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
- {tabs.map((tab, index) => {
53
- return (
54
- <Tab title={t(tab.key)} key={index} id={'tab-' + index} className={styles.tab}>
55
- {t(tab.header)}
56
- </Tab>
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
- {tabs.map((tab, index) => {
98
- return index === selectedTab ? (
99
- <PrescriptionTabPanel location={location} searchTerm={searchTerm} status={tab.status} />
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 { 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
  };