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

feat(patient): add paging and sorting functionality #2026

Merged
merged 65 commits into from
Jun 8, 2020
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
dff2b3e
feat(viewpatients): add paging feature in ViewPatients component
akshay-ap Apr 28, 2020
c4109a4
feat: add Sort request in ViewPatients
akshay-ap Apr 28, 2020
11b6c8b
fix(viewpatients.tsx): add userPageRequest in dependency array
akshay-ap Apr 28, 2020
d302f89
Merge branch 'master' into feat-#1969
matteovivona Apr 29, 2020
7cf0f41
Merge branch 'master' into feat-#1969
Apr 29, 2020
025a4b8
Merge branch 'master' into feat-#1969
Apr 30, 2020
5308f5f
feat(viewpatients): refactor code as recommended
akshay-ap May 1, 2020
8727f38
Merge branch 'feat-#1969' of https://github.com/akshay-ap/hospitalrun…
akshay-ap May 1, 2020
7a2f820
Merge branch 'master' into feat-#1969
May 2, 2020
6b90ee1
Merge branch 'master' into feat-#1969
May 2, 2020
0e26ac5
Merge branch 'master' into feat-#1969
May 2, 2020
629c58b
Merge branch 'master' into feat-#1969
May 2, 2020
d1c55e7
feat(viewpatients): add a new field 'index', paging in next direction
akshay-ap May 3, 2020
810f49c
Merge branch 'feat-#1969' of https://github.com/akshay-ap/hospitalrun…
akshay-ap May 3, 2020
220ad9c
test: fix failing tests
akshay-ap May 3, 2020
c62b13d
Merge branch 'master' into feat-#1969
May 3, 2020
52a59d3
feat(viewpatients): enables to navigation to previous page
akshay-ap May 3, 2020
f2608f5
Merge branch 'feat-#1969' of https://github.com/akshay-ap/hospitalrun…
akshay-ap May 3, 2020
e95e04a
Merge branch 'master' into feat-#1969
May 3, 2020
6cdf785
Merge branch 'master' into feat-#1969
May 3, 2020
923560f
refactor: resolve merge conflict
akshay-ap May 5, 2020
c91088f
Merge branch 'master' into feat-#1969
May 5, 2020
66e23d1
Merge branch 'master' into feat-#1969
May 5, 2020
b96680f
feat(viewpatients): add paging for search patients
akshay-ap May 5, 2020
0be439d
Merge branch 'feat-#1969' of https://github.com/akshay-ap/hospitalrun…
akshay-ap May 5, 2020
c453f01
refactor(useupdateeffect): disable warning
akshay-ap May 5, 2020
acc9023
Merge branch 'master' into feat-#1969
May 6, 2020
ef09ba7
Merge branch 'master' into feat-#1969
May 6, 2020
d7defb5
test(patientrepository.test.ts): add test cases for paging
akshay-ap May 10, 2020
570947f
Merge branch 'feat-#1969' of https://github.com/akshay-ap/hospitalrun…
akshay-ap May 10, 2020
71ac1ca
refactor(patientrepository): changes for findAllPaged
akshay-ap May 12, 2020
7411ad0
feat(pagecomponent): user can change page size
akshay-ap May 14, 2020
efba900
refactor: resolve merge conflict
akshay-ap May 14, 2020
10c426d
Merge branch 'master' into feat-#1969
May 16, 2020
05cf4d4
Merge branch 'master' into feat-#1969
May 16, 2020
62c2c59
refactor: resolve merge conflict
akshay-ap May 18, 2020
7797677
Merge branch 'feat-#1969' of https://github.com/akshay-ap/hospitalrun…
akshay-ap May 18, 2020
bff3ddd
Merge branch 'master' into feat-#1969
May 19, 2020
70c3481
Merge branch 'master' into feat-#1969
May 19, 2020
948c95c
Merge branch 'master' into feat-#1969
May 19, 2020
b285303
Merge branch 'master' into feat-#1969
May 20, 2020
edd1f6d
Merge branch 'master' into feat-#1969
May 20, 2020
671ad02
feat(viewpatients): add Tests, Fix bug
akshay-ap May 20, 2020
62c37b7
Merge branch 'feat-#1969' of https://github.com/akshay-ap/hospitalrun…
akshay-ap May 20, 2020
f5e0f07
Merge branch 'master' into feat-#1969
May 23, 2020
630fcaf
Merge branch 'master' into feat-#1969
May 23, 2020
cf0fbfd
Merge branch 'master' into feat-#1969
May 23, 2020
82b671d
Merge branch 'master' into feat-#1969
May 23, 2020
f11bb49
Merge branch 'master' into feat-#1969
May 23, 2020
3c0b3d9
Merge branch 'master' into feat-#1969
May 23, 2020
09173f9
Merge branch 'master' into feat-#1969
May 23, 2020
47f693c
Merge branch 'master' into feat-#1969
May 23, 2020
7cd9d20
Merge branch 'master' into feat-#1969
May 24, 2020
76759ad
Merge branch 'master' into feat-#1969
May 26, 2020
f9f70e7
Merge branch 'master' into feat-#1969
May 26, 2020
fb63b64
Merge branch 'master' into feat-#1969
May 28, 2020
d69a641
Merge branch 'master' into feat-#1969
May 30, 2020
6d15f9f
refactor(pagerequest): add new line
akshay-ap May 31, 2020
0677b2a
Merge branch 'master' into feat-#1969
Jun 2, 2020
19b1e70
Merge branch 'master' into feat-#1969
Jun 2, 2020
2dcc5a3
Merge branch 'master' into feat-#1969
Jun 3, 2020
e291484
Merge branch 'master' into feat-#1969
Jun 4, 2020
5eef660
Merge branch 'master' into feat-#1969
matteovivona Jun 5, 2020
10f5c77
Merge branch 'master' into feat-#1969
Jun 7, 2020
abcf92c
Merge branch 'master' into feat-#1969
Jun 7, 2020
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
54 changes: 37 additions & 17 deletions src/__tests__/patients/list/ViewPatients.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,32 +10,46 @@ import { mocked } from 'ts-jest/utils'
import { act } from 'react-dom/test-utils'
import * as ButtonBarProvider from 'page-header/ButtonBarProvider'
import format from 'date-fns/format'
import Page from 'clients/Page'
import ViewPatients from '../../../patients/list/ViewPatients'
import PatientRepository from '../../../clients/db/PatientRepository'
import * as patientSlice from '../../../patients/patients-slice'
import Patient from '../../../model/Patient'
import { UnpagedRequest } from '../../../clients/db/PageRequest'

