Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(feat) O3-4219: Add ability to print patient prescriptions #125

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": "*",
Expand Down Expand Up @@ -79,7 +80,7 @@
"eslint-plugin-unused-imports": "^2.0.0",
"husky": "^6.0.0",
"i18next": "^21.10.0",
"i18next-parser": "^6.6.0",
"i18next-parser": "^9.0.2",
"identity-obj-proxy": "^3.0.0",
"jest": "^28.1.3",
"jest-cli": "^28.1.3",
Expand Down
2 changes: 1 addition & 1 deletion src/dispensing-tiles/dispensing-tiles.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import styles from './dispensing-tiles.scss';

const DispensingTiles: React.FC = () => {
const { t } = useTranslation();
const { metrics, isError, isLoading } = useMetrics();
const { metrics, error, isLoading } = useMetrics();

if (isLoading) {
return <DataTableSkeleton role="progressbar" />;
Expand Down
2 changes: 1 addition & 1 deletion src/dispensing-tiles/dispensing-tiles.resource.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export function useMetrics() {

return {
metrics: metrics,
isError: error,
error,
isLoading: !data && !error,
};
}
Expand Down
22 changes: 0 additions & 22 deletions src/dispensing.test.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,3 @@
/**
* This is the root test for this page. It simply checks that the page
* renders. If the components of your page are highly interdependent,
* (e.g., if the `Hello` component had state that communicated
* information between `Greeter` and `PatientGetter`) then you might
* want to do most of your testing here. If those components are
* instead quite independent (as is the case in this example), then
* it would make more sense to test those components independently.
*
* The key thing to remember, always, is: write tests that behave like
* users. They should *look* for elements by their visual
* characteristics, *interact* with them, and (mostly) *assert* based
* on things that would be visually apparent to a user.
*
* To learn more about how we do testing, see the following resources:
* https://kentcdodds.com/blog/how-to-know-what-to-test
* https://kentcdodds.com/blog/testing-implementation-details
* https://kentcdodds.com/blog/common-mistakes-with-react-testing-library
*
* Kent C. Dodds is the inventor of `@testing-library`:
* https://testing-library.com/docs/guiding-principles
*/
import React from 'react';
import { render } from '@testing-library/react';
import Dispensing from './dispensing.component';
Expand Down
12 changes: 5 additions & 7 deletions src/history/history-and-comments.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ const HistoryAndComments: React.FC<{
const { t } = useTranslation();
const session = useSession();
const config = useConfig<PharmacyConfig>();
const { medicationRequestBundles, prescriptionDate, isError, isLoading } = usePrescriptionDetails(
const { medicationRequestBundles, prescriptionDate, error, isLoading } = usePrescriptionDetails(
encounterUuid,
config.refreshInterval,
);
Expand All @@ -60,7 +60,7 @@ const HistoryAndComments: React.FC<{
(performer) =>
performer?.actor?.reference?.length > 1 &&
performer.actor.reference.split('/')[1] === session.currentProvider.uuid,
) != null
) !== null
) {
return true;
}
Expand Down Expand Up @@ -133,7 +133,7 @@ const HistoryAndComments: React.FC<{
props: Record<string, unknown>;
};
const workspaceTitle = getWorkspaceTitle(medicationDispense);
launchWorkspace(workspaceName, { workspaceTitle, ...props, });
launchWorkspace(workspaceName, { workspaceTitle, ...props });
};

if (!editable && !deletable) {
Expand All @@ -145,9 +145,7 @@ const HistoryAndComments: React.FC<{
flipped={true}
className={styles.medicationEventActionMenu}>
{editable && (
<OverflowMenuItem
onClick={handleEdit}
itemText={t('editRecord', 'Edit Record')}></OverflowMenuItem>
<OverflowMenuItem onClick={handleEdit} itemText={t('editRecord', 'Edit record')}></OverflowMenuItem>
)}
{deletable && (
<OverflowMenuItem
Expand Down Expand Up @@ -215,7 +213,7 @@ const HistoryAndComments: React.FC<{
return (
<div className={styles.historyAndCommentsContainer}>
{isLoading && <DataTableSkeleton role="progressbar" />}
{isError && <p>{t('error', 'Error')}</p>}
{error && <p>{t('error', 'Error')}</p>}
{medicationRequestBundles &&
medicationRequestBundles
.flatMap((medicationDispenseBundle) => medicationDispenseBundle.dispenses)
Expand Down
13 changes: 8 additions & 5 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');

Expand All @@ -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
Expand All @@ -37,7 +38,9 @@ export const pauseActionButton = getSyncLifecycle(PauseActionButton, options);
// Dispensing workspace
// t('closePrescription', 'Close prescription')
export const closeDispenseWorkspace = getAsyncLifecycle(() => import('./forms/close-dispense-form.workspace'), options);
// t('dispensDPrescription', 'Dispense prescription')
// t('dispensePrescription', 'Dispense prescription')
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);
2 changes: 1 addition & 1 deletion src/location/location.resource.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export function useLocationForFiltering(config: PharmacyConfig) {

return {
filterLocations,
isError: error,
error,
isLoading: !filterLocations && !error,
};
}
4 changes: 2 additions & 2 deletions src/medication-dispense/medication-dispense.resource.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { fhirBaseUrl, restBaseUrl, openmrsFetch, type Session } from '@openmrs/esm-framework';
import dayjs from 'dayjs';
import useSWR from 'swr';
import { fhirBaseUrl, restBaseUrl, openmrsFetch, type Session } from '@openmrs/esm-framework';
import {
type MedicationDispense,
type MedicationDispenseStatus,
Expand Down Expand Up @@ -51,8 +51,8 @@ export function useOrderConfig() {
);
return {
orderConfigObject: data ? data.data : null,
error,
isLoading: !data && !error,
isError: error,
isValidating,
};
}
Expand Down
4 changes: 2 additions & 2 deletions src/medication-request/medication-request.resource.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ export function usePrescriptionDetails(encounterUuid: string, refreshInterval =
return {
medicationRequestBundles,
prescriptionDate,
isError: error,
error,
isLoading,
};
}
Expand All @@ -193,7 +193,7 @@ export function usePatientAllergies(patientUuid: string, refreshInterval) {
return {
allergies,
totalAllergies: data?.data.total,
isError: error,
error,
};
}

Expand Down
24 changes: 24 additions & 0 deletions src/prescriptions/prescription-actions.component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React from 'react';
import { Layer } from '@carbon/react';
import PrescriptionPrintAction from '../print-prescription/prescription-print-action.component';
import styles from './prescription-actions.scss';

type PrescriptionsActionsFooterProps = {
encounterUuid: string;
patientUuid: string;
};

const PrescriptionsActionsFooter: React.FC<PrescriptionsActionsFooterProps> = ({ encounterUuid, patientUuid }) => {
return (
<Layer className={styles.actionsContainer}>
<div className={styles.actionCluster}>
{/* Left buttons */}
<PrescriptionPrintAction encounterUuid={encounterUuid} patientUuid={patientUuid} />
</div>

<div className={styles.actionCluster}>{/* Right buttons */}</div>
</Layer>
);
};

export default PrescriptionsActionsFooter;
15 changes: 15 additions & 0 deletions src/prescriptions/prescription-actions.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
@use '@carbon/colors';
@use '@carbon/layout';
@use '@carbon/type';

.actionsContainer {
flex-direction: row;
display: flex;
justify-content: space-between;
}

.actionCluster {
gap: layout.$spacing-03;
display: flex;
flex-direction: row;
}
11 changes: 4 additions & 7 deletions src/prescriptions/prescription-details.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { PRIVILEGE_CREATE_DISPENSE } from '../constants';
import { usePatientAllergies, usePrescriptionDetails } from '../medication-request/medication-request.resource';
import ActionButtons from '../components/action-buttons.component';
import MedicationEvent from '../components/medication-event.component';
import PrescriptionsActionsFooter from './prescription-actions.component';
import styles from './prescription-details.scss';

const PrescriptionDetails: React.FC<{
Expand All @@ -20,10 +21,7 @@ const PrescriptionDetails: React.FC<{
const config = useConfig<PharmacyConfig>();
const [isAllergiesLoading, setAllergiesLoadingStatus] = useState(true);
const { allergies, totalAllergies } = usePatientAllergies(patientUuid, config.refreshInterval);
const { medicationRequestBundles, isError, isLoading } = usePrescriptionDetails(
encounterUuid,
config.refreshInterval,
);
const { medicationRequestBundles, error, isLoading } = usePrescriptionDetails(encounterUuid, config.refreshInterval);

useEffect(() => {
if (typeof totalAllergies == 'number') {
Expand Down Expand Up @@ -89,11 +87,9 @@ const PrescriptionDetails: React.FC<{
</div>
</Tile>
)}

<h5 style={{ paddingTop: '8px', paddingBottom: '8px', fontSize: '0.9rem' }}>{t('prescribed', 'Prescribed')}</h5>

{isLoading && <DataTableSkeleton role="progressbar" />}
{isError && <p>{t('error', 'Error')}</p>}
{error && <p>{t('error', 'Error')}</p>}
{medicationRequestBundles &&
medicationRequestBundles.map((bundle) => {
return (
Expand All @@ -113,6 +109,7 @@ const PrescriptionDetails: React.FC<{
</Tile>
);
})}
<PrescriptionsActionsFooter encounterUuid={encounterUuid} patientUuid={patientUuid} />
</div>
);
};
Expand Down
30 changes: 30 additions & 0 deletions src/print-prescription/prescription-print-action.component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React, { useCallback } 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;
};

const PrescriptionPrintAction: React.FC<PrescriptionPrintActionProps> = ({ encounterUuid, patientUuid }) => {
const { t } = useTranslation();

const handleClick = useCallback(() => {
const dispose = showModal('prescription-print-preview-modal', {
onClose: () => dispose(),
encounterUuid,
patientUuid,
});
}, [encounterUuid, patientUuid]);

return (
<Button renderIcon={Printer} iconDescription={t('print', 'Print')} onClick={handleClick} kind="ghost">
{t('printPrescriptions', 'Print prescriptions')}
</Button>
);
};

export default PrescriptionPrintAction;
Loading
Loading