Skip to content

Commit

Permalink
feat: added new pagination with page choice and limit - Ref gestion-d…
Browse files Browse the repository at this point in the history
…e-projet#2698 (#1043)

* feat: added new pagination with page choice - Ref gestion-de-projet#1306

* feat: added pagination limit - Ref gestion-de-projet#2698

* feat: add test for pagination

* ci: exclude styles.ts from coverage

---------

Co-authored-by: Salah-BOUYAHIA <salah.bouyahia-ext@aphp.fr>
  • Loading branch information
ManelleG and Mehdi-BOUYAHIA authored Sep 26, 2024
1 parent 413e029 commit b81f46a
Show file tree
Hide file tree
Showing 28 changed files with 555 additions and 100 deletions.
2 changes: 1 addition & 1 deletion sonar-project.properties
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@ sonar.organization=aphp
# Coverage
sonar.javascript.lcov.reportPaths=./coverage/lcov.info
sonar.exclusions=dist/**
sonar.coverage.exclusions=**/*.test.*,**/*.tsx,src/__tests__/**
sonar.coverage.exclusions=**/*.test.*,**/*.tsx,src/__tests__/**,**/styles.ts
2 changes: 2 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import moment from 'moment'
import { LocalizationProvider } from '@mui/x-date-pickers'

import AppNavigation from './components/Routes/AppNavigation/AppNavigation'
import WarningDialog from 'components/ui/WarningDialog'

import { store, persistor } from './state/store'

Expand All @@ -18,6 +19,7 @@ moment.locale('fr')
const App = () => (
<LocalizationProvider dateAdapter={MomentUtils}>
<Provider store={store}>
<WarningDialog />
<PersistGate loading={null} persistor={persistor}>
<CssBaseline />

Expand Down
44 changes: 44 additions & 0 deletions src/__tests__/utilsFunction/paginationUtils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { checkIfPageAvailable, handlePageError } from 'utils/paginationUtils'
import { vi } from 'vitest'

describe('checkIfPageAvailable', () => {
it('should not return a dispatch with error ', () => {
const count = 0
const currentPage = 1
const setPage = vi.fn()
const dispatch = vi.fn()
const rowsPerPage = 20

checkIfPageAvailable(count, currentPage, setPage, dispatch, rowsPerPage)
expect(dispatch).not.toHaveBeenCalled()
})
it('should return dispatch with error', () => {
const count = 300
const currentPage = 155
const setPage = vi.fn()
const dispatch = vi.fn()
const rowsPerPage = 20

checkIfPageAvailable(count, currentPage, setPage, dispatch, rowsPerPage)
expect(dispatch).toHaveBeenCalled()
})
})

describe('handlePageError', () => {
it('should not return a dispatch with error ', () => {
const page = 1
const setPage = vi.fn()
const dispatch = vi.fn()

handlePageError(page, setPage, dispatch)
expect(dispatch).not.toHaveBeenCalled()
})
it('should return dispatch with error', () => {
const page = 1001
const setPage = vi.fn()
const dispatch = vi.fn()

handlePageError(page, setPage, dispatch)
expect(dispatch).toHaveBeenCalled()
})
})
24 changes: 20 additions & 4 deletions src/components/Dashboard/Documents/Documents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,18 +37,24 @@ import { Save, SavedSearch } from '@mui/icons-material'
import TextInput from 'components/Filters/TextInput'
import { useSavedFilters } from 'hooks/filters/useSavedFilters'
import List from 'components/ui/List'
import { useAppSelector } from 'state'
import { useAppDispatch, useAppSelector } from 'state'
import Modal from 'components/ui/Modal'
import EncounterStatusFilter from 'components/Filters/EncounterStatusFilter'
import { SourceType } from 'types/scope'
import { Hierarchy } from 'types/hierarchy'
import { useSearchParams } from 'react-router-dom'
import { checkIfPageAvailable, handlePageError } from 'utils/paginationUtils'

type DocumentsProps = {
groupId?: string
deidentified: boolean
}

const Documents: React.FC<DocumentsProps> = ({ groupId, deidentified }) => {
const dispatch = useAppDispatch()
const [searchParams, setSearchParams] = useSearchParams()
const getPageParam = searchParams.get('page')

const [toggleFilterByModal, setToggleFilterByModal] = useState(false)
const [toggleSaveFiltersModal, setToggleSaveFiltersModal] = useState(false)
const [toggleSavedFiltersModal, setToggleSavedFiltersModal] = useState(false)
Expand All @@ -72,7 +78,7 @@ const Documents: React.FC<DocumentsProps> = ({ groupId, deidentified }) => {
const [patientsResult, setPatientsResult] = useState<ResultsType>({ nb: 0, total: 0, label: 'patient(s)' })
const [documents, setDocuments] = useState<CohortComposition[]>([])

const [page, setPage] = useState(1)
const [page, setPage] = useState(getPageParam ? parseInt(getPageParam, 10) : 1)
const [searchInputError, setSearchInputError] = useState<SearchInputError | null>(null)
const [loadingStatus, setLoadingStatus] = useState(LoadingStatus.FETCHING)
const [encounterStatusList, setEncounterStatusList] = useState<Hierarchy<any, any>[]>([])
Expand Down Expand Up @@ -119,6 +125,7 @@ const Documents: React.FC<DocumentsProps> = ({ groupId, deidentified }) => {
}, [nda, ipp, executiveUnits, onlyPdfAvailable, docStatuses, docTypes, startDate, endDate, encounterStatus])

const controllerRef = useRef<AbortController>(new AbortController())
const isFirstRender = useRef(true)

const docStatusesList = [FilterByDocumentStatus.VALIDATED, FilterByDocumentStatus.NOT_VALIDATED]

Expand Down Expand Up @@ -164,6 +171,8 @@ const Documents: React.FC<DocumentsProps> = ({ groupId, deidentified }) => {
total: totalAllPatientDocs
}))
setDocuments(documentsList)

