Skip to content

Commit

Permalink
feat(appointmentslist): add an appointments tab to the patient view
Browse files Browse the repository at this point in the history
add an appointments tab to the patient view which shows appointments list for corresponding patient.
Uses same layout as patient list view and also implements fuzzy search

HospitalRun#1769
  • Loading branch information
Ignacio Gea committed Feb 13, 2020
1 parent d5dacd6 commit 99f2b91
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 4 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"private": false,
"license": "MIT",
"dependencies": {
"@hospitalrun/components": "^0.33.0",
"@hospitalrun/components": "^0.33.1",
"@reduxjs/toolkit": "~1.2.1",
"@types/pouchdb-find": "~6.3.4",
"bootstrap": "~4.4.1",
Expand Down
5 changes: 3 additions & 2 deletions src/__tests__/patients/view/ViewPatient.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ describe('ViewPatient', () => {

wrapper.update()

const editButton = wrapper.find(Button).at(2)
const editButton = wrapper.find(Button).at(3)
const onClick = editButton.prop('onClick') as any
expect(editButton.text().trim()).toEqual('actions.edit')

Expand Down Expand Up @@ -120,9 +120,10 @@ describe('ViewPatient', () => {
const tabs = tabsHeader.find(Tab)
expect(tabsHeader).toHaveLength(1)

expect(tabs).toHaveLength(2)
expect(tabs).toHaveLength(3)
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')
})

it('should mark the general information tab as active and render the general information component when route is /patients/:id', async () => {
Expand Down
34 changes: 33 additions & 1 deletion src/clients/db/AppointmentsRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,38 @@ export class AppointmentRepository extends Repository<Appointment> {
constructor() {
super(appointments)
}
}

// Fuzzy search for patient appointments. Used for patient appointment search bar
async searchPatientAppointments(patientId: string, text: string): Promise < Appointment[] > {
return super.search({
selector: {
$and: [
{
patientId: patientId
},
{
$or: [
{
location: {
$regex: RegExp(text, 'i'),
},
},
{
reason: {
$regex: RegExp(text, 'i'),
},
},
{
type: {
$regex: RegExp(text, 'i'),
},
},
],
},
],
},
})
}
}

export default new AppointmentRepository()
75 changes: 75 additions & 0 deletions src/patients/appointments/AppointmentsList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import React, { useEffect, useState } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { useHistory } from 'react-router'
import { useTranslation } from 'react-i18next'
import { TextInput, Button, List, ListItem, Container, Row } from '@hospitalrun/components'
import { RootState } from '../../store'
import { fetchPatientAppointments } from '../../scheduling/appointments/appointments-slice'

interface Props {
patientId: string
}

const AppointmentsList = (props: Props) => {
const dispatch = useDispatch()
const history = useHistory()
const { t } = useTranslation()

const patientId = props.patientId
const { appointments } = useSelector((state: RootState) => state.appointments)
const [searchText, setSearchText] = useState<string>('')

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

const list = (
// inline style added to pick up on newlines for string literal
<ul style={{whiteSpace: 'pre-line'}}>
{appointments.map((a) => (
<ListItem action key={a.id} onClick={() => history.push(`/appointments/${a.id}`)}>
{`${t('scheduling.appointment.location')}: ${a.location}
${t('scheduling.appointment.reason')}: ${a.reason}
${t('scheduling.appointment.type')}: ${a.type}
${t('scheduling.appointment.startDate')}: ${new Date(a.startDateTime).toLocaleString()}
${t('scheduling.appointment.endDate')}: ${new Date(a.endDateTime).toLocaleString()}`}
</ListItem>
))}
</ul>
)

const onSearchBoxChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setSearchText(event.target.value)
}

const onSearchFormSubmit = (event: React.FormEvent | React.MouseEvent) => {
event.preventDefault()
dispatch(fetchPatientAppointments(patientId, searchText))
}

return (
<Container>
<form className="form-inline" onSubmit={onSearchFormSubmit}>
<div className="input-group" style={{ width: '100%' }}>
<TextInput
size="lg"
value={searchText}
placeholder={t('actions.search')}
onChange={onSearchBoxChange}
/>
<div className="input-group-append">
<Button onClick={onSearchFormSubmit}>{t('actions.search')}</Button>
</div>
</div>
</form>

<Row>
<List layout="flush" style={{ width: '100%', marginTop: '10px', marginLeft: '-25px' }}>
{list}
</List>
</Row>
</Container>
)
}

export default AppointmentsList
9 changes: 9 additions & 0 deletions src/patients/view/ViewPatient.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { getPatientFullName } from '../util/patient-name-util'
import Patient from '../../model/Patient'
import GeneralInformation from '../GeneralInformation'
import RelatedPerson from '../related-persons/RelatedPersonTab'
import AppointmentsList from '../appointments/AppointmentsList'

const getFriendlyId = (p: Patient): string => {
if (p) {
Expand Down Expand Up @@ -54,6 +55,11 @@ const ViewPatient = () => {
label={t('patient.relatedPersons.label')}
onClick={() => history.push(`/patients/${patient.id}/relatedpersons`)}
/>
<Tab
active={location.pathname === `/patients/${patient.id}/appointments`}
label={t('scheduling.appointments.label')}
onClick={() => history.push(`/patients/${patient.id}/appointments`)}
/>
</TabsHeader>
<Panel>
<Route exact path="/patients/:id">
Expand All @@ -76,6 +82,9 @@ const ViewPatient = () => {
<Route exact path="/patients/:id/relatedpersons">
<RelatedPerson patient={patient} />
</Route>
<Route exact path='/patients/:id/appointments'>
<AppointmentsList patientId={patient.id} />
</Route>
</Panel>
</div>
)
Expand Down
14 changes: 14 additions & 0 deletions src/scheduling/appointments/appointments-slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,20 @@ export const fetchAppointments = (): AppThunk => async (dispatch) => {
dispatch(fetchAppointmentsSuccess(appointments))
}

export const fetchPatientAppointments = (patientId: string, searchString?: string): AppThunk => async (dispatch) => {
dispatch(fetchAppointmentsStart())

let appointments
if (searchString === undefined || searchString.trim() === '') {
const query = { selector: { patientId: patientId } }
appointments = await AppointmentRepository.search(query)
} else {
appointments = await AppointmentRepository.searchPatientAppointments(patientId, searchString)
}

dispatch(fetchAppointmentsSuccess(appointments))
}

export const createAppointment = (appointment: Appointment, history: any): AppThunk => async (
dispatch,
) => {
Expand Down

0 comments on commit 99f2b91

Please sign in to comment.