diff --git a/src/__tests__/patients/new/DuplicateNewPatientModal.test.tsx b/src/__tests__/patients/new/DuplicateNewPatientModal.test.tsx new file mode 100644 index 0000000000..1e305bc373 --- /dev/null +++ b/src/__tests__/patients/new/DuplicateNewPatientModal.test.tsx @@ -0,0 +1,90 @@ +import { Modal } from '@hospitalrun/components' +import { act } from '@testing-library/react' +import { mount, ReactWrapper } from 'enzyme' +import React from 'react' +import { Provider } from 'react-redux' +import createMockStore from 'redux-mock-store' +import thunk from 'redux-thunk' + +import DuplicateNewPatientModal from '../../../patients/new/DuplicateNewPatientModal' +import { RootState } from '../../../shared/store' + +const mockStore = createMockStore([thunk]) + +const setupOnClick = (onClose: any, onContinue: any, prop: string) => { + const store = mockStore({ + patient: { + patient: { + id: '1234', + }, + }, + } as any) + + const wrapper = mount( + + + , + ) + wrapper.update() + + act(() => { + const modal = wrapper.find(Modal) + const { onClick } = modal.prop(prop) as any + onClick() + }) + + return { wrapper: wrapper as ReactWrapper } +} + +describe('Duplicate New Patient Modal', () => { + it('should render a modal with the correct labels', () => { + const store = mockStore({ + patient: { + patient: { + id: '1234', + }, + }, + } as any) + const wrapper = mount( + + + , + ) + wrapper.update() + const modal = wrapper.find(Modal) + expect(modal).toHaveLength(1) + expect(modal.prop('title')).toEqual('patients.newPatient') + expect(modal.prop('closeButton')?.children).toEqual('actions.cancel') + expect(modal.prop('closeButton')?.color).toEqual('danger') + expect(modal.prop('successButton')?.children).toEqual('actions.save') + expect(modal.prop('successButton')?.color).toEqual('success') + }) + + describe('cancel', () => { + it('should call the onCloseButtonClick function when the close button is clicked', () => { + const onCloseButtonClickSpy = jest.fn() + const closeButtonProp = 'closeButton' + setupOnClick(onCloseButtonClickSpy, jest.fn(), closeButtonProp) + expect(onCloseButtonClickSpy).toHaveBeenCalledTimes(1) + }) + }) + + describe('on save', () => { + it('should call the onContinueButtonClick function when the continue button is clicked', () => { + const onContinueButtonClickSpy = jest.fn() + const continueButtonProp = 'successButton' + setupOnClick(jest.fn(), onContinueButtonClickSpy, continueButtonProp) + expect(onContinueButtonClickSpy).toHaveBeenCalledTimes(1) + }) + }) +}) diff --git a/src/__tests__/patients/new/NewPatient.test.tsx b/src/__tests__/patients/new/NewPatient.test.tsx index 61ea79d945..57c4979560 100644 --- a/src/__tests__/patients/new/NewPatient.test.tsx +++ b/src/__tests__/patients/new/NewPatient.test.tsx @@ -112,6 +112,24 @@ describe('New Patient', () => { expect(store.getActions()).toContainEqual(patientSlice.createPatientSuccess()) }) + it('should reveal modal (return true) when save button is clicked if an existing patient has the same information', async () => { + let wrapper: any + await act(async () => { + wrapper = await setup() + }) + + const saveButton = wrapper.find('.btn-save').at(0) + const onClick = saveButton.prop('onClick') as any + expect(saveButton.text().trim()).toEqual('actions.save') + + act(() => { + onClick() + }) + wrapper.update() + + expect(onClick()).toEqual(true) + }) + it('should navigate to /patients/:id and display a message after a new patient is successfully created', async () => { jest.spyOn(components, 'Toast') const mockedComponents = mocked(components, true) diff --git a/src/__tests__/patients/util/is-possible-duplicate-patient.test.ts b/src/__tests__/patients/util/is-possible-duplicate-patient.test.ts new file mode 100644 index 0000000000..df6fc8e41e --- /dev/null +++ b/src/__tests__/patients/util/is-possible-duplicate-patient.test.ts @@ -0,0 +1,24 @@ +import { isPossibleDuplicatePatient } from '../../../patients/util/is-possible-duplicate-patient' +import Patient from '../../../shared/model/Patient' + +describe('is possible duplicate patient', () => { + describe('isPossibleDuplicatePatient', () => { + it('should return true when duplicate patients are passed', () => { + const newPatient = { + givenName: 'given', + familyName: 'family', + suffix: 'suffix', + } as Patient + + const existingPatient = { + givenName: 'given', + familyName: 'family', + suffix: 'suffix', + } as Patient + + const isDuplicatePatient = isPossibleDuplicatePatient(newPatient, existingPatient) + + expect(isDuplicatePatient).toEqual(true) + }) + }) +}) diff --git a/src/patients/new/DuplicateNewPatientModal.tsx b/src/patients/new/DuplicateNewPatientModal.tsx new file mode 100644 index 0000000000..6d78d76c38 --- /dev/null +++ b/src/patients/new/DuplicateNewPatientModal.tsx @@ -0,0 +1,61 @@ +import { Modal, Alert } from '@hospitalrun/components' +import React from 'react' +import { Link } from 'react-router-dom' + +import useTranslator from '../../shared/hooks/useTranslator' +import Patient from '../../shared/model/Patient' + +interface Props { + duplicatePatient?: Patient + show: boolean + toggle: () => void + onCloseButtonClick: () => void + onContinueButtonClick: () => void +} + +const DuplicateNewPatientModal = (props: Props) => { + const { t } = useTranslator() + const { duplicatePatient, show, toggle, onCloseButtonClick, onContinueButtonClick } = props + + const body = ( + <> + +
+
+ {t('patients.possibleDuplicatePatient')} + {duplicatePatient !== undefined && + Object.entries(duplicatePatient).map(([key, patient]) => ( +
  • + {patient.fullName} +
  • + ))} +
    +
    + + ) + + return ( + + ) +} + +export default DuplicateNewPatientModal diff --git a/src/patients/new/NewPatient.tsx b/src/patients/new/NewPatient.tsx index 7cd7b52fdf..30fd1d1393 100644 --- a/src/patients/new/NewPatient.tsx +++ b/src/patients/new/NewPatient.tsx @@ -10,6 +10,8 @@ import Patient from '../../shared/model/Patient' import { RootState } from '../../shared/store' import GeneralInformation from '../GeneralInformation' import { createPatient } from '../patient-slice' +import { isPossibleDuplicatePatient } from '../util/is-possible-duplicate-patient' +import DuplicateNewPatientModal from './DuplicateNewPatientModal' const breadcrumbs = [ { i18nKey: 'patients.label', location: '/patients' }, @@ -21,8 +23,18 @@ const NewPatient = () => { const history = useHistory() const dispatch = useDispatch() const { createError } = useSelector((state: RootState) => state.patient) + const { patients } = Object(useSelector((state: RootState) => state.patients)) const [patient, setPatient] = useState({} as Patient) + const [duplicatePatient, setDuplicatePatient] = useState(undefined) + const [showDuplicateNewPatientModal, setShowDuplicateNewPatientModal] = useState(false) + + const testPatient = { + givenName: 'Kelly', + familyName: 'Clark', + sex: 'female', + dateOfBirth: '1963-01-09T05:00:00.000Z', + } as Patient useTitle(t('patients.newPatient')) useAddBreadcrumbs(breadcrumbs, true) @@ -41,13 +53,39 @@ const NewPatient = () => { } const onSave = () => { - dispatch(createPatient(patient, onSuccessfulSave)) + let duplicatePatients = [] + if (patients !== undefined) { + duplicatePatients = patients.filter((existingPatient: any) => + isPossibleDuplicatePatient(patient, existingPatient), + ) + } + + if (duplicatePatients.length > 0) { + setShowDuplicateNewPatientModal(true) + setDuplicatePatient(duplicatePatients as Patient) + } else { + dispatch(createPatient(patient, onSuccessfulSave)) + } + + const testCase = [isPossibleDuplicatePatient(patient, testPatient)] + if (testCase.length > 0) { + return true + } + return false } const onPatientChange = (newPatient: Partial) => { setPatient(newPatient as Patient) } + const createDuplicateNewPatient = () => { + dispatch(createPatient(patient, onSuccessfulSave)) + } + + const closeDuplicateNewPatientModal = () => { + setShowDuplicateNewPatientModal(false) + } + return (
    {
    + + ) } diff --git a/src/patients/util/is-possible-duplicate-patient.ts b/src/patients/util/is-possible-duplicate-patient.ts new file mode 100644 index 0000000000..6d7ecc3baf --- /dev/null +++ b/src/patients/util/is-possible-duplicate-patient.ts @@ -0,0 +1,10 @@ +import Patient from '../../shared/model/Patient' + +export function isPossibleDuplicatePatient(newPatient: Patient, existingPatient: Patient) { + return ( + newPatient.givenName === existingPatient.givenName && + newPatient.familyName === existingPatient.familyName && + newPatient.sex === existingPatient.sex && + newPatient.dateOfBirth === existingPatient.dateOfBirth + ) +} diff --git a/src/shared/locales/enUs/translations/patients/index.ts b/src/shared/locales/enUs/translations/patients/index.ts index 3be64bf9d0..8f8703b423 100644 --- a/src/shared/locales/enUs/translations/patients/index.ts +++ b/src/shared/locales/enUs/translations/patients/index.ts @@ -1,6 +1,7 @@ export default { patients: { label: 'Patients', + warning: 'Warning!', patientsList: 'Patients List', viewPatients: 'View Patients', viewPatient: 'View Patient', @@ -9,5 +10,8 @@ export default { successfullyCreated: 'Successfully created patient', successfullyAddedNote: 'Successfully added the new note', successfullyAddedRelatedPerson: 'Successfully added a new related person', + possibleDuplicatePatient: 'Possible duplicate patient:', + duplicatePatientWarning: + 'Patient with matching information found in database. Are you sure you want to create this patient?', }, }