Skip to content

Commit

Permalink
O3-3276: Add support for printing patient identification stickers
Browse files Browse the repository at this point in the history
  • Loading branch information
jnsereko committed May 28, 2024
1 parent 69a8604 commit e4a5e94
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 63 deletions.
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import React, { useCallback, useEffect, useRef, useState } from 'react';
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Button, ModalBody, ModalFooter, ModalHeader } from '@carbon/react';
import styles from './print-identifier-sticker.scss';
import { useReactToPrint } from 'react-to-print';
import { Column } from '@carbon/react';
import { Grid } from '@carbon/react';
import { type ConfigObject } from '../config-schema';
import { useConfig } from '@openmrs/esm-framework';
import { age, displayName, useConfig } from '@openmrs/esm-framework';

interface PrintIdentifierStickerProps {
patient: fhir.Patient;
Expand All @@ -17,7 +16,7 @@ const PrintIdentifierSticker: React.FC<PrintIdentifierStickerProps> = ({ patient
const { t } = useTranslation();
const [isPrinting, setIsPrinting] = useState(false);
const contentToPrintRef = useRef(null);
const { printIdentifierStickerFields, printIdentifierStickerSize } = useConfig<ConfigObject>();
const { printIdentifierStickerFields, printIdentifierStickerSize, excludePatientIdentifierCodeTypes } = useConfig();

const headerTitle = t('patientIdentifierSticker', 'Patient Identifier Sticker');

Expand All @@ -29,22 +28,38 @@ const PrintIdentifierSticker: React.FC<PrintIdentifierStickerProps> = ({ patient
}
}, [isPrinting]);

const calculateAge = (birthDate) => {
const ageDifMs = Date.now() - new Date(birthDate).getTime();
const ageDate = new Date(ageDifMs);
return Math.abs(ageDate.getUTCFullYear() - 1970);
};
const patientDetails = useMemo(() => {
const getGender = (gender: string): string => {
switch (gender) {
case 'male':
return t('male', 'Male');
case 'female':
return t('female', 'Female');
case 'other':
return t('other', 'Other');
case 'unknown':
return t('unknown', 'Unknown');
default:
return gender;
}
};

const patientDetails = {
id: patient?.id,
photo: patient?.photo,
name: patient?.name,
dateOfBirth: patient?.birthDate,
age: calculateAge(patient?.birthDate),
gender: patient?.gender,
address: patient?.address,
identifiers: patient?.identifier,
};
const identifiers =
patient?.identifier?.filter(
(identifier) => !excludePatientIdentifierCodeTypes?.uuids.includes(identifier.type.coding[0].code),
) ?? [];

return {
id: patient?.id,
photo: patient?.photo,
name: patient ? displayName(patient) : '',
dateOfBirth: patient.birthDate,
age: age(patient?.birthDate),
gender: getGender(patient?.gender),
address: patient?.address,
identifiers: identifiers?.length ? identifiers.map(({ value, type }) => ({ value, type })) : [],
};
}, [patient, t, excludePatientIdentifierCodeTypes?.uuids]);

const handleBeforeGetContent = useCallback(() => {
return new Promise((resolve) => {
Expand All @@ -70,7 +85,7 @@ const PrintIdentifierSticker: React.FC<PrintIdentifierStickerProps> = ({ patient

const handlePrint = useReactToPrint({
content: () => contentToPrintRef.current,
documentTitle: `${patientDetails.name[0].given}'s - ${headerTitle}`,
documentTitle: `${patientDetails.name} - ${headerTitle}`,
onBeforeGetContent: handleBeforeGetContent,
onAfterPrint: handleAfterPrint,
});
Expand Down Expand Up @@ -112,37 +127,29 @@ const PrintIdentifierSticker: React.FC<PrintIdentifierStickerProps> = ({ patient
<ModalHeader closeModal={closeModal} title={t('printIdentifierSticker', 'Print Identifier Sticker')} />
<ModalBody>
<div className={styles.stickerContainer} ref={contentToPrintRef}>
{printIdentifierStickerFields.includes('name') &&
patientDetails?.name?.map((name, index) => (
<div key={`name-${index}`} className={styles.patientName}>
{name.family} {name.given}
</div>
))}
{printIdentifierStickerFields.includes('name') && (
<div key="name" className={styles.patientName}>
{patientDetails?.name}
</div>
)}
{renderElementsInPairs(
[
patientDetails?.identifiers?.map((identifier, index) => (
<div key={`identifier-${index}`}>
<p key={`identifier-text-${index}`}>
{identifier.type.text}: <strong>{identifier.value}</strong>
{identifier?.type?.text}: <strong>{identifier?.value}</strong>
</p>
</div>
)),
<p key="gender">
{t('sex', 'Sex')}:{' '}
<strong>
{patientDetails?.gender == 'female'
? 'F'
: patientDetails?.gender == 'male'
? 'M'
: patientDetails?.gender}
</strong>
</p>,
<p key="age">
{t('age', 'Age')}: <strong>{patientDetails?.age}</strong>
{t('sex', 'Sex')}: <strong>{patientDetails?.gender}</strong>
</p>,
<p key="dateOfBirth">
{t('bod', 'DOB')}: <strong>{patientDetails?.dateOfBirth}</strong>
</p>,
<p key="age">
{t('age', 'Age')}: <strong>{patientDetails?.age}</strong>
</p>,
].flat(),
).map((pair, index) => (
<Grid className={styles.gridRow} key={`grid-${index}`}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,31 @@
@use '@carbon/styles/scss/type';
@import '~@openmrs/esm-styleguide/src/vars';


.stickerContainer {
padding: 1rem 1rem 0 1rem;
padding: 1rem 1rem 0 1rem;
}

.row {
margin: spacing.$spacing-03 0rem 0rem;
display: flex;
flex-flow: row wrap;
gap: spacing.$spacing-05;
margin: spacing.$spacing-03 0rem 0rem;
display: flex;
flex-flow: row wrap;
gap: spacing.$spacing-05;
}

.gridRow {
padding: 0px;
display: flex;
flex-direction: row;
justify-content: space-between;
padding: 0px;
display: flex;
flex-direction: row;
justify-content: space-between;

div {
margin-left: 0px;
margin-bottom: 5px;
}
div {
margin-left: 0px;
margin-bottom: 5px;
}
}

.patientName {
font-family: Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;
font-size: 2rem;
font-weight: bolder;
margin-bottom: 5px;
font-size: 1.5rem;
font-weight: bolder;
margin-bottom: 5px;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { mockPatient } from 'tools';
import { useReactToPrint } from 'react-to-print';
import PrintIdentifierSticker from './print-identifier-sticker.component';
Expand Down Expand Up @@ -29,30 +30,43 @@ jest.mock('@openmrs/esm-framework', () => {

describe('PrintIdentifierSticker', () => {
test('renders the component', () => {
render(<PrintIdentifierSticker patient={mockPatient} closeModal={mockedCloseModal} />);
renderPrintIdentifierSticker();

expect(screen.getByText('Print Identifier Sticker')).toBeInTheDocument();
expect(screen.getByText('Wilson John')).toBeInTheDocument();
expect(screen.getByText('John Wilson')).toBeInTheDocument();
expect(screen.getByText('100GEJ')).toBeInTheDocument();
expect(screen.getByText('1972-04-04')).toBeInTheDocument();
});

test('calls closeModal when cancel button is clicked', () => {
test('calls closeModal when cancel button is clicked', async () => {
const user = userEvent.setup();

renderPrintIdentifierSticker();
fireEvent.click(screen.getByText('Cancel'));

const cancelButton = screen.getByRole('button', { name: /Cancel/ });
expect(cancelButton).toBeInTheDocument();

await user.click(cancelButton);
expect(mockedCloseModal).toHaveBeenCalled();
});

test('calls the print function when print button is clicked', async () => {
const handlePrint = jest.fn();
(useReactToPrint as jest.Mock).mockReturnValue(handlePrint);

const user = userEvent.setup();

renderPrintIdentifierSticker();
fireEvent.click(screen.getByText('Print'));

const printButton = screen.getByRole('button', { name: /Print/ });
expect(printButton).toBeInTheDocument();

await user.click(printButton);
await waitFor(() => {
expect(handlePrint).toHaveBeenCalled();
});
});
});
function renderPrintIdentifierSticker() {
render(<PrintIdentifierSticker patient={mockPatient} closeModal={mockedCloseModal} />);
}
2 changes: 1 addition & 1 deletion packages/esm-patient-banner-app/src/config-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export const configSchema = {
printIdentifierStickerSize: {
_type: Type.String,
_description:
'Specifies the paper size for printing the sticker. You can define the size using units (e.g., mm, in) or named sizes (e.g., "148mm 210mm", "A1", "A2", "A4", "A5").'
'Specifies the paper size for printing the sticker. You can define the size using units (e.g., mm, in) or named sizes (e.g., "148mm 210mm", "A1", "A2", "A4", "A5").',
_default: '4in 6in',
},
};
Expand Down
6 changes: 5 additions & 1 deletion packages/esm-patient-banner-app/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,15 @@
"country": "Country",
"countyDistrict": "District",
"district": "District",
"female": "Female",
"male": "Male",
"other": "Other",
"patientIdentifierSticker": "Patient Identifier Sticker",
"postalCode": "Postal code",
"print": "Print",
"printIdentifierSticker": "Print Identifier Sticker",
"sex": "Sex",
"state": "State",
"stateProvince": "State"
"stateProvince": "State",
"unknown": "Unknown"
}

0 comments on commit e4a5e94

Please sign in to comment.