From cd878747589e5d2b39605e6529e409a378d62e98 Mon Sep 17 00:00:00 2001 From: Laurent Ouma Date: Wed, 20 Nov 2024 21:28:32 +0300 Subject: [PATCH 01/10] Added preview model with printing capability --- package.json | 3 +- src/index.ts | 11 +- .../prescription-tab-panel.component.tsx | 9 +- .../prescription-print-action.component.tsx | 34 ++++++ .../prescription-print-preview.modal.tsx | 83 ++++++++++++++ .../prescription-printout.component.tsx | 108 ++++++++++++++++++ .../print-prescription.scss | 12 ++ src/routes.json | 8 +- yarn.lock | 11 ++ 9 files changed, 272 insertions(+), 7 deletions(-) create mode 100644 src/print-prescription/prescription-print-action.component.tsx create mode 100644 src/print-prescription/prescription-print-preview.modal.tsx create mode 100644 src/print-prescription/prescription-printout.component.tsx create mode 100644 src/print-prescription/print-prescription.scss diff --git a/package.json b/package.json index 44f0461..8b78230 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,8 @@ "dependencies": { "@carbon/react": "1.12.0", "classnames": "^2.5.1", - "lodash-es": "^4.17.21" + "lodash-es": "^4.17.21", + "react-to-print": "^2.14.13" }, "peerDependencies": { "@openmrs/esm-framework": "*", diff --git a/src/index.ts b/src/index.ts index 3b527f5..bb566b5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,6 +7,7 @@ import DispensingDashboardComponent from './dashboard/dispensing-dashboard.compo import DispensingLinkComponent from './dispensing-link.component'; import DispensingLinkHomepageComponent from './dashboard/dispensing-dashboard-link.component'; import PauseActionButton from './components/prescription-actions/pause-action-button.component'; +import PrescriptionPrintPreviewModal from './print-prescription/prescription-print-preview.modal'; export const importTranslation = require.context('../translations', false, /.json$/, 'lazy'); @@ -17,16 +18,16 @@ const options = { moduleName, }; +export function startupApp() { + defineConfigSchema(moduleName, configSchema); +} + export const dispensing = getSyncLifecycle(DispensingComponent, options); export const dispensingLink = getSyncLifecycle(DispensingLinkComponent, options); export const dispensingDashboard = getSyncLifecycle(DispensingDashboardComponent, options); -export function startupApp() { - defineConfigSchema(moduleName, configSchema); -} - export const dispensingDashboardLink = getSyncLifecycle(DispensingLinkHomepageComponent, options); // Prescription action buttons @@ -41,3 +42,5 @@ export const closeDispenseWorkspace = getAsyncLifecycle(() => import('./forms/cl export const dispenseWorkspace = getAsyncLifecycle(() => import('./forms/dispense-form.workspace'), options); // t('pausePrescription', 'Pause prescription') export const pauseDispenseWorkspace = getAsyncLifecycle(() => import('./forms/pause-dispense-form.workspace'), options); + +export const printPrescriptionPreviewModal = getSyncLifecycle(PrescriptionPrintPreviewModal, options); diff --git a/src/prescriptions/prescription-tab-panel.component.tsx b/src/prescriptions/prescription-tab-panel.component.tsx index 48def7f..75cd574 100644 --- a/src/prescriptions/prescription-tab-panel.component.tsx +++ b/src/prescriptions/prescription-tab-panel.component.tsx @@ -24,6 +24,7 @@ import { usePrescriptionsTable } from '../medication-request/medication-request. import PatientInfoCell from '../patient/patient-info-cell.component'; import PrescriptionExpanded from './prescription-expanded.component'; import styles from './prescriptions.scss'; +import PrescriptionPrintAction from '../print-prescription/prescription-print-action.component'; interface PrescriptionTabPanelProps { searchTerm: string; @@ -54,6 +55,7 @@ const PrescriptionTabPanel: React.FC = ({ searchTerm, { header: t('drugs', 'Drugs'), key: 'drugs' }, { header: t('lastDispenser', 'Last dispenser'), key: 'lastDispenser' }, { header: t('status', 'Status'), key: 'status' }, + { header: t('actions', 'Actions'), key: 'actions' }, ]; // add the locations column, if enabled @@ -67,6 +69,11 @@ const PrescriptionTabPanel: React.FC = ({ searchTerm, setNextOffSet(0); }, [searchTerm]); + const rows = prescriptionsTableRows?.map((row) => ({ + ...row, + actions: , + })); + return (
@@ -74,7 +81,7 @@ const PrescriptionTabPanel: React.FC = ({ searchTerm, {error &&

Error

} {prescriptionsTableRows && ( <> - + {({ rows, headers, getExpandHeaderProps, getHeaderProps, getRowProps, getTableProps }) => ( diff --git a/src/print-prescription/prescription-print-action.component.tsx b/src/print-prescription/prescription-print-action.component.tsx new file mode 100644 index 0000000..f48e08a --- /dev/null +++ b/src/print-prescription/prescription-print-action.component.tsx @@ -0,0 +1,34 @@ +import React from 'react'; +import { Button } from '@carbon/react'; +import { Printer } from '@carbon/react/icons'; +import { useTranslation } from 'react-i18next'; +import { showModal } from '@openmrs/esm-framework'; + +type PrescriptionPrintActionProps = { + encounterUuid: string; + patientUuid: string; + status: string; +}; + +const PrescriptionPrintAction: React.FC = ({ encounterUuid, patientUuid }) => { + const { t } = useTranslation(); + + return ( + + + + + + ); +}; + +export default PrescriptionPrintPreviewModal; diff --git a/src/print-prescription/prescription-printout.component.tsx b/src/print-prescription/prescription-printout.component.tsx new file mode 100644 index 0000000..32a6165 --- /dev/null +++ b/src/print-prescription/prescription-printout.component.tsx @@ -0,0 +1,108 @@ +import React from 'react'; +import { type DosageInstruction, type MedicationRequestBundle, type Quantity } from '../types'; +import { + getDosageInstruction, + getMedicationDisplay, + getMedicationReferenceOrCodeableConcept, + getQuantity, + getRefillsAllowed, +} from '../utils'; +import { + StructuredListWrapper, + StructuredListBody, + StructuredListRow, + StructuredListCell, +} from '@carbon/react'; +import { useTranslation } from 'react-i18next'; + +type PrescriptionsPrintoutProps = { + medicationrequests: Array; +}; + +const PrescriptionsPrintout: React.FC = ({ medicationrequests }) => { + const { t } = useTranslation(); + return ( + + {/* + + Patient Name Here + Facility name here + + */} + + {medicationrequests?.map((request, index) => { + const medicationEvent = request.request; + const dosageInstruction: DosageInstruction = getDosageInstruction(medicationEvent.dosageInstruction); + const quantity: Quantity = getQuantity(medicationEvent); + const refillsAllowed: number = getRefillsAllowed(medicationEvent); + return ( +
+ + + {getMedicationDisplay(getMedicationReferenceOrCodeableConcept(medicationEvent))} + + + + {dosageInstruction && ( + + + {t('dose', 'Dose').toUpperCase()} + {': '} + + {dosageInstruction?.doseAndRate?.map((doseAndRate, index) => { + return ( + + {doseAndRate?.doseQuantity?.value} {doseAndRate?.doseQuantity?.unit} + + ); + })} + {' '} + — {dosageInstruction?.route?.text} — {dosageInstruction?.timing?.code?.text}{' '} + {dosageInstruction?.timing?.repeat?.duration + ? 'for ' + + dosageInstruction?.timing?.repeat?.duration + + ' ' + + dosageInstruction?.timing?.repeat?.durationUnit + : ' '} + + + )} + {quantity && ( + + + {t('quantity', 'Quantity')} + {': '} + + {quantity.value} {quantity.unit} + + + + )} + + {(refillsAllowed || refillsAllowed === 0) && ( + + + {t('refills', 'Refills')} + {': '} {refillsAllowed} + + + )} + {dosageInstruction?.text && ( + + {dosageInstruction.text} + + )} + {dosageInstruction?.additionalInstruction?.length > 0 && ( + + {dosageInstruction?.additionalInstruction[0].text} + + )} +
+ ); + })} +
+
+ ); +}; + +export default PrescriptionsPrintout; diff --git a/src/print-prescription/print-prescription.scss b/src/print-prescription/print-prescription.scss new file mode 100644 index 0000000..e3eb303 --- /dev/null +++ b/src/print-prescription/print-prescription.scss @@ -0,0 +1,12 @@ +@use '@carbon/layout'; +@use '@carbon/type'; +@use '@carbon/colors'; + +.title { + @include type.type-style('heading-02'); +} + +.btnSet { + width: 100%; + background-color: red; +} diff --git a/src/routes.json b/src/routes.json index ecde740..ab89aa9 100644 --- a/src/routes.json +++ b/src/routes.json @@ -72,5 +72,11 @@ "title": "Dispense prescription", "width": "wider" } + ], + "modals": [ + { + "name": "prescription-print-preview-modal", + "component": "printPrescriptionPreviewModal" + } ] -} +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 81dd862..206ef54 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2958,6 +2958,7 @@ __metadata: react-dom: "npm:^18.2.0" react-i18next: "npm:^11.7.0" react-router-dom: "npm:^6.3.0" + react-to-print: "npm:^2.14.13" rxjs: "npm:^6.6.7" swr: "npm:^2.2.5" typescript: "npm:^4.3.2" @@ -15370,6 +15371,16 @@ __metadata: languageName: node linkType: hard +"react-to-print@npm:^2.14.13": + version: 2.15.1 + resolution: "react-to-print@npm:2.15.1" + peerDependencies: + react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 + react-dom: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 + checksum: 10/0786fd5dd055b5b50f9a0dba99f0ccb30b9407b8e77748f5fc2557446ed0d0c65759e927cc2cdaa9e34f286838bbb230d3b0ba03d20b9754d0469fc4dac7c529 + languageName: node + linkType: hard + "react@npm:^18.1.0, react@npm:^18.2.0": version: 18.2.0 resolution: "react@npm:18.2.0" From 96b2537fbc390dff66b641e57e957885a90a09c5 Mon Sep 17 00:00:00 2001 From: Laurent Ouma Date: Thu, 21 Nov 2024 14:56:11 +0300 Subject: [PATCH 02/10] Print preview and printing and printing done --- .../prescription-print-preview.modal.tsx | 16 +- .../prescription-printout.component.tsx | 178 ++++++++++-------- .../print-prescription.scss | 25 ++- 3 files changed, 129 insertions(+), 90 deletions(-) diff --git a/src/print-prescription/prescription-print-preview.modal.tsx b/src/print-prescription/prescription-print-preview.modal.tsx index d12dac6..99457f9 100644 --- a/src/print-prescription/prescription-print-preview.modal.tsx +++ b/src/print-prescription/prescription-print-preview.modal.tsx @@ -1,19 +1,19 @@ -import React, { useRef, useState } from 'react'; import { Button, + ButtonSet, InlineLoading, + InlineNotification, ModalBody, ModalFooter, ModalHeader, - ButtonSet, - InlineNotification, } from '@carbon/react'; -import styles from './print-prescription.scss'; -import { useTranslation } from 'react-i18next'; -import PrescriptionsPrintout from './prescription-printout.component'; -import { usePrescriptionDetails } from '../medication-request/medication-request.resource'; import { ErrorState } from '@openmrs/esm-framework'; +import React, { useRef, useState } from 'react'; +import { useTranslation } from 'react-i18next'; import { useReactToPrint } from 'react-to-print'; +import { usePrescriptionDetails } from '../medication-request/medication-request.resource'; +import PrescriptionsPrintout from './prescription-printout.component'; +import styles from './print-prescription.scss'; type PrescriptionPrintPreviewModalProps = { onClose: () => void; @@ -32,7 +32,6 @@ const PrescriptionPrintPreviewModal: React.FC(null); const [printError, setPrintError] = useState(null); - const handlePrint = useReactToPrint({ content: () => componentRef.current, onBeforeGetContent: () => { @@ -41,6 +40,7 @@ const PrescriptionPrintPreviewModal: React.FC { setPrintError(t('printError', 'An error occurred while printing. Please try again.')); }, + copyStyles: true, }); return ( diff --git a/src/print-prescription/prescription-printout.component.tsx b/src/print-prescription/prescription-printout.component.tsx index 32a6165..7201198 100644 --- a/src/print-prescription/prescription-printout.component.tsx +++ b/src/print-prescription/prescription-printout.component.tsx @@ -1,4 +1,7 @@ +import { Layer, StructuredListBody, StructuredListCell, StructuredListRow, StructuredListWrapper } from '@carbon/react'; +import { formatDate, parseDate, useSession } from '@openmrs/esm-framework'; import React from 'react'; +import { useTranslation } from 'react-i18next'; import { type DosageInstruction, type MedicationRequestBundle, type Quantity } from '../types'; import { getDosageInstruction, @@ -7,13 +10,7 @@ import { getQuantity, getRefillsAllowed, } from '../utils'; -import { - StructuredListWrapper, - StructuredListBody, - StructuredListRow, - StructuredListCell, -} from '@carbon/react'; -import { useTranslation } from 'react-i18next'; +import styles from './print-prescription.scss'; type PrescriptionsPrintoutProps = { medicationrequests: Array; @@ -21,87 +18,106 @@ type PrescriptionsPrintoutProps = { const PrescriptionsPrintout: React.FC = ({ medicationrequests }) => { const { t } = useTranslation(); + const { + sessionLocation: { display: facilityName }, + } = useSession(); + const patient = medicationrequests[0]?.request?.subject; return ( - - {/* + + + {/* Patient Name Here Facility name here */} - - {medicationrequests?.map((request, index) => { - const medicationEvent = request.request; - const dosageInstruction: DosageInstruction = getDosageInstruction(medicationEvent.dosageInstruction); - const quantity: Quantity = getQuantity(medicationEvent); - const refillsAllowed: number = getRefillsAllowed(medicationEvent); - return ( -
- - - {getMedicationDisplay(getMedicationReferenceOrCodeableConcept(medicationEvent))} - - - - {dosageInstruction && ( - - - {t('dose', 'Dose').toUpperCase()} - {': '} - - {dosageInstruction?.doseAndRate?.map((doseAndRate, index) => { - return ( - - {doseAndRate?.doseQuantity?.value} {doseAndRate?.doseQuantity?.unit} - - ); - })} - {' '} - — {dosageInstruction?.route?.text} — {dosageInstruction?.timing?.code?.text}{' '} - {dosageInstruction?.timing?.repeat?.duration - ? 'for ' + - dosageInstruction?.timing?.repeat?.duration + - ' ' + - dosageInstruction?.timing?.repeat?.durationUnit - : ' '} - - - )} - {quantity && ( - - - {t('quantity', 'Quantity')} - {': '} - - {quantity.value} {quantity.unit} - - - - )} - - {(refillsAllowed || refillsAllowed === 0) && ( - - - {t('refills', 'Refills')} - {': '} {refillsAllowed} - - - )} - {dosageInstruction?.text && ( - - {dosageInstruction.text} - - )} - {dosageInstruction?.additionalInstruction?.length > 0 && ( - - {dosageInstruction?.additionalInstruction[0].text} - + + + +
+
+

Prescription Instructions

+ {patient && ( +

+ {patient.display + .match(/^([A-Za-z\s]+)\s\(/) + ?.at(1) + ?.toUpperCase()} +

)} -
- ); - })} -
-
+
+
+ + + {medicationrequests?.map((request, index) => { + const medicationEvent = request.request; + const dosageInstruction: DosageInstruction = getDosageInstruction(medicationEvent.dosageInstruction); + const quantity: Quantity = getQuantity(medicationEvent); + const refillsAllowed: number = getRefillsAllowed(medicationEvent); + return ( +
+ {dosageInstruction && ( + + +

+ + {getMedicationDisplay(getMedicationReferenceOrCodeableConcept(medicationEvent))} + +

+
+

+ {t('dose', 'Dose')} + {': '} + + {dosageInstruction?.doseAndRate?.map((doseAndRate, index) => { + return ( + + {doseAndRate?.doseQuantity?.value} {doseAndRate?.doseQuantity?.unit} + + ); + })} + {' '} + — {dosageInstruction?.route?.text} — {dosageInstruction?.timing?.code?.text}{' '} + {dosageInstruction?.timing?.repeat?.duration + ? 'for ' + + dosageInstruction?.timing?.repeat?.duration + + ' ' + + dosageInstruction?.timing?.repeat?.durationUnit + : ' '} + {quantity && ( +

+ {t('quantity', 'Quantity')} + {': '} + + {quantity.value} {quantity.unit} + +

+ )} +

+

+ {t('datePrescribed', 'Date prescribed')} + {': '} {formatDate(parseDate((request.request as any).authoredOn))} +

+ {(refillsAllowed || refillsAllowed === 0) && ( +

+ {t('refills', 'Refills')} + {': '} {refillsAllowed} +

+ )} + {dosageInstruction?.text &&

{dosageInstruction.text}

} + {dosageInstruction?.additionalInstruction?.length > 0 && ( +

{dosageInstruction?.additionalInstruction[0].text}

+ )} +
+
+ )} +
+ ); + })} +

{facilityName}

+ +
+ ); }; diff --git a/src/print-prescription/print-prescription.scss b/src/print-prescription/print-prescription.scss index e3eb303..5947119 100644 --- a/src/print-prescription/print-prescription.scss +++ b/src/print-prescription/print-prescription.scss @@ -8,5 +8,28 @@ .btnSet { width: 100%; - background-color: red; +} + +.faintText { + color: colors.$gray-50; + @include type.type-style('heading-01'); +} + +.printOutContainer { + background-color: colors.$gray-20; + padding: layout.$spacing-05; + width: 320px; +} + +.printoutTitle { + @include type.type-style('heading-03'); + text-align: center; + font-weight: bold; +} + +.facilityName { + color: colors.$gray-50; + text-align: end; + padding: layout.$spacing-05 0 layout.$spacing-05 0; + @include type.type-style('label-01'); } From 878ef44faf6b72623d26e13fd3af1d465aab8c67 Mon Sep 17 00:00:00 2001 From: Laurent Ouma Date: Wed, 27 Nov 2024 19:15:35 +0300 Subject: [PATCH 03/10] Added ability to select prescriptions to print --- .../prescription-actions.component.tsx | 23 +++ .../prescription-details.component.tsx | 4 +- .../prescription-tab-panel.component.tsx | 9 +- src/prescriptions/prescriptions-actions.scss | 15 ++ .../prescription-print-action.component.tsx | 8 +- .../prescription-print-preview.modal.tsx | 16 ++- .../prescription-printout.component.tsx | 133 +++++++++--------- .../print-prescription.scss | 8 ++ .../printable-prescriptions.component.tsx | 50 +++++++ 9 files changed, 181 insertions(+), 85 deletions(-) create mode 100644 src/prescriptions/prescription-actions.component.tsx create mode 100644 src/prescriptions/prescriptions-actions.scss create mode 100644 src/print-prescription/printable-prescriptions.component.tsx diff --git a/src/prescriptions/prescription-actions.component.tsx b/src/prescriptions/prescription-actions.component.tsx new file mode 100644 index 0000000..bde089b --- /dev/null +++ b/src/prescriptions/prescription-actions.component.tsx @@ -0,0 +1,23 @@ +import React from 'react'; +import PrescriptionPrintAction from '../print-prescription/prescription-print-action.component'; +import styles from './prescriptions-actions.scss'; +import { Layer } from '@carbon/react'; +type PrescriptionsActionsFooterProps = { + encounterUuid: string; + patientUuid: string; +}; + +const PrescriptionsActionsFooter: React.FC = ({ encounterUuid, patientUuid }) => { + return ( + +
+ {/* Left buttons */} + +
+ +
{/* Right buttons */}
+
+ ); +}; + +export default PrescriptionsActionsFooter; diff --git a/src/prescriptions/prescription-details.component.tsx b/src/prescriptions/prescription-details.component.tsx index fadeb4b..04f9f2a 100644 --- a/src/prescriptions/prescription-details.component.tsx +++ b/src/prescriptions/prescription-details.component.tsx @@ -11,6 +11,7 @@ import { usePatientAllergies, usePrescriptionDetails } from '../medication-reque import ActionButtons from '../components/action-buttons.component'; import MedicationEvent from '../components/medication-event.component'; import styles from './prescription-details.scss'; +import PrescriptionsActionsFooter from './prescription-actions.component'; const PrescriptionDetails: React.FC<{ encounterUuid: string; @@ -89,9 +90,7 @@ const PrescriptionDetails: React.FC<{ )} -
{t('prescribed', 'Prescribed')}
- {isLoading && } {isError &&

{t('error', 'Error')}

} {medicationRequestBundles && @@ -113,6 +112,7 @@ const PrescriptionDetails: React.FC<{ ); })} + ); }; diff --git a/src/prescriptions/prescription-tab-panel.component.tsx b/src/prescriptions/prescription-tab-panel.component.tsx index 75cd574..48def7f 100644 --- a/src/prescriptions/prescription-tab-panel.component.tsx +++ b/src/prescriptions/prescription-tab-panel.component.tsx @@ -24,7 +24,6 @@ import { usePrescriptionsTable } from '../medication-request/medication-request. import PatientInfoCell from '../patient/patient-info-cell.component'; import PrescriptionExpanded from './prescription-expanded.component'; import styles from './prescriptions.scss'; -import PrescriptionPrintAction from '../print-prescription/prescription-print-action.component'; interface PrescriptionTabPanelProps { searchTerm: string; @@ -55,7 +54,6 @@ const PrescriptionTabPanel: React.FC = ({ searchTerm, { header: t('drugs', 'Drugs'), key: 'drugs' }, { header: t('lastDispenser', 'Last dispenser'), key: 'lastDispenser' }, { header: t('status', 'Status'), key: 'status' }, - { header: t('actions', 'Actions'), key: 'actions' }, ]; // add the locations column, if enabled @@ -69,11 +67,6 @@ const PrescriptionTabPanel: React.FC = ({ searchTerm, setNextOffSet(0); }, [searchTerm]); - const rows = prescriptionsTableRows?.map((row) => ({ - ...row, - actions: , - })); - return (
@@ -81,7 +74,7 @@ const PrescriptionTabPanel: React.FC = ({ searchTerm, {error &&

Error

} {prescriptionsTableRows && ( <> - + {({ rows, headers, getExpandHeaderProps, getHeaderProps, getRowProps, getTableProps }) => (
diff --git a/src/prescriptions/prescriptions-actions.scss b/src/prescriptions/prescriptions-actions.scss new file mode 100644 index 0000000..e5a17bc --- /dev/null +++ b/src/prescriptions/prescriptions-actions.scss @@ -0,0 +1,15 @@ +@use '@carbon/layout'; +@use '@carbon/type'; +@use '@carbon/colors'; + +.actionsContainer { + flex-direction: row; + display: flex; + justify-content: space-between; +} + +.actionCluster { + gap: layout.$spacing-03; + display: flex; + flex-direction: row; +} diff --git a/src/print-prescription/prescription-print-action.component.tsx b/src/print-prescription/prescription-print-action.component.tsx index f48e08a..85ea86d 100644 --- a/src/print-prescription/prescription-print-action.component.tsx +++ b/src/print-prescription/prescription-print-action.component.tsx @@ -7,7 +7,6 @@ import { showModal } from '@openmrs/esm-framework'; type PrescriptionPrintActionProps = { encounterUuid: string; patientUuid: string; - status: string; }; const PrescriptionPrintAction: React.FC = ({ encounterUuid, patientUuid }) => { @@ -17,17 +16,16 @@ const PrescriptionPrintAction: React.FC = ({ encou ); }; diff --git a/src/print-prescription/prescription-print-preview.modal.tsx b/src/print-prescription/prescription-print-preview.modal.tsx index 99457f9..ebaae4b 100644 --- a/src/print-prescription/prescription-print-preview.modal.tsx +++ b/src/print-prescription/prescription-print-preview.modal.tsx @@ -14,6 +14,7 @@ import { useReactToPrint } from 'react-to-print'; import { usePrescriptionDetails } from '../medication-request/medication-request.resource'; import PrescriptionsPrintout from './prescription-printout.component'; import styles from './print-prescription.scss'; +import PrintablePrescriptionsSelector from './printable-prescriptions.component'; type PrescriptionPrintPreviewModalProps = { onClose: () => void; @@ -30,6 +31,7 @@ const PrescriptionPrintPreviewModal: React.FC { const { t } = useTranslation(); const { medicationRequestBundles, isError, isLoading } = usePrescriptionDetails(encounterUuid); + const [excludedPrescription, setExcludedPrescriptions] = useState([]); const componentRef = useRef(null); const [printError, setPrintError] = useState(null); const handlePrint = useReactToPrint({ @@ -58,8 +60,18 @@ const PrescriptionPrintPreviewModal: React.FC} {!isLoading && medicationRequestBundles?.length > 0 && ( -
- +
+ +
+ +
)} {printError && ( diff --git a/src/print-prescription/prescription-printout.component.tsx b/src/print-prescription/prescription-printout.component.tsx index 7201198..3e31c3b 100644 --- a/src/print-prescription/prescription-printout.component.tsx +++ b/src/print-prescription/prescription-printout.component.tsx @@ -14,9 +14,10 @@ import styles from './print-prescription.scss'; type PrescriptionsPrintoutProps = { medicationrequests: Array; + excludedPrescription: Array; }; -const PrescriptionsPrintout: React.FC = ({ medicationrequests }) => { +const PrescriptionsPrintout: React.FC = ({ medicationrequests, excludedPrescription }) => { const { t } = useTranslation(); const { sessionLocation: { display: facilityName }, @@ -25,12 +26,6 @@ const PrescriptionsPrintout: React.FC = ({ medicatio return ( - {/* - - Patient Name Here - Facility name here - - */} @@ -49,71 +44,73 @@ const PrescriptionsPrintout: React.FC = ({ medicatio
- {medicationrequests?.map((request, index) => { - const medicationEvent = request.request; - const dosageInstruction: DosageInstruction = getDosageInstruction(medicationEvent.dosageInstruction); - const quantity: Quantity = getQuantity(medicationEvent); - const refillsAllowed: number = getRefillsAllowed(medicationEvent); - return ( -
- {dosageInstruction && ( - - -

- - {getMedicationDisplay(getMedicationReferenceOrCodeableConcept(medicationEvent))} - -

-
-

- {t('dose', 'Dose')} - {': '} - - {dosageInstruction?.doseAndRate?.map((doseAndRate, index) => { - return ( - - {doseAndRate?.doseQuantity?.value} {doseAndRate?.doseQuantity?.unit} + {medicationrequests + ?.filter((req) => !excludedPrescription.includes(req.request.id)) + ?.map((request, index) => { + const medicationEvent = request.request; + const dosageInstruction: DosageInstruction = getDosageInstruction(medicationEvent.dosageInstruction); + const quantity: Quantity = getQuantity(medicationEvent); + const refillsAllowed: number = getRefillsAllowed(medicationEvent); + return ( +

+ {dosageInstruction && ( + + +

+ + {getMedicationDisplay(getMedicationReferenceOrCodeableConcept(medicationEvent))} + +

+
+

+ {t('dose', 'Dose')} + {': '} + + {dosageInstruction?.doseAndRate?.map((doseAndRate, index) => { + return ( + + {doseAndRate?.doseQuantity?.value} {doseAndRate?.doseQuantity?.unit} + + ); + })} + {' '} + — {dosageInstruction?.route?.text} — {dosageInstruction?.timing?.code?.text}{' '} + {dosageInstruction?.timing?.repeat?.duration + ? 'for ' + + dosageInstruction?.timing?.repeat?.duration + + ' ' + + dosageInstruction?.timing?.repeat?.durationUnit + : ' '} + {quantity && ( +

+ {t('quantity', 'Quantity')} + {': '} + + {quantity.value} {quantity.unit} - ); - })} - {' '} - — {dosageInstruction?.route?.text} — {dosageInstruction?.timing?.code?.text}{' '} - {dosageInstruction?.timing?.repeat?.duration - ? 'for ' + - dosageInstruction?.timing?.repeat?.duration + - ' ' + - dosageInstruction?.timing?.repeat?.durationUnit - : ' '} - {quantity && ( +

+ )} +

+

+ {t('datePrescribed', 'Date prescribed')} + {': '} {formatDate(parseDate((request.request as any).authoredOn))} +

+ {(refillsAllowed || refillsAllowed === 0) && (

- {t('quantity', 'Quantity')} - {': '} - - {quantity.value} {quantity.unit} - + {t('refills', 'Refills')} + {': '} {refillsAllowed}

)} -

-

- {t('datePrescribed', 'Date prescribed')} - {': '} {formatDate(parseDate((request.request as any).authoredOn))} -

- {(refillsAllowed || refillsAllowed === 0) && ( -

- {t('refills', 'Refills')} - {': '} {refillsAllowed} -

- )} - {dosageInstruction?.text &&

{dosageInstruction.text}

} - {dosageInstruction?.additionalInstruction?.length > 0 && ( -

{dosageInstruction?.additionalInstruction[0].text}

- )} -
-
- )} -
- ); - })} + {dosageInstruction?.text &&

{dosageInstruction.text}

} + {dosageInstruction?.additionalInstruction?.length > 0 && ( +

{dosageInstruction?.additionalInstruction[0].text}

+ )} +
+
+ )} +
+ ); + })}

{facilityName}

diff --git a/src/print-prescription/print-prescription.scss b/src/print-prescription/print-prescription.scss index 5947119..4df0abf 100644 --- a/src/print-prescription/print-prescription.scss +++ b/src/print-prescription/print-prescription.scss @@ -33,3 +33,11 @@ padding: layout.$spacing-05 0 layout.$spacing-05 0; @include type.type-style('label-01'); } + +.printoutSelectorRow { + display: flex; + flex-direction: row; + gap: layout.$spacing-05; + padding: 0 layout.$spacing-05 0 layout.$spacing-05; + justify-content: space-between; +} diff --git a/src/print-prescription/printable-prescriptions.component.tsx b/src/print-prescription/printable-prescriptions.component.tsx new file mode 100644 index 0000000..0e164cf --- /dev/null +++ b/src/print-prescription/printable-prescriptions.component.tsx @@ -0,0 +1,50 @@ +import React from 'react'; +import { type DosageInstruction, type MedicationRequestBundle } from '../types'; +import { getDosageInstruction, getMedicationDisplay, getMedicationReferenceOrCodeableConcept } from '../utils'; +import { Checkbox } from '@carbon/react'; +import { useTranslation } from 'react-i18next'; + +type PrintablePrescriptionsSelectorProps = { + medicationrequests: Array; + excludedPrescription: Array; + onExcludedPrescriptionChange: React.Dispatch>; +}; + +const PrintablePrescriptionsSelector: React.FC = ({ + medicationrequests, + onExcludedPrescriptionChange, + excludedPrescription, +}) => { + const { t } = useTranslation(); + return ( +
+

+ {t('selectPrescriptions', 'Check prescriptions to print')} +

+ {medicationrequests?.map((request, index) => { + const medicationEvent = request.request; + const dosageInstruction: DosageInstruction = getDosageInstruction(medicationEvent.dosageInstruction); + return ( +
+ {dosageInstruction && ( + { + if (checked) { + onExcludedPrescriptionChange(excludedPrescription.filter((id) => id !== medicationEvent.id)); + } else { + onExcludedPrescriptionChange([...excludedPrescription, medicationEvent.id]); + } + }} + /> + )} +
+ ); + })} +
+ ); +}; + +export default PrintablePrescriptionsSelector; From fa1c6f79ad1164798fa92a809c220ee7ffb8f5ff Mon Sep 17 00:00:00 2001 From: Laurent Ouma Date: Mon, 2 Dec 2024 08:34:43 +0300 Subject: [PATCH 04/10] wraped free text under translation.Extracted patient display processing logic into utility function --- .../prescription-printout.component.tsx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/print-prescription/prescription-printout.component.tsx b/src/print-prescription/prescription-printout.component.tsx index 3e31c3b..d41899b 100644 --- a/src/print-prescription/prescription-printout.component.tsx +++ b/src/print-prescription/prescription-printout.component.tsx @@ -23,6 +23,7 @@ const PrescriptionsPrintout: React.FC = ({ medicatio sessionLocation: { display: facilityName }, } = useSession(); const patient = medicationrequests[0]?.request?.subject; + const extractpatientName = (display: string) => display.match(/^([A-Za-z\s]+)\s\(/)?.at(1); return ( @@ -31,13 +32,10 @@ const PrescriptionsPrintout: React.FC = ({ medicatio

-

Prescription Instructions

+

{t('prescriptionInstructions', 'Prescription Instructions')}

{patient && (

- {patient.display - .match(/^([A-Za-z\s]+)\s\(/) - ?.at(1) - ?.toUpperCase()} + {extractpatientName(patient.display)?.toUpperCase()}

)}
From 9d6fb9525080ae245b361a34a4f0ef9387059e80 Mon Sep 17 00:00:00 2001 From: Laurent Ouma Date: Tue, 3 Dec 2024 09:49:09 +0300 Subject: [PATCH 05/10] Added all addtional instructions and indicating not refillable for prescription that are not refillable --- .../prescription-printout.component.tsx | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/print-prescription/prescription-printout.component.tsx b/src/print-prescription/prescription-printout.component.tsx index d41899b..afc6ff9 100644 --- a/src/print-prescription/prescription-printout.component.tsx +++ b/src/print-prescription/prescription-printout.component.tsx @@ -72,9 +72,9 @@ const PrescriptionsPrintout: React.FC = ({ medicatio ); })} {' '} - — {dosageInstruction?.route?.text} — {dosageInstruction?.timing?.code?.text}{' '} + — {dosageInstruction?.route?.text} — {dosageInstruction?.timing?.code?.text} {dosageInstruction?.timing?.repeat?.duration - ? 'for ' + + ? ` ${t('for', 'for')} ` + dosageInstruction?.timing?.repeat?.duration + ' ' + dosageInstruction?.timing?.repeat?.durationUnit @@ -93,15 +93,21 @@ const PrescriptionsPrintout: React.FC = ({ medicatio {t('datePrescribed', 'Date prescribed')} {': '} {formatDate(parseDate((request.request as any).authoredOn))}

- {(refillsAllowed || refillsAllowed === 0) && ( -

- {t('refills', 'Refills')} - {': '} {refillsAllowed} -

- )} +

+ {t('refills', 'Refills')} + {': '}{' '} + + {refillsAllowed || refillsAllowed === 0 + ? refillsAllowed + : t('notRefillable', 'Not Refillable')} + +

+ {dosageInstruction?.text &&

{dosageInstruction.text}

} {dosageInstruction?.additionalInstruction?.length > 0 && ( -

{dosageInstruction?.additionalInstruction[0].text}

+

+ {dosageInstruction?.additionalInstruction.map((instruction) => instruction.text).join(', ')} +

)}
From 393c60b1e0c1e6876d20aa3d50082fc9b2870a99 Mon Sep 17 00:00:00 2001 From: Laurent Ouma Date: Tue, 3 Dec 2024 23:12:13 +0300 Subject: [PATCH 06/10] Updated display for none refillable prescriptions to no refills --- src/print-prescription/prescription-printout.component.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/print-prescription/prescription-printout.component.tsx b/src/print-prescription/prescription-printout.component.tsx index afc6ff9..57e9455 100644 --- a/src/print-prescription/prescription-printout.component.tsx +++ b/src/print-prescription/prescription-printout.component.tsx @@ -97,9 +97,7 @@ const PrescriptionsPrintout: React.FC = ({ medicatio {t('refills', 'Refills')} {': '}{' '} - {refillsAllowed || refillsAllowed === 0 - ? refillsAllowed - : t('notRefillable', 'Not Refillable')} + {refillsAllowed || refillsAllowed === 0 ? refillsAllowed : t('notRefills', 'No Refills')}

From c995ff46c383a9a64b8ad987b4b500d02546e3ec Mon Sep 17 00:00:00 2001 From: Laurent Ouma Date: Tue, 10 Dec 2024 21:13:28 +0300 Subject: [PATCH 07/10] Captitalize patient name from styles, refactored patient name extractor from display --- .../prescription-printout.component.tsx | 8 +++++--- src/print-prescription/print-prescription.scss | 5 +++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/print-prescription/prescription-printout.component.tsx b/src/print-prescription/prescription-printout.component.tsx index 57e9455..f3380c1 100644 --- a/src/print-prescription/prescription-printout.component.tsx +++ b/src/print-prescription/prescription-printout.component.tsx @@ -1,5 +1,6 @@ import { Layer, StructuredListBody, StructuredListCell, StructuredListRow, StructuredListWrapper } from '@carbon/react'; import { formatDate, parseDate, useSession } from '@openmrs/esm-framework'; +import classNames from 'classnames'; import React from 'react'; import { useTranslation } from 'react-i18next'; import { type DosageInstruction, type MedicationRequestBundle, type Quantity } from '../types'; @@ -23,7 +24,8 @@ const PrescriptionsPrintout: React.FC = ({ medicatio sessionLocation: { display: facilityName }, } = useSession(); const patient = medicationrequests[0]?.request?.subject; - const extractpatientName = (display: string) => display.match(/^([A-Za-z\s]+)\s\(/)?.at(1); + + const extractpatientName = (display: string) => (display.includes('(') ? display.split('(')[0] : display); return ( @@ -34,8 +36,8 @@ const PrescriptionsPrintout: React.FC = ({ medicatio

{t('prescriptionInstructions', 'Prescription Instructions')}

{patient && ( -

- {extractpatientName(patient.display)?.toUpperCase()} +

+ {extractpatientName(patient.display)}

)}
diff --git a/src/print-prescription/print-prescription.scss b/src/print-prescription/print-prescription.scss index 4df0abf..2bec901 100644 --- a/src/print-prescription/print-prescription.scss +++ b/src/print-prescription/print-prescription.scss @@ -41,3 +41,8 @@ padding: 0 layout.$spacing-05 0 layout.$spacing-05; justify-content: space-between; } + +.patientName { + text-transform: capitalize; + text-align: center; +} From 7dba4da01571ed8b6e1dfefd54bd92295b200d77 Mon Sep 17 00:00:00 2001 From: Laurent Ouma Date: Tue, 10 Dec 2024 21:20:21 +0300 Subject: [PATCH 08/10] Fixed class name spelling --- src/print-prescription/prescription-printout.component.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/print-prescription/prescription-printout.component.tsx b/src/print-prescription/prescription-printout.component.tsx index f3380c1..cf5ea76 100644 --- a/src/print-prescription/prescription-printout.component.tsx +++ b/src/print-prescription/prescription-printout.component.tsx @@ -36,7 +36,7 @@ const PrescriptionsPrintout: React.FC = ({ medicatio

{t('prescriptionInstructions', 'Prescription Instructions')}

{patient && ( -

+

{extractpatientName(patient.display)}

)} From 3e4b478d2342ece4e0ed61e4f0bba6eb3f0a24a6 Mon Sep 17 00:00:00 2001 From: Laurent Ouma Date: Tue, 17 Dec 2024 00:00:23 +0300 Subject: [PATCH 09/10] fixed variable names casing and naming, stylings and extracting of handlers funtions and wrapping them in useCallback for persisting avoiding reconstructions --- .../prescription-actions.component.tsx | 6 ++-- ...actions.scss => prescription-actions.scss} | 0 .../prescription-print-action.component.tsx | 22 +++++++-------- .../prescription-print-preview.modal.tsx | 12 ++++---- .../prescription-printout.component.tsx | 6 ++-- .../print-prescription.scss | 2 +- .../printable-prescriptions.component.tsx | 28 +++++++++++-------- src/types.ts | 1 + 8 files changed, 40 insertions(+), 37 deletions(-) rename src/prescriptions/{prescriptions-actions.scss => prescription-actions.scss} (100%) diff --git a/src/prescriptions/prescription-actions.component.tsx b/src/prescriptions/prescription-actions.component.tsx index bde089b..f5a5ab4 100644 --- a/src/prescriptions/prescription-actions.component.tsx +++ b/src/prescriptions/prescription-actions.component.tsx @@ -1,6 +1,6 @@ import React from 'react'; import PrescriptionPrintAction from '../print-prescription/prescription-print-action.component'; -import styles from './prescriptions-actions.scss'; +import styles from './prescription-actions.scss'; import { Layer } from '@carbon/react'; type PrescriptionsActionsFooterProps = { encounterUuid: string; @@ -10,12 +10,12 @@ type PrescriptionsActionsFooterProps = { const PrescriptionsActionsFooter: React.FC = ({ encounterUuid, patientUuid }) => { return ( -
+
{/* Left buttons */}
-
{/* Right buttons */}
+
{/* Right buttons */}
); }; diff --git a/src/prescriptions/prescriptions-actions.scss b/src/prescriptions/prescription-actions.scss similarity index 100% rename from src/prescriptions/prescriptions-actions.scss rename to src/prescriptions/prescription-actions.scss diff --git a/src/print-prescription/prescription-print-action.component.tsx b/src/print-prescription/prescription-print-action.component.tsx index 85ea86d..29c1529 100644 --- a/src/print-prescription/prescription-print-action.component.tsx +++ b/src/print-prescription/prescription-print-action.component.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useCallback } from 'react'; import { Button } from '@carbon/react'; import { Printer } from '@carbon/react/icons'; import { useTranslation } from 'react-i18next'; @@ -12,18 +12,16 @@ type PrescriptionPrintActionProps = { const PrescriptionPrintAction: React.FC = ({ encounterUuid, patientUuid }) => { const { t } = useTranslation(); + const handleClick = useCallback(() => { + const dispose = showModal('prescription-print-preview-modal', { + onClose: () => dispose(), + encounterUuid, + patientUuid, + }); + }, [encounterUuid, patientUuid]); + return ( - ); diff --git a/src/print-prescription/prescription-print-preview.modal.tsx b/src/print-prescription/prescription-print-preview.modal.tsx index ebaae4b..9983b5c 100644 --- a/src/print-prescription/prescription-print-preview.modal.tsx +++ b/src/print-prescription/prescription-print-preview.modal.tsx @@ -7,7 +7,7 @@ import { ModalFooter, ModalHeader, } from '@carbon/react'; -import { ErrorState } from '@openmrs/esm-framework'; +import { ErrorState, getCoreTranslation } from '@openmrs/esm-framework'; import React, { useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useReactToPrint } from 'react-to-print'; @@ -31,7 +31,7 @@ const PrescriptionPrintPreviewModal: React.FC { const { t } = useTranslation(); const { medicationRequestBundles, isError, isLoading } = usePrescriptionDetails(encounterUuid); - const [excludedPrescription, setExcludedPrescriptions] = useState([]); + const [excludedPrescriptions, setExcludedPrescriptions] = useState([]); const componentRef = useRef(null); const [printError, setPrintError] = useState(null); const handlePrint = useReactToPrint({ @@ -62,14 +62,14 @@ const PrescriptionPrintPreviewModal: React.FC 0 && (
@@ -81,7 +81,7 @@ const PrescriptionPrintPreviewModal: React.FC