diff --git a/__mocks__/medication.mock.ts b/__mocks__/medication.mock.ts index f34dbbd955..1e0cf91cf1 100644 --- a/__mocks__/medication.mock.ts +++ b/__mocks__/medication.mock.ts @@ -1,277 +1,207 @@ -import { OrderBasketItem } from '../packages/esm-patient-medications-app/src/types/order-basket-item'; +import { Drug } from '../packages/esm-patient-medications-app/src/types/order'; -export const mockDrugSearchResults = { - data: { - results: [ - { - uuid: '18f43c99-2329-426e-97b5-c3356e6afe54', - name: 'Aspirin', - display: 'Aspirin', - strength: '81mg', - dosageForm: { - display: 'Tablet', - uuid: '1513AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', - }, - concept: { - uuid: '1074AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', - }, - }, - ], +export const mockDrugSearchResultItems: Array = [ + { + uuid: '09e58895-e7f0-4649-b7c0-e665c5c08e93', + display: 'Aspirin', + name: 'Aspirin', + strength: '81mg', + dosageForm: { display: 'Tablet', uuid: '1513AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' }, + concept: { display: 'Aspirin', uuid: '71617AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' }, }, -}; -export const mockDurationUnitsResults = { - data: { - answers: [ - { uuid: '1074AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', display: 'Months' }, - { uuid: '1073AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', display: 'Weeks' }, - { uuid: '1072AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', display: 'Days' }, - { uuid: '1734AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', display: 'Years' }, - { uuid: '1733AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', display: 'Minutes' }, - { uuid: '1822AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', display: 'Hours' }, - { - uuid: '162582AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', - display: 'Number of occurrences', - }, - { uuid: '162583AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', display: 'Seconds' }, - ], + { + uuid: '38087db3-7395-431f-88d5-bb25e06e33f1', + display: 'Aspirin', + name: 'Aspirin', + strength: '325mg', + dosageForm: { display: 'Tablet', uuid: '1513AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' }, + concept: { display: 'Aspirin', uuid: '71617AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' }, }, -}; - -export const mockPatientEncounterIDResults = { - data: { - results: [{ uuid: '0926163e-8a28-43c2-a2cf-9851dc72f39d' }], + { + uuid: 'a722710f-403b-451f-804b-09f8624b0838', + display: 'Aspirin', + name: 'Aspirin', + strength: '162.5mg', + dosageForm: { display: 'Tablet', uuid: '1513AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' }, + concept: { display: 'Aspirin', uuid: '71617AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' }, }, -}; + { + uuid: '02dd2a8e-1a8f-49cb-bc06-daf9e1af16ba', + display: 'Aspirine Co', + name: 'Aspirine Co', + strength: '81mg', + dosageForm: { display: 'Tablet', uuid: '1513AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' }, + concept: { display: 'Aspirin', uuid: '71617AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' }, + }, +]; -export const mockMedicationOrderByUuidResponse = { - data: [ +export const mockDrugOrderTemplates = { + '09e58895-e7f0-4649-b7c0-e665c5c08e93': [ { - uuid: '42b8fe2f-8c55-45a2-8e40-cb0b8100de7c', - route: { - uuid: '160240AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', - display: 'Oral', - }, - action: 'REVISE', - urgency: 'ROUTINE', - display: '(REVISE) sulfadoxine: 2.0 Capsule Oral Once daily 5 Days dosing', - drug: { - display: 'sulfadoxine', - strength: '500mg', - }, - frequency: { - display: 'Once daily', - }, - dose: 2.0, - doseUnits: { - display: 'Capsule', - }, - orderer: { - uuid: 'e89cae4a-3cb3-40a2-b964-8b20dda2c985', - display: 'ghvbjnkm-1 - Fifty User', - person: { - uuid: 'e7dd932e-c2ac-4917-bf66-e59793adbd5f', - display: 'Fifty User', - links: [ - { - rel: 'self', - uri: 'http://localhost:8090/openmrs/ws/rest/v1/person/e7dd932e-c2ac-4917-bf66-e59793adbd5f', - }, - ], - }, - identifier: 'ghvbjnkm-1', - attributes: [], - retired: false, - links: [ - { - rel: 'self', - uri: 'http://localhost:8090/openmrs/ws/rest/v1/provider/e89cae4a-3cb3-40a2-b964-8b20dda2c985', - }, - { - rel: 'full', - uri: 'http://localhost:8090/openmrs/ws/rest/v1/provider/e89cae4a-3cb3-40a2-b964-8b20dda2c985?v=full', - }, - ], - resourceVersion: '1.9', - }, - dateStopped: null, - dateActivated: '2020-02-19T14:16:17.000+0000', - previousOrder: { - uuid: '0d46ad72-9d94-41f7-92cf-0a568cfff357', - orderNumber: 'ORD-142', - accessionNumber: null, - patient: { - uuid: '8673ee4f-e2ab-4077-ba55-4980f408773e', - display: '100GEJ - John Wilson', - links: [ + uuid: '270527a4-4cd9-4a84-8a11-f86e3d89f885', + display: 'Another template', + name: 'Another template', + description: 'For demo purposes', + template: { + type: 'https://schema.openmrs.org/order/template/drug/simple/v1', + dosingType: 'org.openmrs.SimpleDosingInstructions', + dosingInstructions: { + dose: [ { - rel: 'self', - uri: 'http://localhost:8090/openmrs/ws/rest/v1/patient/8673ee4f-e2ab-4077-ba55-4980f408773e', + value: 1, + default: true, }, ], - }, - concept: { - uuid: '84462AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', - display: 'SULFADOXINE', - links: [ + unit: [ { - rel: 'self', - uri: 'http://localhost:8090/openmrs/ws/rest/v1/concept/84462AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', + value: 'mg', + valueCoded: '161553AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', + default: true, }, ], - }, - action: 'NEW', - careSetting: { - uuid: '6f0c9a92-6f24-11e3-af88-005056821db0', - display: 'Outpatient', - links: [ + route: [ { - rel: 'self', - uri: 'http://localhost:8090/openmrs/ws/rest/v1/caresetting/6f0c9a92-6f24-11e3-af88-005056821db0', + value: 'oral', + valueCoded: '160240AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', + default: true, }, ], - }, - previousOrder: null, - dateActivated: '2020-02-19T14:15:47.000+0000', - scheduledDate: null, - dateStopped: '2020-02-19T14:16:16.000+0000', - autoExpireDate: null, - encounter: { - uuid: '11c22e25-f9f8-4c79-b384-1da39ee7d5d2', - display: 'Visit Note 10/11/2016', - links: [ + frequency: [ { - rel: 'self', - uri: 'http://localhost:8090/openmrs/ws/rest/v1/encounter/11c22e25-f9f8-4c79-b384-1da39ee7d5d2', + value: 'twice daily', + valueCoded: '160858AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', + default: true, }, ], - }, - orderer: { - uuid: 'e89cae4a-3cb3-40a2-b964-8b20dda2c985', - display: 'ghvbjnkm-1 - Fifty User', - links: [ + asNeeded: false, + asNeededCondition: 'Some value here..', + instructions: [ { - rel: 'self', - uri: 'http://localhost:8090/openmrs/ws/rest/v1/provider/e89cae4a-3cb3-40a2-b964-8b20dda2c985', + value: 'with or without food', + default: true, }, ], }, - orderReason: null, - orderReasonNonCoded: null, - orderType: { - uuid: '131168f4-15f5-102d-96e4-000c29c2a5d7', - display: 'Drug Order', - name: 'Drug Order', - javaClassName: 'org.openmrs.DrugOrder', - retired: false, - description: 'An order for a medication to be given to the patient', - conceptClasses: [], - parent: null, - links: [ - { - rel: 'self', - uri: 'http://localhost:8090/openmrs/ws/rest/v1/ordertype/131168f4-15f5-102d-96e4-000c29c2a5d7', - }, - { - rel: 'full', - uri: 'http://localhost:8090/openmrs/ws/rest/v1/ordertype/131168f4-15f5-102d-96e4-000c29c2a5d7?v=full', - }, - ], - resourceVersion: '1.10', + }, + retired: false, + drug: { + uuid: '09e58895-e7f0-4649-b7c0-e665c5c08e93', + display: 'Aspirin 81mg', + links: [ + { + rel: 'self', + uri: 'http://dev3.openmrs.org/openmrs/ws/rest/v1/drug/09e58895-e7f0-4649-b7c0-e665c5c08e93', + resourceAlias: 'drug', + }, + ], + }, + concept: { + uuid: '71617AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', + display: 'Aspirin', + links: [ + { + rel: 'self', + uri: 'http://dev3.openmrs.org/openmrs/ws/rest/v1/concept/71617AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', + resourceAlias: 'concept', + }, + ], + }, + links: [ + { + rel: 'self', + uri: 'http://dev3.openmrs.org/openmrs/ws/rest/v1/ordertemplates/orderTemplate/270527a4-4cd9-4a84-8a11-f86e3d89f885', + resourceAlias: 'orderTemplate', }, - urgency: 'ROUTINE', - instructions: null, - commentToFulfiller: null, - display: '(NEW) sulfadoxine: 1.0 Capsule Oral Once daily 5 Days dosing', - drug: { - uuid: 'fc92c351-8a85-41b9-95bf-a7dfea46c9cd', - display: 'sulfadoxine', - links: [ - { - rel: 'self', - uri: 'http://localhost:8090/openmrs/ws/rest/v1/drug/fc92c351-8a85-41b9-95bf-a7dfea46c9cd', - }, - ], + { + rel: 'full', + uri: 'http://dev3.openmrs.org/openmrs/ws/rest/v1/ordertemplates/orderTemplate/270527a4-4cd9-4a84-8a11-f86e3d89f885?v=full', + resourceAlias: 'orderTemplate', }, + ], + }, + ], + '02dd2a8e-1a8f-49cb-bc06-daf9e1af16ba': [ + { + uuid: '6b8de2bd-7fd5-432a-b215-861400f87e39', + display: 'Demo template', + name: 'Demo template', + description: 'For demo purposes', + template: { + type: 'https://schema.openmrs.org/order/template/drug/simple/v1', dosingType: 'org.openmrs.SimpleDosingInstructions', - dose: 1.0, - doseUnits: { - uuid: '1608AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', - display: 'Capsule', - links: [ + dosingInstructions: { + dose: [ { - rel: 'self', - uri: 'http://localhost:8090/openmrs/ws/rest/v1/concept/1608AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', + value: 300, + default: true, }, ], - }, - frequency: { - uuid: '160862OFAAAAAAAAAAAAAAA', - display: 'Once daily', - links: [ + unit: [ { - rel: 'self', - uri: 'http://localhost:8090/openmrs/ws/rest/v1/orderfrequency/160862OFAAAAAAAAAAAAAAA', + value: 'mg', + valueCoded: '161553AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', + default: true, }, ], - }, - asNeeded: false, - asNeededCondition: null, - quantity: 1.0, - quantityUnits: { - uuid: '162396AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', - display: 'Box', - links: [ + route: [ { - rel: 'self', - uri: 'http://localhost:8090/openmrs/ws/rest/v1/concept/162396AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', + value: 'oral', + valueCoded: '160240AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', + default: true, }, ], - }, - numRefills: 5, - dosingInstructions: 'dosing', - duration: 5, - durationUnits: { - uuid: '1072AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', - display: 'Days', - links: [ + frequency: [ { - rel: 'self', - uri: 'http://localhost:8090/openmrs/ws/rest/v1/concept/1072AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', + value: 'once daily', + valueCoded: '160858AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', + default: true, }, ], - }, - route: { - uuid: '160240AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', - display: 'Oral', - links: [ + asNeeded: false, + asNeededCondition: 'Some value here..', + instructions: [ { - rel: 'self', - uri: 'http://localhost:8090/openmrs/ws/rest/v1/concept/160240AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', + value: 'with or without food', + default: true, }, ], }, - brandName: null, - dispenseAsWritten: false, - drugNonCoded: null, + }, + retired: false, + drug: { + uuid: '09e58895-e7f0-4649-b7c0-e665c5c08e93', + display: 'Aspirin 81mg', links: [ { rel: 'self', - uri: 'http://localhost:8090/openmrs/ws/rest/v1/order/0d46ad72-9d94-41f7-92cf-0a568cfff357', + uri: 'http://dev3.openmrs.org/openmrs/ws/rest/v1/drug/09e58895-e7f0-4649-b7c0-e665c5c08e93', + resourceAlias: 'drug', }, + ], + }, + concept: { + uuid: '71617AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', + display: 'Aspirin', + links: [ { - rel: 'full', - uri: 'http://localhost:8090/openmrs/ws/rest/v1/order/0d46ad72-9d94-41f7-92cf-0a568cfff357?v=full', + rel: 'self', + uri: 'http://dev3.openmrs.org/openmrs/ws/rest/v1/concept/71617AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', + resourceAlias: 'concept', }, ], - type: 'drugorder', - resourceVersion: '1.10', - }, - numRefills: 5, - duration: 5, - durationUnits: { - display: 'Days', }, - type: 'drugorder', + links: [ + { + rel: 'self', + uri: 'http://dev3.openmrs.org/openmrs/ws/rest/v1/ordertemplates/orderTemplate/6b8de2bd-7fd5-432a-b215-861400f87e39', + resourceAlias: 'orderTemplate', + }, + { + rel: 'full', + uri: 'http://dev3.openmrs.org/openmrs/ws/rest/v1/ordertemplates/orderTemplate/6b8de2bd-7fd5-432a-b215-861400f87e39?v=full', + resourceAlias: 'orderTemplate', + }, + ], }, ], }; @@ -1228,62 +1158,3 @@ export const mockDrugOrders = { ], }, }; - -export const mockOrderTemplates = [ - { - uuid: 'cb43930e-3ed2-4099-ba0c-e6c841e22c4d', - display: 'Aspirin', - name: 'Aspirin', - description: 'Aspirin demo order template', - template: { - type: 'https://schema.openmrs.org/order/template/drug/simple/v1', - dosingType: 'org.openmrs.SimpleDosingInstructions', - dosingInstructions: { - dose: [ - { - value: 81, - default: true, - }, - ], - unit: [ - { - value: 'mg', - valueCoded: '3013AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', - default: true, - }, - ], - route: [ - { - value: 'Oral', - valueCoded: '160240AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', - default: true, - }, - ], - frequency: [ - { - value: 'Once daily', - valueCoded: '160862AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', - default: true, - }, - ], - instructions: [ - { - value: 'Taken after meals', - default: true, - }, - ], - asNeeded: false, - asNeededCondition: 'With or without food', - }, - }, - retired: false, - drug: { - uuid: '18f43c99-2329-426e-97b5-c3356e6afe54', - display: 'Aspirin', - }, - concept: { - uuid: '1074AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', - display: 'Abacavir sulfate', - }, - }, -]; diff --git a/packages/esm-patient-medications-app/src/api/api.ts b/packages/esm-patient-medications-app/src/api/api.ts index 9e8f8b4c25..2cea8a9877 100644 --- a/packages/esm-patient-medications-app/src/api/api.ts +++ b/packages/esm-patient-medications-app/src/api/api.ts @@ -1,6 +1,6 @@ import useSWR from 'swr'; import useSWRImmutable from 'swr/immutable'; -import { FetchResponse, openmrsFetch, Session, useConfig, OpenmrsResource, useSession } from '@openmrs/esm-framework'; +import { FetchResponse, openmrsFetch, useConfig, OpenmrsResource, useSession } from '@openmrs/esm-framework'; import { OrderPost, PatientMedicationFetchResponse } from '../types/order'; import { ConfigObject } from '../config-schema'; import { useEffect, useMemo, useState } from 'react'; @@ -45,15 +45,6 @@ export function getPatientEncounterId(patientUuid: string, abortController: Abor }); } -export function getDrugByName(drugName: string, abortController?: AbortController) { - return openmrsFetch( - `/ws/rest/v1/drug?q=${drugName}&v=custom:(uuid,display,name,strength,dosageForm:(display,uuid),concept)`, - { - signal: abortController?.signal, - }, - ); -} - export function useDurationUnits(durationUnitsConcept) { const url = `/ws/rest/v1/concept/${durationUnitsConcept}?v=custom:(answers:(uuid,display))`; const { data, error } = useSWRImmutable }>, Error>( @@ -82,12 +73,6 @@ export function getMedicationByUuid(abortController: AbortController, orderUuid: ); } -export function getOrderTemplatesByDrug(drugUuid: string, abortController?: AbortController) { - return openmrsFetch(`/ws/rest/v1/ordertemplates/orderTemplate?drug=${drugUuid}&retired=${false}`, { - signal: abortController?.signal, - }); -} - export function useCurrentOrderBasketEncounter(patientUuid: string) { const { currentVisit, mutate: mutateVisit } = useVisitOrOfflineVisit(patientUuid); const currentVisitUuid = currentVisit?.uuid; diff --git a/packages/esm-patient-medications-app/src/api/drug-order-template.ts b/packages/esm-patient-medications-app/src/api/drug-order-template.ts index 3106a26676..73abbfdf29 100644 --- a/packages/esm-patient-medications-app/src/api/drug-order-template.ts +++ b/packages/esm-patient-medications-app/src/api/drug-order-template.ts @@ -1,6 +1,7 @@ import { Drug } from '../types/order'; export interface DrugOrderTemplate { + uuid: string; name: string; drug: Drug; template: OrderTemplate; diff --git a/packages/esm-patient-medications-app/src/order-basket/drug-search.ts b/packages/esm-patient-medications-app/src/order-basket/drug-search.ts deleted file mode 100644 index cffb13d004..0000000000 --- a/packages/esm-patient-medications-app/src/order-basket/drug-search.ts +++ /dev/null @@ -1,150 +0,0 @@ -import uniqBy from 'lodash-es/uniqBy'; -import { getDrugByName, getOrderTemplatesByDrug } from '../api/api'; -import { OrderBasketItem } from '../types/order-basket-item'; -import { Drug } from '../types/order'; -import { DrugOrderTemplate, OrderTemplate } from '../api/drug-order-template'; - -// Note: -// There's currently no backend API available for the data in `common-medication.json`. -// This means that there's also no external API where we can search for that data. -// This results in us having to simulate a search on the frontend. -// The way this works is that we search all drugs matching the search term(s) in the backend and enrich that drug data -// with the data found in `common-medication.json`. These exploded results are then again filtered with the search term(s). -// -// For example, imagine that we search for "Aspirin 81mg". -// We call the drug endpoint with both "Aspirin" and "81mg" and receive a single drug (Aspirin) for the first term. -// `common-medication.json` now defines a lot of additional data for Aspirin (e.g. 81mg, 243mg, ...). -// -> We explode this info into multiple Aspirin results. But since the user also entered "81mg" as a search term, we -// also slim the final list of search results down again. -// -// This method certainly isn't perfect, but again, since the common medication data is only available to us, it's kind of -// the best thing we can do here. -interface DaysDurationUnit { - uuid: string; - display: string; -} - -export async function searchMedications( - searchTerm: string, - abortController: AbortController, - daysDurationUnit: DaysDurationUnit, -) { - const allSearchTerms = searchTerm.match(/\S+/g); - const drugs = await searchDrugsInBackend(allSearchTerms, abortController); - const drugToOrderTemplates = (await Promise.all( - drugs.map(async (drug) => { - let response: any = null; - try { - response = await getOrderTemplatesByDrug(drug.uuid); - } catch (error) { - // most likely there is no `Order Template` backend support - response = { data: { results: [] } }; // empty response - } - const orderTemplates = response.data.results.map((ot) => { - try { - ot.template = JSON.parse(ot.template); - return ot; - } catch (error) { - console.error(error); - return null; - } - }) as Array; - return { drug: drug, templates: orderTemplates.filter((x) => !!x) }; - }), - )) as any as Array<{ drug: Drug; templates: Array }>; - const explodedSearchResults = drugToOrderTemplates.flatMap(({ drug, templates }) => [ - ...explodeResultWithOrderTemplates(drug, templates, daysDurationUnit), - ]); - return filterExplodedResultsBySearchTerm(allSearchTerms, explodedSearchResults); -} - -async function searchDrugsInBackend(allSearchTerms: Array, abortController: AbortController) { - const resultsPerSearchTerm = await Promise.all( - allSearchTerms.map(async (searchTerm) => { - const res = await getDrugByName(searchTerm, abortController); - return res.data.results; - }), - ); - const results = resultsPerSearchTerm.flatMap((x) => x); - return uniqBy(results, 'uuid'); -} - -function getDefault(template: OrderTemplate, prop: string) { - return template.dosingInstructions[prop].find((x) => x.default) || template.dosingInstructions[prop][0]; -} - -function* explodeResultWithOrderTemplates( - drug: Drug, - templates: Array, - daysDurationUnit: DaysDurationUnit, -): Generator { - if (templates?.length) { - for (const template of templates) { - yield { - action: 'NEW', - drug, - unit: getDefault(template.template, 'unit'), - dosage: getDefault(template.template, 'dose')?.value, - frequency: getDefault(template.template, 'frequency'), - route: getDefault(template.template, 'route'), - commonMedicationName: drug.name, - isFreeTextDosage: false, - patientInstructions: '', - asNeeded: template.template.dosingInstructions.asNeeded || false, - asNeededCondition: template.template.dosingInstructions.asNeededCondition, - startDate: new Date(), - duration: null, - durationUnit: daysDurationUnit, - pillsDispensed: 0, - numRefills: 0, - freeTextDosage: '', - indication: '', - template: template.template, - orderer: null, - careSetting: null, - quantityUnits: null, - }; - } - } else { - yield { - action: 'NEW', - drug, - unit: null, - dosage: null, - frequency: null, - route: null, - commonMedicationName: drug.name, - isFreeTextDosage: false, - patientInstructions: '', - asNeeded: false, - asNeededCondition: null, - startDate: new Date(), - duration: null, - durationUnit: daysDurationUnit, - pillsDispensed: 0, - numRefills: 0, - freeTextDosage: '', - indication: '', - orderer: null, - careSetting: null, - quantityUnits: null, - }; - } -} - -function filterExplodedResultsBySearchTerm(allSearchTerms: Array, results: any) { - return results.filter((result) => - allSearchTerms.every( - (searchTerm) => - includesIgnoreCase(result.drug.name, searchTerm) || - includesIgnoreCase(result.dosageUnit.name, searchTerm) || - includesIgnoreCase(result.dosage.dosage, searchTerm) || - includesIgnoreCase(result.frequency.name, searchTerm) || - includesIgnoreCase(result.route.name, searchTerm), - ), - ); -} - -function includesIgnoreCase(a: string, b: string) { - return a.toLowerCase().includes(b.toLowerCase()); -} diff --git a/packages/esm-patient-medications-app/src/order-basket/medication-order-form.component.tsx b/packages/esm-patient-medications-app/src/order-basket/medication-order-form.component.tsx index 06a5facd0f..82ba2feec2 100644 --- a/packages/esm-patient-medications-app/src/order-basket/medication-order-form.component.tsx +++ b/packages/esm-patient-medications-app/src/order-basket/medication-order-form.component.tsx @@ -233,6 +233,7 @@ export default function MedicationOrderForm({ initialOrderBasketItem, onSign, on dosage: value ? parseFloat(value) : 0, }); }} + min={0} hideSteppers /> diff --git a/packages/esm-patient-medications-app/src/order-basket/order-basket-item.component.tsx b/packages/esm-patient-medications-app/src/order-basket/order-basket-item.component.tsx index eaff23c5b1..a6fe08f7f1 100644 --- a/packages/esm-patient-medications-app/src/order-basket/order-basket-item.component.tsx +++ b/packages/esm-patient-medications-app/src/order-basket/order-basket-item.component.tsx @@ -31,12 +31,12 @@ export default function OrderBasketItemTile({ orderBasketItem, onItemClick, onRe
{orderBasketItem.isFreeTextDosage ? ( <> - {orderBasketItem.drug.concept.display} + {orderBasketItem.drug?.display} — {orderBasketItem.freeTextDosage} ) : ( <> - {orderBasketItem.drug.concept.display} + {orderBasketItem.drug?.display} {' '} — {orderBasketItem.drug.strength} — {orderBasketItem.drug.dosageForm.display} diff --git a/packages/esm-patient-medications-app/src/order-basket/order-basket-search-results.component.tsx b/packages/esm-patient-medications-app/src/order-basket/order-basket-search-results.component.tsx deleted file mode 100644 index ebf936cbe9..0000000000 --- a/packages/esm-patient-medications-app/src/order-basket/order-basket-search-results.component.tsx +++ /dev/null @@ -1,140 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { Button, Pagination, ClickableTile, Tile, SkeletonText, SkeletonIcon } from '@carbon/react'; -import { ShoppingCart } from '@carbon/react/icons'; -import { useTranslation } from 'react-i18next'; -import { createErrorHandler, useConfig, useLayoutType } from '@openmrs/esm-framework'; -import { searchMedications } from './drug-search'; -import { OrderBasketItem } from '../types/order-basket-item'; -import { paginate } from '../utils/pagination'; -import { ConfigObject } from '../config-schema'; -import styles from './order-basket-search-results.scss'; - -export interface OrderBasketSearchResultsProps { - searchTerm: string; - setSearchTerm: (value: string) => void; - onSearchResultClicked: (searchResult: OrderBasketItem, directlyAddToBasket: boolean) => void; -} - -export default function OrderBasketSearchResults({ - searchTerm, - setSearchTerm, - onSearchResultClicked, -}: OrderBasketSearchResultsProps) { - const { t } = useTranslation(); - const isTablet = useLayoutType() === 'tablet'; - const [isLoading, setIsLoading] = useState(false); - const [searchResults, setSearchResults] = useState>([]); - const [page, setPage] = useState(1); - const [pageSize, setPageSize] = useState(10); - const [currentSearchResultPage] = paginate(searchResults, page, pageSize); - const config = useConfig() as ConfigObject; - - useEffect(() => { - const abortController = new AbortController(); - if (searchTerm) { - if (searchTerm.length == 1) { - setSearchResults([]); - return; - } - setIsLoading(true); - searchMedications(searchTerm, abortController, config.daysDurationUnit).then((results) => { - setIsLoading(false); - setSearchResults(results); - }, createErrorHandler); - } - return () => abortController.abort(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [searchTerm]); - - const handleSearchResultClicked = (searchResult: OrderBasketItem, directlyAddToBasket: boolean) => { - setSearchTerm(''); - setSearchResults([]); - onSearchResultClicked(searchResult, directlyAddToBasket); - }; - - return ( - <> - {!!searchTerm && isLoading && ( - <> - - - - )} - {!!searchTerm && !isLoading && ( -
-
- - {t('searchResultsExactMatchesForTerm', '{count} exact match(es) for "{searchTerm}"', { - count: searchResults.length, - searchTerm, - })} - - -
- {currentSearchResultPage.map((result, index) => ( - handleSearchResultClicked(result, false)} - > -
-
-

- {result.drug?.display}{' '} - {result?.drug?.strength && <>— {result?.drug?.strength}}{' '} - {result?.drug?.dosageForm?.display && <>— {result?.drug?.dosageForm?.display}} - {result.template && ( - <> - {result.frequency?.value} —{' '} - {result.route?.value} - - )} -

-
-
-
- ))} - {searchResults.length > 0 && ( - <> - { - setPage(page); - setPageSize(pageSize); - }} - /> -
- - )} -
- )} - - ); -} - -const DrugOrderSearchResultSkeleton = () => { - return ( - -
- - -
-
- ); -}; diff --git a/packages/esm-patient-medications-app/src/order-basket/order-basket-search-results.test.tsx b/packages/esm-patient-medications-app/src/order-basket/order-basket-search-results.test.tsx deleted file mode 100644 index 14785cc2eb..0000000000 --- a/packages/esm-patient-medications-app/src/order-basket/order-basket-search-results.test.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import React from 'react'; -import { screen, render, waitFor } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import { getByTextWithMarkup } from '../../../../tools/test-helpers'; -import { mockMedicationOrderSearchResults, mockOrderTemplates } from '../../../../__mocks__/medication.mock'; -import { paginate } from '../utils/pagination'; -import { searchMedications } from './drug-search'; -import OrderBasketSearchResults from './order-basket-search-results.component'; - -const mockPaginate = paginate as jest.Mock; -const mockSearchMedications = searchMedications as jest.Mock; - -const testProps = { - encounterUuid: '', - onSearchResultClicked: jest.fn(), - searchTerm: 'aspirin', - setSearchTerm: jest.fn(), -}; - -jest.mock('./drug-search', () => ({ - searchMedications: jest.fn(), - searchDrugsInBackend: jest.fn(), - explodeDrugResultWithCommonMedicationData: jest.fn(), - filterExplodedResultsBySearchTerm: jest.fn(), - includesIgnoreCase: jest.fn(), -})); - -jest.mock('../utils/pagination', () => ({ - paginate: jest.fn(), -})); - -describe('OrderBasketSearchResults', () => { - test('renders matching orders as clickable tiles after searching for a drug order', async () => { - const user = userEvent.setup(); - // link order template - mockMedicationOrderSearchResults[0]['template'] = mockOrderTemplates[0].template; - - mockSearchMedications.mockResolvedValue(mockMedicationOrderSearchResults); - mockPaginate.mockReturnValue([mockMedicationOrderSearchResults]); - - renderOrderBasketSearchResults(); - - await screen.findAllByRole('listitem'); - expect(screen.getAllByRole('listitem').length).toEqual(3); - // Anotates results with dosing info if an order-template was found. - expect(getByTextWithMarkup(/Aspirin — 81mg — Tablet\s*Once daily — Oral/i)).toBeInTheDocument(); - // Only displays drug name for results without a matching order template - expect(getByTextWithMarkup(/Aspirin — 125mg — Tablet/i)).toBeInTheDocument(); - expect(getByTextWithMarkup(/Aspirin — 243mg — Tablet/i)).toBeInTheDocument(); - expect(screen.getAllByRole('button', { name: /Immediately add to basket/i }).length).toEqual(3); - - await waitFor(() => user.click(screen.getAllByRole('listitem')[0])); - - expect(testProps.onSearchResultClicked).toHaveBeenCalledWith(mockMedicationOrderSearchResults[0], false); - }); -}); - -function renderOrderBasketSearchResults() { - render(); -} diff --git a/packages/esm-patient-medications-app/src/order-basket/order-basket-search/drug-search.resource.tsx b/packages/esm-patient-medications-app/src/order-basket/order-basket-search/drug-search.resource.tsx new file mode 100644 index 0000000000..3b0194815a --- /dev/null +++ b/packages/esm-patient-medications-app/src/order-basket/order-basket-search/drug-search.resource.tsx @@ -0,0 +1,120 @@ +import { FetchResponse, openmrsFetch } from '@openmrs/esm-framework'; +import { useMemo } from 'react'; +import useSWRImmutable from 'swr/immutable'; +import { DrugOrderTemplate, OrderTemplate } from '../../api/drug-order-template'; +import { Drug } from '../../types/order'; +import { OrderBasketItem } from '../../types/order-basket-item'; + +export function useDrugSearch(query): { + isLoading: boolean; + drugs: Array; + error: Error; +} { + const { data, error } = useSWRImmutable }>, Error>( + query + ? `/ws/rest/v1/drug?q=${query}&v=custom:(uuid,display,name,strength,dosageForm:(display,uuid),concept:(display,uuid))` + : null, + openmrsFetch, + ); + + const results = useMemo( + () => ({ + isLoading: !data && !error, + drugs: data?.data?.results, + error, + }), + [data, error], + ); + + return results; +} + +export function useDrugTemplate(drugUuid: string): { + isLoading: boolean; + templates: Array; + error: Error; +} { + const { data, error } = useSWRImmutable< + FetchResponse<{ + results: Array<{ + uuid: string; + drug: Drug; + name: string; + template: string; + }>; + }>, + Error + >(drugUuid ? `/ws/rest/v1/ordertemplates/orderTemplate?drug=${drugUuid}` : null, openmrsFetch); + + const results = useMemo( + () => ({ + isLoading: !data && !error, + templates: data?.data?.results?.map((drug) => ({ + ...drug, + template: JSON.parse(drug.template) as OrderTemplate, + })), + error: error, + }), + [data, error], + ); + return results; +} + +export function getDefault(template: OrderTemplate, prop: string) { + return template.dosingInstructions[prop].find((x) => x.default) || template.dosingInstructions[prop][0]; +} + +export function getTemplateOrderBasketItem( + drug, + daysDurationUnit, + template: DrugOrderTemplate = null, +): OrderBasketItem { + return template + ? { + action: 'NEW', + drug, + unit: getDefault(template.template, 'unit'), + dosage: getDefault(template.template, 'dose')?.value, + frequency: getDefault(template.template, 'frequency'), + route: getDefault(template.template, 'route'), + commonMedicationName: drug.name, + isFreeTextDosage: false, + patientInstructions: '', + asNeeded: template.template.dosingInstructions.asNeeded || false, + asNeededCondition: template.template.dosingInstructions.asNeededCondition, + startDate: new Date(), + duration: null, + durationUnit: daysDurationUnit, + pillsDispensed: 0, + numRefills: 0, + freeTextDosage: '', + indication: '', + template: template.template, + orderer: null, + careSetting: null, + quantityUnits: null, + } + : { + action: 'NEW', + drug, + unit: null, + dosage: null, + frequency: null, + route: null, + commonMedicationName: drug.name, + isFreeTextDosage: false, + patientInstructions: '', + asNeeded: false, + asNeededCondition: null, + startDate: new Date(), + duration: null, + durationUnit: daysDurationUnit, + pillsDispensed: 0, + numRefills: 0, + freeTextDosage: '', + indication: '', + orderer: null, + careSetting: null, + quantityUnits: null, + }; +} diff --git a/packages/esm-patient-medications-app/src/order-basket/order-basket-search.component.tsx b/packages/esm-patient-medications-app/src/order-basket/order-basket-search/index.tsx similarity index 95% rename from packages/esm-patient-medications-app/src/order-basket/order-basket-search.component.tsx rename to packages/esm-patient-medications-app/src/order-basket/order-basket-search/index.tsx index 46080cf13e..5e3b14e718 100644 --- a/packages/esm-patient-medications-app/src/order-basket/order-basket-search.component.tsx +++ b/packages/esm-patient-medications-app/src/order-basket/order-basket-search/index.tsx @@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next'; import { Search } from '@carbon/react'; import { useLayoutType } from '@openmrs/esm-framework'; import OrderBasketSearchResults from './order-basket-search-results.component'; -import { OrderBasketItem } from '../types/order-basket-item'; +import { OrderBasketItem } from '../../types/order-basket-item'; import styles from './order-basket-search.scss'; export interface OrderBasketSearchProps { diff --git a/packages/esm-patient-medications-app/src/order-basket/order-basket-search/order-basket-search-results.component.tsx b/packages/esm-patient-medications-app/src/order-basket/order-basket-search/order-basket-search-results.component.tsx new file mode 100644 index 0000000000..abaab30ced --- /dev/null +++ b/packages/esm-patient-medications-app/src/order-basket/order-basket-search/order-basket-search-results.component.tsx @@ -0,0 +1,194 @@ +import React, { useMemo } from 'react'; +import { Button, ClickableTile, Tile, SkeletonText, InlineNotification, ButtonSkeleton } from '@carbon/react'; +import { ShoppingCart } from '@carbon/react/icons'; +import { useTranslation } from 'react-i18next'; +import { useConfig, useLayoutType } from '@openmrs/esm-framework'; +import { OrderBasketItem } from '../../types/order-basket-item'; +import { ConfigObject } from '../../config-schema'; +import styles from './order-basket-search-results.scss'; +import { getTemplateOrderBasketItem, useDrugSearch, useDrugTemplate } from './drug-search.resource'; +import { Drug } from '../../types/order'; + +export interface OrderBasketSearchResultsProps { + searchTerm: string; + setSearchTerm: (value: string) => void; + onSearchResultClicked: (searchResult: OrderBasketItem, directlyAddToBasket: boolean) => void; +} + +export default function OrderBasketSearchResults({ + searchTerm, + setSearchTerm, + onSearchResultClicked, +}: OrderBasketSearchResultsProps) { + const { t } = useTranslation(); + const isTablet = useLayoutType() === 'tablet'; + const { drugs, isLoading, error } = useDrugSearch(searchTerm); + + if (!searchTerm) { + return null; + } + + if (isLoading) { + return ; + } + + if (error) { + return ( + +
+

+ {t('errorFetchingDrugResults', 'Error fetching results for "{searchTerm}"', { + searchTerm, + })} +

+

+ {t('trySearchingAgain', 'Please try searching again')} +

+
+
+ ); + } + + return ( + <> + {drugs?.length ? ( +
+
+ + {t('searchResultsMatchesForTerm', '{count} result{plural} for "{searchTerm}"', { + count: drugs?.length, + searchTerm, + plural: drugs?.length > 1 ? 's' : '', + })} + + +
+
+ {drugs.map((drug) => ( + + ))} +
+
+ ) : ( + +
+

+ {t('noResultsForDrugSearch', 'No results to display for "{searchTerm}"', { + searchTerm, + })} +

+

+ {t('tryTo', 'Try to')}{' '} + setSearchTerm('')}> + {t('searchAgain', 'search again')} + {' '} + {t('usingADifferentTerm', 'using a different term')} +

+
+
+ )} +
+ + ); +} + +interface DrugSearchResultItemProps { + drug: Drug; + onSearchResultClicked: (searchResult: OrderBasketItem, directlyAddToBasket: boolean) => void; +} + +const DrugSearchResultItem: React.FC = ({ drug, onSearchResultClicked }) => { + const isTablet = useLayoutType() === 'tablet'; + const { + templates, + isLoading: isLoadingTemplates, + error: fetchingDrugOrderTemplatesError, + } = useDrugTemplate(drug?.uuid); + const { t } = useTranslation(); + const config = useConfig() as ConfigObject; + const orderItems: Array = useMemo( + () => + templates?.length + ? templates.map((template) => getTemplateOrderBasketItem(drug, config?.daysDurationUnit, template)) + : [getTemplateOrderBasketItem(drug, config?.daysDurationUnit)], + [templates, drug, config?.daysDurationUnit], + ); + + const handleSearchResultClicked = (searchResult: OrderBasketItem, directlyAddToBasket: boolean) => { + onSearchResultClicked(searchResult, directlyAddToBasket); + }; + + return ( + <> + {orderItems.map((orderItem, indx) => ( + handleSearchResultClicked(orderItem, false)} + > +
+
+

+ {drug?.display} {drug?.strength && <>— {drug?.strength}}{' '} + {drug?.dosageForm?.display && <>— {drug?.dosageForm?.display}} +

+ {fetchingDrugOrderTemplatesError ? ( +

+ + {t('errorFetchingDrugOrderTemplates', 'Error fetching drug order templates')} + +

+ ) : ( +

+ {orderItem?.frequency?.value && {orderItem?.frequency?.value}} + {orderItem?.route?.value && — {orderItem?.route?.value}} +

+ )} +
+
+
+ ))} + + ); +}; + +const DrugSearchSkeleton = () => { + const isTablet = useLayoutType() === 'tablet'; + const tileClassName = `${isTablet ? `${styles.tabletSearchResultTile}` : `${styles.desktopSearchResultTile}`} ${ + styles.skeletonTile + }`; + return ( +
+
+ + +
+ + + + + + + + + + + + +
+
+ ); +}; diff --git a/packages/esm-patient-medications-app/src/order-basket/order-basket-search-results.scss b/packages/esm-patient-medications-app/src/order-basket/order-basket-search/order-basket-search-results.scss similarity index 52% rename from packages/esm-patient-medications-app/src/order-basket/order-basket-search-results.scss rename to packages/esm-patient-medications-app/src/order-basket/order-basket-search/order-basket-search-results.scss index 0d006c181b..148247a3ff 100644 --- a/packages/esm-patient-medications-app/src/order-basket/order-basket-search-results.scss +++ b/packages/esm-patient-medications-app/src/order-basket/order-basket-search/order-basket-search-results.scss @@ -1,13 +1,12 @@ @use '@carbon/styles/scss/spacing'; @use '@carbon/styles/scss/type'; -@import "../root"; +@import "../../root"; .container { - margin: spacing.$spacing-03 1rem spacing.$spacing-03; + margin: spacing.$spacing-03 spacing.$spacing-05 spacing.$spacing-03; } .desktopSearchResultTile { - margin-top: 5px; border-left: 4px solid var(--brand-03); &:not(:last-of-type) { @@ -18,6 +17,7 @@ .tabletSearchResultTile { margin-top: 5px; border-left: 2px solid var(--brand-03); + background-color: $ui-02; &:not(:last-of-type) { margin-bottom: 1rem; @@ -65,7 +65,59 @@ flex: 0 0 auto; } +.errorLabel { + @extend .label01; + color: $danger; +} + .searchResultSkeletonWrapper { - @extend .searchResultTile; - border-bottom: 1px solid $ui-03; + margin: spacing.$spacing-03 1rem spacing.$spacing-03; + + :global(.cds--skeleton__text) { + margin: 0; + } + + .searchResultCntSkeleton { + margin-right: spacing.$spacing-07; + } + + .skeletonTile { + display: flex; + align-items: center; + } +} + +.emptyState { + margin: spacing.$spacing-05; + display: flex; + justify-content: center; + align-items: center; + padding: spacing.$spacing-07; + border: 1px solid $ui-03; + text-align: center; + background-color: $ui-01; +} + +:global(.omrs-breakpoint-lt-small-desktop) .emptyState { + background-color: $ui-02; +} + +.link { + color: $interactive-01; + text-decoration: underline; + cursor: pointer; +} + +.resultsContainer { + max-height: 27.5rem; + overflow-y: scroll; +} + +.resultsContainer::-webkit-scrollbar { + width: spacing.$spacing-03; +} + +.resultsContainer::-webkit-scrollbar-thumb { + background: $ui-04; + border-radius: spacing.$spacing-02; } \ No newline at end of file diff --git a/packages/esm-patient-medications-app/src/order-basket/order-basket-search/order-basket-search-results.test.tsx b/packages/esm-patient-medications-app/src/order-basket/order-basket-search/order-basket-search-results.test.tsx new file mode 100644 index 0000000000..2d79ccedea --- /dev/null +++ b/packages/esm-patient-medications-app/src/order-basket/order-basket-search/order-basket-search-results.test.tsx @@ -0,0 +1,68 @@ +import React from 'react'; +import { screen, render, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { getByTextWithMarkup } from '../../../../../tools/test-helpers'; +import { mockDrugOrderTemplates, mockDrugSearchResultItems } from '../../../../../__mocks__/medication.mock'; +import { getTemplateOrderBasketItem, useDrugSearch, useDrugTemplate } from './drug-search.resource'; +import OrderBasketSearchResults from './order-basket-search-results.component'; + +const testProps = { + encounterUuid: '', + onSearchResultClicked: jest.fn(), + searchTerm: 'aspirin', + setSearchTerm: jest.fn(), +}; + +jest.mock('./drug-search.resource', () => ({ + ...jest.requireActual('./drug-search.resource'), + useDrugSearch: jest.fn(), + useDrugTemplate: jest.fn(), +})); + +describe('OrderBasketSearchResults', () => { + test('renders matching orders as clickable tiles after searching for a drug order', async () => { + const user = userEvent.setup(); + + (useDrugSearch as jest.Mock).mockImplementation(() => ({ + isLoading: false, + drugs: mockDrugSearchResultItems, + error: null, + })); + + (useDrugTemplate as jest.Mock).mockImplementation((drugUuid) => ({ + templates: mockDrugOrderTemplates[drugUuid] ?? [], + isLoading: false, + error: false, + })); + + const mockDate = new Date(1466424490000); + const spy = jest.spyOn(global, 'Date').mockImplementation(() => mockDate.toISOString()); + + renderOrderBasketSearchResults(); + + await screen.findAllByRole('listitem'); + expect(screen.getAllByRole('listitem').length).toEqual(4); + // Anotates results with dosing info if an order-template was found. + expect(getByTextWithMarkup(/Aspirin — 81mg — Tablet/i)).toBeInTheDocument(); + // Only displays drug name for results without a matching order template + expect(getByTextWithMarkup(/Aspirin — 325mg — Tablet/i)).toBeInTheDocument(); + expect(getByTextWithMarkup(/Aspirin — 162.5mg — Tablet/i)).toBeInTheDocument(); + + expect(screen.getAllByRole('button', { name: /Immediately add to basket/i }).length).toEqual(4); + + await waitFor(() => user.click(screen.getAllByRole('listitem')[0])); + + expect(testProps.onSearchResultClicked).toHaveBeenCalledWith( + getTemplateOrderBasketItem( + mockDrugSearchResultItems[0], + undefined, + mockDrugOrderTemplates[mockDrugSearchResultItems[0].uuid][0], + ), + false, + ); + }); +}); + +function renderOrderBasketSearchResults() { + render(); +} diff --git a/packages/esm-patient-medications-app/src/order-basket/order-basket-search.scss b/packages/esm-patient-medications-app/src/order-basket/order-basket-search/order-basket-search.scss similarity index 95% rename from packages/esm-patient-medications-app/src/order-basket/order-basket-search.scss rename to packages/esm-patient-medications-app/src/order-basket/order-basket-search/order-basket-search.scss index 67074fcc60..eb2b59345e 100644 --- a/packages/esm-patient-medications-app/src/order-basket/order-basket-search.scss +++ b/packages/esm-patient-medications-app/src/order-basket/order-basket-search/order-basket-search.scss @@ -1,4 +1,4 @@ -@import "../root"; +@import "../../root"; .searchPopupContainer { position: relative; diff --git a/packages/esm-patient-medications-app/src/order-basket/order-basket.component.tsx b/packages/esm-patient-medications-app/src/order-basket/order-basket.component.tsx index 726335f3f9..330978b87e 100644 --- a/packages/esm-patient-medications-app/src/order-basket/order-basket.component.tsx +++ b/packages/esm-patient-medications-app/src/order-basket/order-basket.component.tsx @@ -13,7 +13,7 @@ import { OrderBasketStore, OrderBasketStoreActions, orderBasketStoreActions } fr import MedicationOrderForm from './medication-order-form.component'; import MedicationsDetailsTable from '../components/medications-details-table.component'; import OrderBasketItemList from './order-basket-item-list.component'; -import OrderBasketSearch from './order-basket-search.component'; +import OrderBasketSearch from './order-basket-search'; import styles from './order-basket.scss'; export interface OrderBasketProps { diff --git a/packages/esm-patient-medications-app/translations/en.json b/packages/esm-patient-medications-app/translations/en.json index 1c213aa18c..255b30e487 100644 --- a/packages/esm-patient-medications-app/translations/en.json +++ b/packages/esm-patient-medications-app/translations/en.json @@ -31,6 +31,8 @@ "endDate": "End date", "error": "Error", "errorCreatingAnEncounter": "Error when creating an encounter", + "errorFetchingDrugOrderTemplates": "Error fetching drug order templates", + "errorFetchingDrugResults": "Error fetching medications", "errorFetchingDurationUnits": "Error occured when fetching duration units", "fetchingDurationUnits": "Fetching duration units...", "freeTextDosage": "Free Text Dosage", @@ -41,6 +43,7 @@ "modify": "Modify", "noDurationHint": "An empty field indicates an indefinite duration.", "none": "None", + "noResultsForDrugSearch": "No results to display for \"{searchTerm}\"", "orderActionDiscontinue": "Discontinue", "orderActionNew": "New", "orderActionRenewed": "Renew", @@ -74,15 +77,19 @@ "revisedOrders_one": "{count} order(s) being modified (revised)", "revisedOrders_other": "{count} order(s) being modified (revised)", "saveOrder": "Save order", + "searchAgain": "search again", "searchFieldPlaceholder": "Search for a drug or orderset (e.g. \"Aspirin\")", "searchForAnOrder": "Search for an order above", - "searchResultsExactMatchesForTerm_one": "{count} exact match(es) for \"{searchTerm}\"", - "searchResultsExactMatchesForTerm_other": "{count} exact match(es) for \"{searchTerm}\"", + "searchResultsMatchesForTerm_one": "{count} result{plural} for \"{searchTerm}\"", + "searchResultsMatchesForTerm_other": "{count} result{plural} for \"{searchTerm}\"", "signAndClose": "Sign and close", "startAVisitToRecordOrders": "Start a visit to order", "startDate": "Start date", "startVisit": "Start visit", "takeAsNeeded": "Take As Needed", "tryReopeningTheForm": "Please try launching the form again", - "tryReopeningTheWorkspaceAgain": "Please try launching the workspace again" + "tryReopeningTheWorkspaceAgain": "Please try launching the workspace again", + "trySearchingAgain": "Please try searching again", + "tryTo": "Try to", + "usingADifferentTerm": "using a different term" }