Skip to content

Commit

Permalink
feat(viewpatient): added labs tab to ViewPatient
Browse files Browse the repository at this point in the history
A patient can see all his requests for labs in the Labs tab

fix HospitalRun#1972
  • Loading branch information
rubencgt committed Apr 14, 2020
1 parent 09ea338 commit c78e155
Show file tree
Hide file tree
Showing 7 changed files with 166 additions and 2 deletions.
32 changes: 30 additions & 2 deletions src/__tests__/patients/view/ViewPatient.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import * as titleUtil from '../../../page-header/useTitle'
import ViewPatient from '../../../patients/view/ViewPatient'
import * as patientSlice from '../../../patients/patient-slice'
import Permissions from '../../../model/Permissions'
import Labs from '../../../patients/labs/Labs'
import Lab from '../../../model/Lab'

const mockStore = configureMockStore([thunk])

Expand Down Expand Up @@ -49,11 +51,12 @@ describe('ViewPatient', () => {
jest.spyOn(PatientRepository, 'find')
const mockedPatientRepository = mocked(PatientRepository, true)
mockedPatientRepository.find.mockResolvedValue(patient)

const labs: Lab[] = []
history = createMemoryHistory()
store = mockStore({
patient: { patient },
user: { permissions },
labs: { labs },
})

history.push('/patients/123')
Expand Down Expand Up @@ -127,13 +130,14 @@ describe('ViewPatient', () => {
const tabs = tabsHeader.find(Tab)
expect(tabsHeader).toHaveLength(1)

expect(tabs).toHaveLength(6)
expect(tabs).toHaveLength(7)
expect(tabs.at(0).prop('label')).toEqual('patient.generalInformation')
expect(tabs.at(1).prop('label')).toEqual('patient.relatedPersons.label')
expect(tabs.at(2).prop('label')).toEqual('scheduling.appointments.label')
expect(tabs.at(3).prop('label')).toEqual('patient.allergies.label')
expect(tabs.at(4).prop('label')).toEqual('patient.diagnoses.label')
expect(tabs.at(5).prop('label')).toEqual('patient.notes.label')
expect(tabs.at(6).prop('label')).toEqual('patient.labs.label')
})

it('should mark the general information tab as active and render the general information component when route is /patients/:id', async () => {
Expand Down Expand Up @@ -262,4 +266,28 @@ describe('ViewPatient', () => {
expect(notesTab).toHaveLength(1)
expect(notesTab.prop('patient')).toEqual(patient)
})

it('should mark the labs tab as active when it is clicked and render the lab component when route is /patients/:id/labs', async () => {
let wrapper: any
await act(async () => {
wrapper = await setup()
})

await act(async () => {
const tabsHeader = wrapper.find(TabsHeader)
const tabs = tabsHeader.find(Tab)
tabs.at(6).prop('onClick')()
})

wrapper.update()

const tabsHeader = wrapper.find(TabsHeader)
const tabs = tabsHeader.find(Tab)
const labsTab = wrapper.find(Labs)

expect(history.location.pathname).toEqual(`/patients/${patient.id}/labs`)
expect(tabs.at(6).prop('active')).toBeTruthy()
expect(labsTab).toHaveLength(1)
expect(labsTab.prop('patientId')).toEqual(patient.id)
})
})
12 changes: 12 additions & 0 deletions src/clients/db/LabRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,18 @@ export class LabRepository extends Repository<Lab> {
constructor() {
super(labs)
}

async searchPatientLabs(patientId: string): Promise<Lab[]> {
return super.search({
selector: {
$and: [
{
patientId,
},
],
},
})
}
}

export default new LabRepository()
42 changes: 42 additions & 0 deletions src/labs/labs-slice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
import Lab from '../model/Lab'
import { AppThunk } from '../store'
import LabRepository from '../clients/db/LabRepository'

interface LabsState {
isLoading: boolean
labs: Lab[]
}

const initialState: LabsState = {
isLoading: false,
labs: [],
}

function startLoading(state: LabsState) {
state.isLoading = true
}

const labsSlice = createSlice({
name: 'labs',
initialState,
reducers: {
fetchLabsStart: startLoading,
fetchLabsSuccess: (state, { payload }: PayloadAction<Lab[]>) => {
state.isLoading = false
state.labs = payload
},
},
})

export const { fetchLabsStart, fetchLabsSuccess } = labsSlice.actions

export const fetchPatientLabs = (patientId: string): AppThunk => async (dispatch) => {
dispatch(fetchLabsStart())

const labs = await LabRepository.searchPatientLabs(patientId)

dispatch(fetchLabsSuccess(labs))
}

export default labsSlice.reducer
8 changes: 8 additions & 0 deletions src/locales/enUs/translations/patient/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,14 @@ export default {
},
addNoteAbove: 'Add a note using the button above.',
},
labs: {
label: 'Labs',
new: 'Add New Lab',
warning: {
noLabs: 'No Labs',
},
noLabsMessage: 'No labs requests for this person.',
},
types: {
charity: 'Charity',
private: 'Private',
Expand Down
63 changes: 63 additions & 0 deletions src/patients/labs/Labs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import React, { useEffect } from 'react'
import { Alert } from '@hospitalrun/components'
import { useTranslation } from 'react-i18next'
import { useDispatch, useSelector } from 'react-redux'
import format from 'date-fns/format'
import { useHistory } from 'react-router'
import { RootState } from '../../store'
import { fetchPatientLabs } from '../../labs/labs-slice'
import Lab from '../../model/Lab'

interface Props {
patientId: string
}

const Labs = (props: Props) => {
const dispatch = useDispatch()
const history = useHistory()
const { patientId } = props
const { t } = useTranslation()
const { labs } = useSelector((state: RootState) => state.labs)

useEffect(() => {
dispatch(fetchPatientLabs(patientId))
}, [dispatch, patientId])

const onTableRowClick = (lab: Lab) => {
history.push(`/labs/${lab.id}`)
}

return (
<div>
{(!labs || labs.length === 0) && (
<Alert
color="warning"
title={t('patient.labs.warning.noLabs')}
message={t('patient.labs.noLabsMessage')}
/>
)}
{labs.length > 0 && (
<table className="table table-hover">
<thead className="thead-light">
<tr>
<th>{t('labs.lab.type')}</th>
<th>{t('labs.lab.requestedOn')}</th>
<th>{t('labs.lab.status')}</th>
</tr>
</thead>
<tbody>
{labs.map((lab) => (
<tr onClick={() => onTableRowClick(lab)} key={lab.id}>
<td>{lab.type}</td>
<td>{format(new Date(lab.requestedOn), 'yyyy-MM-dd hh:mm a')}</td>
<td>{lab.status}</td>
</tr>
))}
</tbody>
</table>
)}
</div>
)
}

export default Labs
9 changes: 9 additions & 0 deletions src/patients/view/ViewPatient.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import RelatedPerson from '../related-persons/RelatedPersonTab'
import useAddBreadcrumbs from '../../breadcrumbs/useAddBreadcrumbs'
import AppointmentsList from '../appointments/AppointmentsList'
import Note from '../notes/NoteTab'
import Labs from '../labs/Labs'

const getPatientCode = (p: Patient): string => {
if (p) {
Expand Down Expand Up @@ -113,6 +114,11 @@ const ViewPatient = () => {
label={t('patient.notes.label')}
onClick={() => history.push(`/patients/${patient.id}/notes`)}
/>
<Tab
active={location.pathname === `/patients/${patient.id}/labs`}
label={t('patient.labs.label')}
onClick={() => history.push(`/patients/${patient.id}/labs`)}
/>
</TabsHeader>
<Panel>
<Route exact path="/patients/:id">
Expand All @@ -133,6 +139,9 @@ const ViewPatient = () => {
<Route exact path="/patients/:id/notes">
<Note patient={patient} />
</Route>
<Route exact path="/patients/:id/labs">
<Labs patientId={patient.id} />
</Route>
</Panel>
</div>
)
Expand Down
2 changes: 2 additions & 0 deletions src/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import title from '../page-header/title-slice'
import user from '../user/user-slice'
import breadcrumbs from '../breadcrumbs/breadcrumbs-slice'
import components from '../components/component-slice'
import labs from '../labs/labs-slice'

const reducer = combineReducers({
patient,
Expand All @@ -18,6 +19,7 @@ const reducer = combineReducers({
appointments,
breadcrumbs,
components,
labs,
})

const store = configureStore({
Expand Down

0 comments on commit c78e155

Please sign in to comment.