Skip to content
This repository has been archived by the owner on Jan 9, 2023. It is now read-only.

feat(imaging): link image with visit #2309

Merged
merged 15 commits into from
Aug 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/__tests__/imagings/ViewImagings.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ describe('View Imagings', () => {
id: '1234',
type: 'imaging type',
patient: 'patient',
fullName: 'full name',
status: 'requested',
requestedOn: expectedDate.toISOString(),
requestedBy: 'some user',
Expand Down Expand Up @@ -110,7 +111,7 @@ describe('View Imagings', () => {
expect.objectContaining({ label: 'imagings.imaging.requestedOn', key: 'requestedOn' }),
)
expect(columns[3]).toEqual(
expect.objectContaining({ label: 'imagings.imaging.patient', key: 'patient' }),
expect.objectContaining({ label: 'imagings.imaging.patient', key: 'fullName' }),
)
expect(columns[4]).toEqual(
expect.objectContaining({ label: 'imagings.imaging.requestedBy', key: 'requestedBy' }),
Expand Down
19 changes: 17 additions & 2 deletions src/__tests__/imagings/requests/NewImagingRequest.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,14 @@ describe('New Imaging Request', () => {
expect(typeahead.prop('searchAccessor')).toEqual('fullName')
})

it('should render a dropdown list of visits', async () => {
const wrapper = await setup('loading', {})
const visitsTypeSelect = wrapper.find('.visits').find(SelectWithLabelFormGroup)
expect(visitsTypeSelect).toBeDefined()
expect(visitsTypeSelect.prop('label')).toEqual('patient.visits.label')
expect(visitsTypeSelect.prop('isRequired')).toBeTruthy()
})

it('should render a type input box', async () => {
const wrapper = await setup('loading', {})
const typeInputBox = wrapper.find(TextInputWithLabelFormGroup)
Expand All @@ -98,7 +106,7 @@ describe('New Imaging Request', () => {

it('should render a status types select', async () => {
const wrapper = await setup('loading', {})
const statusTypesSelect = wrapper.find(SelectWithLabelFormGroup)
const statusTypesSelect = wrapper.find('.imaging-status').find(SelectWithLabelFormGroup)

expect(statusTypesSelect).toBeDefined()
expect(statusTypesSelect.prop('label')).toEqual('imagings.imaging.status')
Expand Down Expand Up @@ -184,6 +192,7 @@ describe('New Imaging Request', () => {
patient: 'patient',
type: 'expected type',
status: 'requested',
visitId: 'expected visitId',
notes: 'expected notes',
id: '1234',
requestedOn: expectedDate.toISOString(),
Expand All @@ -204,12 +213,18 @@ describe('New Imaging Request', () => {
onChange({ currentTarget: { value: expectedImaging.type } })
})

const statusSelect = wrapper.find(SelectWithLabelFormGroup)
const statusSelect = wrapper.find('.imaging-status').find(SelectWithLabelFormGroup)
blestab marked this conversation as resolved.
Show resolved Hide resolved
act(() => {
const onChange = statusSelect.prop('onChange') as any
onChange({ currentTarget: { value: expectedImaging.status } })
})

const visitsSelect = wrapper.find('.visits').find(SelectWithLabelFormGroup)
act(() => {
const onChange = visitsSelect.prop('onChange') as any
onChange({ currentTarget: { value: expectedImaging.visitId } })
})

const notesTextField = wrapper.find(TextFieldWithLabelFormGroup)
act(() => {
const onChange = notesTextField.prop('onChange') as any
Expand Down
2 changes: 1 addition & 1 deletion src/imagings/ViewImagings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ const ViewImagings = () => {
formatter: (row) =>
row.requestedOn ? format(new Date(row.requestedOn), 'yyyy-MM-dd hh:mm a') : '',
},
{ label: t('imagings.imaging.patient'), key: 'patient' },
{ label: t('imagings.imaging.patient'), key: 'fullName' },
{
label: t('imagings.imaging.requestedBy'),
key: 'requestedBy',
Expand Down
1 change: 1 addition & 0 deletions src/imagings/imaging-slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ interface Error {
type?: string
status?: string
message?: string
visitId?: string
}

interface ImagingState {
Expand Down
116 changes: 89 additions & 27 deletions src/imagings/requests/NewImagingRequest.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Typeahead, Label, Button, Alert } from '@hospitalrun/components'
import { Typeahead, Label, Button, Alert, Column, Row } from '@hospitalrun/components'
import format from 'date-fns/format'
import React, { useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { useHistory } from 'react-router-dom'
Expand All @@ -23,6 +24,7 @@ const NewImagingRequest = () => {
const history = useHistory()
useTitle(t('imagings.requests.new'))
const { status, error } = useSelector((state: RootState) => state.imaging)
const [visitOption, setVisitOption] = useState([] as Option[])

const statusOptions: Option[] = [
{ label: t('imagings.status.requested'), value: 'requested' },
Expand All @@ -32,9 +34,11 @@ const NewImagingRequest = () => {

const [newImagingRequest, setNewImagingRequest] = useState({
patient: '',
fullName: '',
type: '',
notes: '',
status: '',
visitId: '',
})

const breadcrumbs = [
Expand All @@ -46,10 +50,28 @@ const NewImagingRequest = () => {
useAddBreadcrumbs(breadcrumbs)

const onPatientChange = (patient: Patient) => {
setNewImagingRequest((previousNewImagingRequest) => ({
...previousNewImagingRequest,
patient: patient.fullName as string,
}))
if (patient) {
setNewImagingRequest((previousNewImagingRequest) => ({
...previousNewImagingRequest,
patient: patient.id,
fullName: patient.fullName as string,
}))

const visits = patient.visits?.map((v) => ({
label: `${v.type} at ${format(new Date(v.startDateTime), 'yyyy-MM-dd hh:mm a')}`,
value: v.id,
})) as Option[]

setVisitOption(visits)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do the visits need to be a state variable? seems like we could just reference patient.visits wherever we use the visits.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we need the visits to be a state variable. Because we can only fetch the visits data inside the onPatientChange method using the patient parameter (as we fetch the patient using this line onSearch={async (query: string) => PatientRepository.search(query)}). Using state variable ensures that the visits data will be re-rendered whenever the patient field value is changed.

} else {
setNewImagingRequest((previousNewImagingRequest) => ({
...previousNewImagingRequest,
patient: '',
fullName: '',
visitId: '',
}))
setVisitOption([])
}
}

const onImagingTypeChange = (event: React.ChangeEvent<HTMLInputElement>) => {
Expand All @@ -67,6 +89,13 @@ const NewImagingRequest = () => {
}))
}

const onVisitChange = (value: string) => {
setNewImagingRequest((previousNewImagingRequest) => ({
...previousNewImagingRequest,
visitId: value,
}))
}

const onNoteChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
const notes = event.currentTarget.value
setNewImagingRequest((previousNewImagingRequest) => ({
Expand All @@ -88,25 +117,54 @@ const NewImagingRequest = () => {
history.push('/imaging')
}

const defaultSelectedVisitsOption = () => {
if (visitOption !== undefined) {
return visitOption.filter(({ value }) => value === newImagingRequest.visitId)
}
return []
}

return (
<>
{status === 'error' && (
<Alert color="danger" title={t('states.error')} message={t(error.message || '')} />
)}
<form>
<div className="form-group patient-typeahead">
<Label htmlFor="patientTypeahead" isRequired text={t('imagings.imaging.patient')} />
<Typeahead
id="patientTypeahead"
placeholder={t('imagings.imaging.patient')}
onChange={(p: Patient[]) => onPatientChange(p[0])}
onSearch={async (query: string) => PatientRepository.search(query)}
searchAccessor="fullName"
renderMenuItemChildren={(p: Patient) => <div>{`${p.fullName} (${p.code})`}</div>}
isInvalid={!!error.patient}
feedback={t(error.patient as string)}
/>
</div>
<Row>
<Column>
<div className="form-group patient-typeahead">
<Label htmlFor="patientTypeahead" isRequired text={t('imagings.imaging.patient')} />
<Typeahead
id="patientTypeahead"
placeholder={t('imagings.imaging.patient')}
onChange={(p: Patient[]) => {
onPatientChange(p[0])
}}
onSearch={async (query: string) => PatientRepository.search(query)}
searchAccessor="fullName"
renderMenuItemChildren={(p: Patient) => <div>{`${p.fullName} (${p.code})`}</div>}
isInvalid={!!error.patient}
feedback={t(error.patient as string)}
/>
</div>
</Column>
<Column>
<div className="visits">
<SelectWithLabelFormGroup
name="visit"
label={t('patient.visits.label')}
isRequired
isEditable={newImagingRequest.patient !== undefined}
options={visitOption || []}
defaultSelected={defaultSelectedVisitsOption()}
onChange={(values) => {
onVisitChange(values[0])
}}
/>
</div>
</Column>
</Row>

<TextInputWithLabelFormGroup
name="imagingType"
label={t('imagings.imaging.type')}
Expand All @@ -117,15 +175,19 @@ const NewImagingRequest = () => {
value={newImagingRequest.type}
onChange={onImagingTypeChange}
/>
<SelectWithLabelFormGroup
name="status"
label={t('imagings.imaging.status')}
options={statusOptions}
isRequired
isEditable
defaultSelected={statusOptions.filter(({ value }) => value === newImagingRequest.status)}
onChange={(values) => onStatusChange(values[0])}
/>
<div className="imaging-status">
<SelectWithLabelFormGroup
name="status"
label={t('imagings.imaging.status')}
options={statusOptions}
isRequired
isEditable
defaultSelected={statusOptions.filter(
({ value }) => value === newImagingRequest.status,
)}
onChange={(values) => onStatusChange(values[0])}
/>
</div>
<div className="form-group">
<TextFieldWithLabelFormGroup
name="ImagingNotes"
Expand Down
2 changes: 2 additions & 0 deletions src/shared/model/Imaging.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ import AbstractDBModel from './AbstractDBModel'
export default interface Imaging extends AbstractDBModel {
code: string
patient: string
fullName: string
type: string
status: 'requested' | 'completed' | 'canceled'
visitId: string
requestedOn: string
requestedBy: string // will be the currently logged in user's id
completedOn?: string
Expand Down