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

Commit

Permalink
feat(patients): add no patients exist (#2235)
Browse files Browse the repository at this point in the history
  • Loading branch information
Fibii committed Aug 5, 2020
1 parent f256129 commit 886163a
Show file tree
Hide file tree
Showing 12 changed files with 147 additions and 20 deletions.
38 changes: 35 additions & 3 deletions src/__tests__/patients/list/ViewPatients.test.tsx
Expand Up @@ -11,6 +11,7 @@ import { mocked } from 'ts-jest/utils'
import * as ButtonBarProvider from '../../../page-header/button-toolbar/ButtonBarProvider'
import ViewPatients from '../../../patients/list/ViewPatients'
import * as patientSlice from '../../../patients/patients-slice'
import NoPatientsExist from '../../../patients/view/NoPatientsExist'
import { UnpagedRequest } from '../../../shared/db/PageRequest'
import PatientRepository from '../../../shared/db/PatientRepository'

Expand All @@ -35,12 +36,13 @@ describe('Patients', () => {
},
]

const setup = (isLoading?: boolean) => {
const setup = (isLoading?: boolean, currentPatients = patients, count = patients.length) => {
const store = mockStore({
patients: {
patients,
patients: currentPatients,
isLoading,
pageRequest: UnpagedRequest,
count,
},
})
return mount(
Expand Down Expand Up @@ -80,6 +82,13 @@ describe('Patients', () => {
expect(wrapper.find(Spinner)).toHaveLength(1)
})

it('should render no patients exists when no patients exist', () => {
const wrapper = setup(false, [], 0)

const addNewPatient = wrapper.find(NoPatientsExist)
expect(addNewPatient).toHaveLength(1)
})

it('should render a table of patients', () => {
const wrapper = setup()

Expand Down Expand Up @@ -121,7 +130,10 @@ describe('Patients', () => {
describe('search functionality', () => {
beforeEach(() => jest.useFakeTimers())

afterEach(() => jest.useRealTimers())
afterEach(() => {
jest.useRealTimers()
jest.restoreAllMocks()
})

it('should search for patients after the search text has not changed for 500 milliseconds', () => {
const searchPatientsSpy = jest.spyOn(patientSlice, 'searchPatients')
Expand All @@ -145,5 +157,25 @@ describe('Patients', () => {
sorts: [{ field: 'index', direction: 'asc' }],
})
})

it("shound't display NoPatientsFound if a search result has no results", () => {
const searchPatientsSpy = jest.spyOn(patientSlice, 'searchPatients')
const wrapper = setup()
searchPatientsSpy.mockClear()
const expectedSearchText = '$$$not a patient$$$'

act(() => {
const onChange = wrapper.find(TextInput).prop('onChange') as any
onChange({ target: { value: expectedSearchText } })
})

act(() => {
jest.advanceTimersByTime(500)
})

wrapper.update()

expect(NoPatientsExist).toHaveLength(0)
})
})
})
3 changes: 2 additions & 1 deletion src/__tests__/patients/patients-slice.test.ts
Expand Up @@ -81,7 +81,8 @@ describe('patients slice', () => {

await searchPatients('')(dispatch, getState, null)

expect(PatientRepository.findAll).toHaveBeenCalledTimes(1)
// expecting 2 here because searchPatients uses PatientRepository.count() which calls #findAll
expect(PatientRepository.findAll).toHaveBeenCalledTimes(2)
})

it('should dispatch the FETCH_PATIENTS_SUCCESS action', async () => {
Expand Down
33 changes: 33 additions & 0 deletions src/__tests__/patients/view/NoPatientsExist.test.tsx
@@ -0,0 +1,33 @@
import { Icon, Typography, Button } from '@hospitalrun/components'
import { mount } from 'enzyme'
import React from 'react'

import NoPatientsExist from '../../../patients/view/NoPatientsExist'

describe('NoPatientsExist', () => {
const setup = () => mount(<NoPatientsExist />)

it('should render an icon and a button with typography', () => {
const wrapper = setup()

const addNewPatient = wrapper.find(NoPatientsExist)
expect(addNewPatient).toHaveLength(1)

const icon = wrapper.find(Icon).first()
const typography = wrapper.find(Typography)
const button = wrapper.find(Button)
const iconType = icon.prop('icon')
const iconSize = icon.prop('size')
const typographyText = typography.prop('children')
const typographyVariant = typography.prop('variant')
const buttonIcon = button.prop('icon')
const buttonText = button.prop('children')

expect(iconType).toEqual('patients')
expect(iconSize).toEqual('6x')
expect(typographyText).toEqual('patients.noPatients')
expect(typographyVariant).toEqual('h5')
expect(buttonIcon).toEqual('patient-add')
expect(buttonText).toEqual('patients.newPatient')
})
})
34 changes: 20 additions & 14 deletions src/patients/list/ViewPatients.tsx
Expand Up @@ -13,6 +13,7 @@ import useTranslator from '../../shared/hooks/useTranslator'
import useUpdateEffect from '../../shared/hooks/useUpdateEffect'
import { RootState } from '../../shared/store'
import { searchPatients } from '../patients-slice'
import NoPatientsExist from '../view/NoPatientsExist'

const breadcrumbs = [{ i18nKey: 'patients.label', location: '/patients' }]

Expand All @@ -22,7 +23,7 @@ const ViewPatients = () => {
useTitle(t('patients.label'))
useAddBreadcrumbs(breadcrumbs, true)
const dispatch = useDispatch()
const { patients, isLoading } = useSelector((state: RootState) => state.patients)
const { patients, isLoading, count } = useSelector((state: RootState) => state.patients)

const setButtonToolBar = useButtonToolbarSetter()

Expand All @@ -48,22 +49,23 @@ const ViewPatients = () => {
}, [dispatch, debouncedSearchText])

useEffect(() => {
setButtonToolBar([
<Button
key="newPatientButton"
outlined
color="success"
icon="patient-add"
onClick={() => history.push('/patients/new')}
>
{t('patients.newPatient')}
</Button>,
])

if (patients && patients.length > 0) {
setButtonToolBar([
<Button
key="newPatientButton"
outlined
color="success"
icon="patient-add"
onClick={() => history.push('/patients/new')}
>
{t('patients.newPatient')}
</Button>,
])
}
return () => {
setButtonToolBar([])
}
}, [dispatch, setButtonToolBar, t, history])
}, [dispatch, setButtonToolBar, t, history, patients])

const loadingIndicator = <Spinner color="blue" loading size={[10, 25]} type="ScaleLoader" />
const table = (
Expand Down Expand Up @@ -91,6 +93,10 @@ const ViewPatients = () => {
setSearchText(event.target.value)
}

if (count === 0) {
return <NoPatientsExist />
}

return (
<div>
<Container>
Expand Down
11 changes: 9 additions & 2 deletions src/patients/patients-slice.ts
Expand Up @@ -8,11 +8,13 @@ import { AppThunk } from '../shared/store'
interface PatientsState {
isLoading: boolean
patients: Patient[]
count: number
}

const initialState: PatientsState = {
isLoading: false,
patients: [],
count: 0,
}

function startLoading(state: PatientsState) {
Expand All @@ -28,9 +30,13 @@ const patientsSlice = createSlice({
state.isLoading = false
state.patients = payload
},
fetchCountSuccess(state, { payload }: PayloadAction<number>) {
state.count = payload
},
},
})
export const { fetchPatientsStart, fetchPatientsSuccess } = patientsSlice.actions

export const { fetchPatientsStart, fetchPatientsSuccess, fetchCountSuccess } = patientsSlice.actions

export const fetchPatients = (sortRequest: SortRequest = Unsorted): AppThunk => async (
dispatch,
Expand All @@ -53,7 +59,8 @@ export const searchPatients = (
} else {
patients = await PatientRepository.search(searchString)
}

const count = await PatientRepository.count()
dispatch(fetchCountSuccess(count))
dispatch(fetchPatientsSuccess(patients))
}

Expand Down
38 changes: 38 additions & 0 deletions src/patients/view/NoPatientsExist.tsx
@@ -0,0 +1,38 @@
import { Button, Icon, Typography } from '@hospitalrun/components'
import React from 'react'
import { useHistory } from 'react-router-dom'

import useTranslator from '../../shared/hooks/useTranslator'

const NoPatientsExist = () => {
const history = useHistory()
const { t } = useTranslator()

return (
<div style={{ display: 'flex', justifyContent: 'center' }}>
<div style={{ display: 'flex', flexDirection: 'column' }}>
<div style={{ display: 'flex', justifyContent: 'center' }}>
<Icon icon="patients" outline={false} size="6x" />
</div>
<div style={{ display: 'flex', justifyContent: 'center' }}>
<div style={{ textAlign: 'center', width: '60%', margin: 16 }}>
<Typography variant="h5">{t('patients.noPatients')}</Typography>
</div>
</div>
<div style={{ display: 'flex', justifyContent: 'center' }}>
<Button
key="newPatientButton"
outlined
color="primary"
icon="patient-add"
onClick={() => history.push('/patients/new')}
>
{t('patients.newPatient')}
</Button>
</div>
</div>
</div>
)
}

export default NoPatientsExist
5 changes: 5 additions & 0 deletions src/shared/db/Repository.ts
Expand Up @@ -132,6 +132,11 @@ export default class Repository<T extends AbstractDBModel> {
// return pagedResult
// }

async count(): Promise<number> {
const result = await this.findAll()
return result.length
}

async search(criteria: any): Promise<T[]> {
const response = await this.db.find(criteria)
const data = await this.db.rel.parseRelDocs(this.type, response.docs)
Expand Down
1 change: 1 addition & 0 deletions src/shared/locales/ar/translations/patients/index.ts
Expand Up @@ -2,6 +2,7 @@ export default {
patients: {
label: 'المرضى',
viewPatients: 'عرض المرضى',
noPatients: 'لا يوجد مرضى حاليا, قم بانشاء اول مريض حالا !',
viewPatient: 'عرض المريض',
editPatient: 'تغيير المريض',
newPatient: 'مريض جديد',
Expand Down
1 change: 1 addition & 0 deletions src/shared/locales/de/translations/patients/index.ts
Expand Up @@ -2,6 +2,7 @@ export default {
patients: {
label: 'Patienten',
viewPatients: 'Patienten anzeigen',
noPatients: 'Es sind noch keine Patienten vorhanden, fügen Sie den Ersten hinzu',
viewPatient: 'Patient anzeigen',
editPatient: 'Patient bearbeiten',
newPatient: 'Neuer Patient',
Expand Down
1 change: 1 addition & 0 deletions src/shared/locales/enUs/translations/patients/index.ts
Expand Up @@ -4,6 +4,7 @@ export default {
warning: 'Warning!',
patientsList: 'Patients List',
viewPatients: 'View Patients',
noPatients: "There are no patients yet, let's add the first one!",
viewPatient: 'View Patient',
newPatient: 'New Patient',
editPatient: 'Edit Patient',
Expand Down
1 change: 1 addition & 0 deletions src/shared/locales/fr/translations/patients/index.ts
Expand Up @@ -3,6 +3,7 @@ export default {
label: 'Patients',
patientsList: 'Liste des patients',
viewPatients: 'Voir les patients',
noPatients: "Il n'y a aucun patient ici, voilà donc le premier",
viewPatient: 'Voir le patient',
editPatient: 'Modifier le patient',
newPatient: 'Nouveau patient',
Expand Down
1 change: 1 addition & 0 deletions src/shared/locales/ru/translations/patients/index.ts
Expand Up @@ -2,6 +2,7 @@ export default {
patients: {
label: 'Пациенты',
viewPatients: 'Просмотр пациентов',
noPatients: 'Пациентов еще нет, давайте добавим первого!',
viewPatient: 'Просмотр пациента',
editPatient: 'Редактировать пациента',
newPatient: 'Новый пациент',
Expand Down

1 comment on commit 886163a

@vercel
Copy link

@vercel vercel bot commented on 886163a Aug 5, 2020

Choose a reason for hiding this comment

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

Please sign in to comment.