const middlewares = [thunk]
const mockStore = configureStore(middlewares)

describe('Patients', () => {
const patients = [
{
id: '123',
fullName: 'test test',
givenName: 'test',
familyName: 'test',
code: 'P12345',
sex: 'male',
dateOfBirth: new Date().toISOString(),
},
]
const patients: Page<Patient> = {
content: [
{
id: '123',
fullName: 'test test',
isApproximateDateOfBirth: false,
givenName: 'test',
familyName: 'test',
code: 'P12345',
sex: 'male',
dateOfBirth: new Date().toISOString(),
phoneNumber: '99999999',
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
rev: '',
},
],
hasNext: false,
hasPrevious: false,
pageRequest: UnpagedRequest,
}
const mockedPatientRepository = mocked(PatientRepository, true)

const setup = (isLoading?: boolean) => {
const store = mockStore({
patients: {
patients,
isLoading,
pageRequest: UnpagedRequest,
},
})
return mount(
Expand Down Expand Up @@ -80,12 +94,12 @@ describe('Patients', () => {
expect(tableHeaders.at(3).text()).toEqual('patient.sex')
expect(tableHeaders.at(4).text()).toEqual('patient.dateOfBirth')

expect(tableColumns.at(0).text()).toEqual(patients[0].code)
expect(tableColumns.at(1).text()).toEqual(patients[0].givenName)
expect(tableColumns.at(2).text()).toEqual(patients[0].familyName)
expect(tableColumns.at(3).text()).toEqual(patients[0].sex)
expect(tableColumns.at(0).text()).toEqual(patients.content[0].code)
expect(tableColumns.at(1).text()).toEqual(patients.content[0].givenName)
expect(tableColumns.at(2).text()).toEqual(patients.content[0].familyName)
expect(tableColumns.at(3).text()).toEqual(patients.content[0].sex)
expect(tableColumns.at(4).text()).toEqual(
format(new Date(patients[0].dateOfBirth), 'yyyy-MM-dd'),
format(new Date(patients.content[0].dateOfBirth), 'yyyy-MM-dd'),
)
})

Expand Down Expand Up @@ -130,7 +144,13 @@ describe('Patients', () => {
wrapper.update()

expect(searchPatientsSpy).toHaveBeenCalledTimes(1)
expect(searchPatientsSpy).toHaveBeenLastCalledWith(expectedSearchText)
expect(searchPatientsSpy).toHaveBeenLastCalledWith(
expectedSearchText,
{
sorts: [{ field: 'code', direction: 'desc' }],
},
{ limit: 1, skip: 0 },
)
})
})
})
71 changes: 55 additions & 16 deletions src/__tests__/patients/patients-slice.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import '../../__mocks__/matchMediaMock'
import { AnyAction } from 'redux'
import { mocked } from 'ts-jest/utils'
import { UnpagedRequest } from 'clients/db/PageRequest'
import patients, {
fetchPatientsStart,
fetchPatientsSuccess,
searchPatients,
} from '../../patients/patients-slice'
import Patient from '../../model/Patient'
import PatientRepository from '../../clients/db/PatientRepository'