checkIfPageAvailable(totalDocs, page, setPage, dispatch)
} else {
setDocuments([])
}
Expand Down Expand Up @@ -196,8 +205,12 @@ const Documents: React.FC<DocumentsProps> = ({ groupId, deidentified }) => {
}, [])

useEffect(() => {
setLoadingStatus(LoadingStatus.IDDLE)
setPage(1)
if (isFirstRender.current) {
isFirstRender.current = false
} else {
setLoadingStatus(LoadingStatus.IDDLE)
setPage(1)
}
}, [
nda,
ipp,
Expand All @@ -216,6 +229,9 @@ const Documents: React.FC<DocumentsProps> = ({ groupId, deidentified }) => {

useEffect(() => {
setLoadingStatus(LoadingStatus.IDDLE)
setSearchParams({ page: page.toString() })

handlePageError(page, setPage, dispatch)
}, [page])

useEffect(() => {
Expand Down
24 changes: 20 additions & 4 deletions src/components/Dashboard/ImagingList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,14 @@ import { ResourceType } from 'types/requestCriterias'
import { useSavedFilters } from 'hooks/filters/useSavedFilters'
import TextInput from 'components/Filters/TextInput'
import List from 'components/ui/List'
import { useAppSelector } from 'state'
import { useAppDispatch, useAppSelector } from 'state'
import { BlockWrapper } from 'components/ui/Layout'
import EncounterStatusFilter from 'components/Filters/EncounterStatusFilter'
import { SourceType } from 'types/scope'
import { Hierarchy } from 'types/hierarchy'
import { AppConfig } from 'config'
import { useSearchParams } from 'react-router-dom'
import { checkIfPageAvailable, handlePageError } from 'utils/paginationUtils'

type ImagingListProps = {
groupId?: string
Expand All @@ -43,6 +45,10 @@ type ImagingListProps = {

const ImagingList = ({ groupId, deidentified }: ImagingListProps) => {
const appConfig = useContext(AppConfig)
const dispatch = useAppDispatch()
const [searchParams, setSearchParams] = useSearchParams()
const getPageParam = searchParams.get('page')

const [searchResults, setSearchResults] = useState<ResultsType>({ nb: 0, total: 0, label: 'résultats' })
const [imagingList, setImagingList] = useState<CohortImaging[]>([])

Expand All @@ -55,7 +61,7 @@ const ImagingList = ({ groupId, deidentified }: ImagingListProps) => {
const [allModalities, setAllModalities] = useState<Hierarchy<any, any>[]>([])
const [encounterStatusList, setEncounterStatusList] = useState<Hierarchy<any, any>[]>([])

const [page, setPage] = useState(1)
const [page, setPage] = useState(getPageParam ? parseInt(getPageParam, 10) : 1)
const [
{
orderBy,
Expand Down Expand Up @@ -86,6 +92,7 @@ const ImagingList = ({ groupId, deidentified }: ImagingListProps) => {
const controllerRef = useRef<AbortController | null>(null)
const meState = useAppSelector((state) => state.me)
const maintenanceIsActive = meState?.maintenance?.active
const isFirstRender = useRef(true)

const _fetchImaging = async () => {
try {
Expand Down Expand Up @@ -121,6 +128,8 @@ const ImagingList = ({ groupId, deidentified }: ImagingListProps) => {
nb: totalImaging,
total: totalAllImaging
}))

checkIfPageAvailable(totalImaging, page, setPage, dispatch)
}

setLoadingStatus(LoadingStatus.SUCCESS)
Expand Down Expand Up @@ -148,12 +157,19 @@ const ImagingList = ({ groupId, deidentified }: ImagingListProps) => {
}, [])

useEffect(() => {
setLoadingStatus(LoadingStatus.IDDLE)
setPage(1)
if (isFirstRender.current) {
isFirstRender.current = false
} else {
setLoadingStatus(LoadingStatus.IDDLE)
setPage(1)
}
}, [ipp, nda, startDate, endDate, orderBy, searchInput, executiveUnits, modality, groupId, encounterStatus])

useEffect(() => {
setLoadingStatus(LoadingStatus.IDDLE)
setSearchParams({ page: page.toString() })

handlePageError(page, setPage, dispatch)
}, [page])

useEffect(() => {
Expand Down
28 changes: 22 additions & 6 deletions src/components/Dashboard/PatientList/PatientList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,21 @@ import TextInput from 'components/Filters/TextInput'
import { useSavedFilters } from 'hooks/filters/useSavedFilters'
import { ResourceType } from 'types/requestCriterias'
import List from 'components/ui/List'
import { useAppSelector } from 'state'
import { useAppDispatch, useAppSelector } from 'state'
import { useSearchParams } from 'react-router-dom'
import { checkIfPageAvailable, handlePageError } from 'utils/paginationUtils'

type PatientListProps = {
total: number
groupId?: string
deidentified?: boolean | null
loading?: boolean
}

const PatientList = ({ groupId, total, deidentified }: PatientListProps) => {
const dispatch = useAppDispatch()
const [searchParams, setSearchParams] = useSearchParams()
const getPageParam = searchParams.get('page')

const [toggleFilterByModal, setToggleFilterByModal] = useState(false)
const [toggleSaveFiltersModal, setToggleSaveFiltersModal] = useState(false)
const [toggleSavedFiltersModal, setToggleSavedFiltersModal] = useState(false)
Expand All @@ -76,7 +81,7 @@ const PatientList = ({ groupId, total, deidentified }: PatientListProps) => {
resetSavedFilterError
}
} = useSavedFilters<PatientsFilters>(ResourceType.PATIENT)
const [page, setPage] = useState(1)
const [page, setPage] = useState(getPageParam ? parseInt(getPageParam, 10) : 1)
const [patientsResult, setPatientsResult] = useState<ResultsType>({ nb: 0, total, label: 'patient(s)' })
const [patientsList, setPatientsList] = useState<CohortPatient[]>([])
const [loadingStatus, setLoadingStatus] = useState(LoadingStatus.FETCHING)
Expand Down Expand Up @@ -104,10 +109,14 @@ const PatientList = ({ groupId, total, deidentified }: PatientListProps) => {
const controllerRef = useRef<AbortController | null>(null)
const meState = useAppSelector((state) => state.me)
const maintenanceIsActive = meState?.maintenance?.active
const isFirstRender = useRef(true)

const fetchPatients = async () => {
try {
const includeFacets = page === 1
const includeFacets = isFirstRender.current || page === 1
if (isFirstRender.current) {
isFirstRender.current = false
}
setLoadingStatus(LoadingStatus.FETCHING)
const result = await services.cohorts.fetchPatientList(
{
Expand All @@ -132,6 +141,8 @@ const PatientList = ({ groupId, total, deidentified }: PatientListProps) => {
if (agePyramidData) setAgePyramid(agePyramidData)
}
setPatientsResult((ps) => ({ ...ps, nb: totalPatients, label: 'patient(s)' }))

checkIfPageAvailable(totalPatients, page, setPage, dispatch)
}
setLoadingStatus(LoadingStatus.SUCCESS)
} catch (error) {
Expand All @@ -147,12 +158,17 @@ const PatientList = ({ groupId, total, deidentified }: PatientListProps) => {
}, [])

useEffect(() => {
setLoadingStatus(LoadingStatus.IDDLE)
setPage(1)
if (!isFirstRender.current) {
setLoadingStatus(LoadingStatus.IDDLE)
setPage(1)
}
}, [genders, vitalStatuses, birthdatesRanges, orderBy, searchBy, searchInput, groupId])

useEffect(() => {
setLoadingStatus(LoadingStatus.IDDLE)
setSearchParams({ page: page.toString() })

handlePageError(page, setPage, dispatch)
}, [page])

useEffect(() => {
Expand Down
22 changes: 13 additions & 9 deletions src/components/DataTable/DataTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import React, { ReactNode } from 'react'

import {
Grid,
Pagination,
Paper,
Table,
TableBody,
Expand All @@ -12,6 +11,7 @@ import {
TableSortLabel,
Typography
} from '@mui/material'
import { Pagination } from 'components/ui/Pagination'
import { TableCellWrapper } from 'components/ui/TableCell/styles'

import useStyles from './styles'
Expand Down Expand Up @@ -51,7 +51,7 @@ const DataTable: React.FC<DataTableProps> = ({
}
}
return (
<Grid container justifyContent="flex-end">
<Grid container justifyContent="flex-end" marginBottom={8}>
<TableContainer component={Paper}>
<Table className={classes.table}>
<TableHead>
Expand Down Expand Up @@ -111,13 +111,17 @@ const DataTable: React.FC<DataTableProps> = ({
</TableContainer>

{props.noPagination !== true && (
<Pagination
className={classes.pagination}
count={Math.ceil((total ?? 0) / (rowsPerPage ?? 20))}
shape="circular"
onChange={(event, page: number) => setPage && setPage(page)}
page={page}
/>
<Grid
container
justifyContent="center"
style={{ position: 'fixed', bottom: 0, right: 0, backgroundColor: '#E6F1FD' }}
>
<Pagination
count={Math.ceil((total ?? 0) / (rowsPerPage ?? 20))}
currentPage={page ?? 0}
onPageChange={(page: number) => setPage && setPage(page)}
/>
</Grid>
)}
</Grid>
)
Expand Down
20 changes: 10 additions & 10 deletions src/components/DataTable/ListPatient.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react'

import { CircularProgress, Grid, ListItemIcon, ListItemText, Pagination, Typography } from '@mui/material'
import { Box, CircularProgress, Grid, ListItemIcon, ListItemText, Typography } from '@mui/material'

import { CohortPatient } from 'types'

Expand All @@ -13,6 +13,7 @@ import { useLocation, useNavigate, useParams } from 'react-router-dom'
import GenderIcon from 'components/ui/GenderIcon'
import StatusChip, { ChipStyles } from 'components/ui/StatusChip'
import { VitalStatusLabel } from 'types/requestCriterias'
import { Pagination } from 'components/ui/Pagination'

type ListPatientProps = {
loading: boolean
Expand All @@ -34,8 +35,6 @@ const ListPatient: React.FC<ListPatientProps> = ({
total,
onCloseDrawer
}) => {
const { classes } = useStyles()

return (
<Grid container direction="column" alignItems="center">
{loading ? (
Expand All @@ -62,13 +61,14 @@ const ListPatient: React.FC<ListPatientProps> = ({
)}
</ListWrapper>
)}
<Pagination
className={classes.pagination}
count={Math.ceil((total ?? 0) / (rowsPerPage ?? 20))}
shape="circular"
onChange={(event, page: number) => setPage && setPage(page)}
page={page}
/>
<Box marginBottom="1em" width="100%" display="flex" justifyContent="center">
<Pagination
currentPage={page ?? 0}
count={Math.ceil((total ?? 0) / (rowsPerPage ?? 20))}
onPageChange={(page: number) => setPage && setPage(page)}
smallSize
/>
</Box>
</Grid>
)
}
Expand Down
Loading

0 comments on commit b81f46a

Please sign in to comment.