describe('patients slice', () => {
Expand All @@ -18,14 +18,34 @@ describe('patients slice', () => {
it('should create the proper initial state with empty patients array', () => {
const patientsStore = patients(undefined, {} as AnyAction)
expect(patientsStore.isLoading).toBeFalsy()
expect(patientsStore.patients).toHaveLength(0)
expect(patientsStore.patients.content).toHaveLength(0)
})

it('should handle the FETCH_PATIENTS_SUCCESS action', () => {
const expectedPatients = [{ id: '1234' }]
const expectedPatients = {
content: [
{
id: '123',
fullName: 'test test',
isApproximateDateOfBirth: false,
givenName: 'test',
familyName: 'test',
code: 'P12345',
sex: 'male',
dateOfBirth: new Date().toISOString(),
phoneNumber: '99999999',
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
rev: '',
},
],
hasNext: false,
hasPrevious: false,
pageRequest: UnpagedRequest,
}
const patientsStore = patients(undefined, {
type: fetchPatientsSuccess.type,
payload: [{ id: '1234' }],
payload: expectedPatients,
})

expect(patientsStore.isLoading).toBeFalsy()
Expand All @@ -43,39 +63,58 @@ describe('patients slice', () => {
expect(dispatch).toHaveBeenCalledWith({ type: fetchPatientsStart.type })
})

it('should call the PatientRepository search method with the correct search criteria', async () => {
it('should call the PatientRepository searchPaged method with the correct search criteria', async () => {
const dispatch = jest.fn()
const getState = jest.fn()
jest.spyOn(PatientRepository, 'search')
jest.spyOn(PatientRepository, 'searchPaged')

const expectedSearchString = 'search string'
await searchPatients(expectedSearchString)(dispatch, getState, null)

expect(PatientRepository.search).toHaveBeenCalledWith(expectedSearchString)
expect(PatientRepository.searchPaged).toHaveBeenCalledWith(
expectedSearchString,
UnpagedRequest,
)
})

it('should call the PatientRepository findAll method if there is no string text', async () => {
it('should call the PatientRepository findAllPaged method if there is no string text', async () => {
const dispatch = jest.fn()
const getState = jest.fn()
jest.spyOn(PatientRepository, 'findAll')
jest.spyOn(PatientRepository, 'findAllPaged')

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

expect(PatientRepository.findAll).toHaveBeenCalledTimes(1)
expect(PatientRepository.findAllPaged).toHaveBeenCalledTimes(1)
})

it('should dispatch the FETCH_PATIENTS_SUCCESS action', async () => {
const dispatch = jest.fn()
const getState = jest.fn()

const expectedPatients = [
{
id: '1234',
},
] as Patient[]
const expectedPatients = {
content: [
{
id: '123',
fullName: 'test test',
isApproximateDateOfBirth: false,
givenName: 'test',
familyName: 'test',
code: 'P12345',
sex: 'male',
dateOfBirth: new Date().toISOString(),
phoneNumber: '99999999',
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
rev: '',
},
],
hasNext: false,
hasPrevious: false,
pageRequest: UnpagedRequest,
}

const mockedPatientRepository = mocked(PatientRepository, true)
mockedPatientRepository.search.mockResolvedValue(expectedPatients)
mockedPatientRepository.searchPaged.mockResolvedValue(expectedPatients)

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

Expand Down
9 changes: 9 additions & 0 deletions src/clients/Page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import AbstractDBModel from '../model/AbstractDBModel'
import PageRequest from './db/PageRequest'

export default interface Page<T extends AbstractDBModel> {
content: T[]
hasNext?: boolean
akshay-ap marked this conversation as resolved.
Show resolved Hide resolved
hasPrevious?: boolean
pageRequest?: PageRequest
}
5 changes: 5 additions & 0 deletions src/clients/db/PageRequest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default interface PageRequest {
limit: number | undefined
akshay-ap marked this conversation as resolved.
Show resolved Hide resolved
skip: number
}
export const UnpagedRequest: PageRequest = { limit: undefined, skip: 0 }
44 changes: 44 additions & 0 deletions src/clients/db/PatientRepository.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import escapeStringRegexp from 'escape-string-regexp'
akshay-ap marked this conversation as resolved.
Show resolved Hide resolved
import shortid from 'shortid'
import Page from 'clients/Page'
import Patient from '../../model/Patient'
import Repository from './Repository'
import { patients } from '../../config/pouchdb'
import PageRequest, { UnpagedRequest } from './PageRequest'

const formatPatientCode = (prefix: string, sequenceNumber: string) => `${prefix}${sequenceNumber}`

Expand All @@ -11,6 +13,9 @@ const getPatientCode = (): string => formatPatientCode('P-', shortid.generate())
export class PatientRepository extends Repository<Patient> {
constructor() {
super(patients)
patients.createIndex({
index: { fields: ['code', 'fullName'] },
})
}

async search(text: string): Promise<Patient[]> {
Expand All @@ -31,6 +36,45 @@ export class PatientRepository extends Repository<Patient> {
})
}

async searchPaged(
text: string,
pageRequest: PageRequest = UnpagedRequest,
akshay-ap marked this conversation as resolved.
Show resolved Hide resolved
): Promise<Page<Patient>> {
return super
.search({
selector: {
$or: [
{
fullName: {
$regex: RegExp(text, 'i'),
},
},
{
code: text,
},
],
},
skip: pageRequest.skip,
limit: pageRequest.limit,
})
.then(
(searchedData) =>
new Promise<Page<Patient>>((resolve) => {
const pagedResult: Page<Patient> = {
content: searchedData,
pageRequest,
hasNext: pageRequest.limit !== undefined && searchedData.length === pageRequest.limit,
hasPrevious: pageRequest.skip > 0,
}
resolve(pagedResult)
}),
)
.catch((err) => {
console.log(err)
return err
})
}
akshay-ap marked this conversation as resolved.
Show resolved Hide resolved

async save(entity: Patient): Promise<Patient> {
const patientCode = getPatientCode()
entity.code = patientCode
Expand Down
31 changes: 31 additions & 0 deletions src/clients/db/Repository.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
/* eslint "@typescript-eslint/camelcase": "off" */
import { v4 as uuidv4 } from 'uuid'
import Page from 'clients/Page'
import AbstractDBModel from '../../model/AbstractDBModel'
import { Unsorted } from './SortRequest'
import PageRequest, { UnpagedRequest } from './PageRequest'

function mapDocument(document: any): any {
const { _id, _rev, ...values } = document
Expand Down Expand Up @@ -41,6 +43,35 @@ export default class Repository<T extends AbstractDBModel> {
return result.docs.map(mapDocument)
}

async findAllPaged(sort = Unsorted, pageRequest: PageRequest = UnpagedRequest): Promise<Page<T>> {
const selector: any = {
_id: { $gt: null },
}

sort.sorts.forEach((s) => {
selector[s.field] = { $gt: null }
})

const result = await this.db.find({
selector,
sort: sort.sorts.length > 0 ? sort.sorts.map((s) => ({ [s.field]: s.direction })) : undefined,
limit: pageRequest.limit,
skip: pageRequest.skip,
akshay-ap marked this conversation as resolved.
Show resolved Hide resolved
})
const mappedResult = result.docs.map(mapDocument)

const pagedResult: Page<T> = {
content: mappedResult,
hasNext: pageRequest.limit !== undefined && mappedResult.length === pageRequest.limit,
hasPrevious: pageRequest.skip > 0,
pageRequest: {
skip: pageRequest.skip,
limit: pageRequest.limit,
},
}
return pagedResult
}

async search(criteria: any): Promise<T[]> {
const response = await this.db.find(criteria)
return response.docs.map(mapDocument)
Expand Down
42 changes: 42 additions & 0 deletions src/components/PageComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React from 'react'
import { Button } from '@hospitalrun/components'
import { useTranslation } from 'react-i18next'

const PageComponent = ({
hasNext,
hasPrevious,
pageNumber,
setPreviousPageRequest,
setNextPageRequest,
}: any) => {
const { t } = useTranslation()

return (
<div style={{ textAlign: 'center' }}>
<Button
key="actions.previous"
outlined
disabled={!hasPrevious}
style={{ float: 'left' }}
color="success"
onClick={setPreviousPageRequest}
>
{t('actions.previous')}
</Button>
<div style={{ display: 'inline-block' }}>
{t('actions.page')} {pageNumber}
</div>
<Button
key="actions.next"
outlined
style={{ float: 'right' }}
disabled={!hasNext}
color="success"
onClick={setNextPageRequest}
>
{t('actions.next')}
</Button>
</div>
)
}
export default PageComponent
3 changes: 3 additions & 0 deletions src/locales/enUs/translations/actions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,8 @@ export default {
list: 'List',
search: 'Search',
confirmDelete: 'Delete Confirmation',
next: 'Next',
previous: 'Previous',
page: 'Page',
},
}
Loading