Skip to content

Commit

Permalink
Updated admin/users grid to server-side pagination (#1551)
Browse files Browse the repository at this point in the history
* updated admin/users grid to server-side pagination

* fixed wrong import

* added default pagination value
  • Loading branch information
tongo-angelov authored Aug 14, 2023
1 parent 98a0f38 commit 260931a
Show file tree
Hide file tree
Showing 15 changed files with 101 additions and 44 deletions.
18 changes: 11 additions & 7 deletions src/common/hooks/person.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
import { useSession } from 'next-auth/react'
import { useQuery, UseQueryOptions } from '@tanstack/react-query'
import { useQuery } from '@tanstack/react-query'

import { PersonResponse } from 'gql/person'
import { PersonPaginatedResponse, PersonResponse } from 'gql/person'
import { endpoints } from 'service/apiEndpoints'
import { authQueryFnFactory } from 'service/restRequests'
import { PaginationData, SortData } from 'gql/types'

export const usePersonList = (options?: UseQueryOptions<PersonResponse[]>) => {
export const usePersonList = (
paginationData?: PaginationData,
sort?: SortData,
searchData?: string,
) => {
const { data: session } = useSession()
return useQuery<PersonResponse[]>(
[endpoints.person.list.url],
authQueryFnFactory<PersonResponse[]>(session?.accessToken),
options,
return useQuery<PersonPaginatedResponse>(
[endpoints.person.list(paginationData, sort, searchData).url],
authQueryFnFactory<PersonPaginatedResponse>(session?.accessToken),
)
}

Expand Down
8 changes: 4 additions & 4 deletions src/common/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,10 +224,10 @@ export const routes = {
view: (id: string) => `/admin/organizers/${id}`,
},
person: {
index: '/admin/persons',
create: '/admin/persons/create',
view: (id: string) => `/admin/persons/${id}`,
edit: (id: string) => `/admin/persons/${id}/edit`,
index: '/admin/users',
create: '/admin/users/create',
view: (id: string) => `/admin/users/${id}`,
edit: (id: string) => `/admin/users/${id}/edit`,
},
company: {
create: '/admin/companies/create',
Expand Down
2 changes: 1 addition & 1 deletion src/components/admin/coordinators/SelectCoordinator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { useField } from 'formik'
export default function SelectCoordinator({ name = 'coordinatorId' }) {
const { t } = useTranslation()
const [field] = useField(name)
const { data: personList } = usePersonList()
const { data: { items: personList } = { items: [] } } = usePersonList()
const { data: coordinatorList } = useCoordinatorsList()

return (
Expand Down
11 changes: 6 additions & 5 deletions src/components/admin/donations/grid/Grid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import { getExactDateTime } from 'common/util/date'
import { useRouter } from 'next/router'
import { money } from 'common/util/money'
import { CampaignDonationHistoryResponse } from 'gql/campaigns'
import { PersonResponse } from 'gql/person'
import { usePersonList } from 'common/hooks/person'
import RenderEditPersonCell from './RenderEditPersonCell'
import { useStores } from '../../../../common/hooks/useStores'
Expand Down Expand Up @@ -58,10 +57,10 @@ export default observer(function Grid() {
campaignId,
{ pageIndex: paginationModel.page, pageSize: paginationModel.pageSize },
donationStore.donationFilters,
donationStore.donationSearch,
donationStore.donationSearch ?? '',
)

const { data }: UseQueryResult<PersonResponse[]> = usePersonList()
const { data: { items: personList } = { items: [] } } = usePersonList()

const RenderVaultCell = ({ params }: RenderCellProps) => {
return <>{params.row.targetVault.name}</>
Expand Down Expand Up @@ -173,7 +172,7 @@ export default observer(function Grid() {
return <RenderPersonCell params={params} />
},
renderEditCell: (params: GridRenderEditCellParams) => {
return <RenderEditPersonCell params={params} personList={data} onUpdate={refetch} />
return <RenderEditPersonCell params={params} personList={personList} onUpdate={refetch} />
},
},
{
Expand All @@ -191,7 +190,9 @@ export default observer(function Grid() {
},

renderEditCell: (params: GridRenderEditCellParams) => {
return <RenderEditBillingEmailCell params={params} personList={data} onUpdate={refetch} />
return (
<RenderEditBillingEmailCell params={params} personList={personList} onUpdate={refetch} />
)
},
},
{
Expand Down
2 changes: 1 addition & 1 deletion src/components/admin/expenses/grid/DetailsModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { ModalStore } from '../ExpensesPage'
export default observer(function DetailsModal() {
const { selectedRecord } = ModalStore
const { data }: UseQueryResult<ExpenseResponse> = useViewExpense(selectedRecord.id)
const { data: personList } = usePersonList()
const { data: { items: personList } = { items: [] } } = usePersonList()
const approvedBy = personList?.find((person) => person.id == data?.approvedById)

const { t } = useTranslation('')
Expand Down
2 changes: 1 addition & 1 deletion src/components/admin/expenses/grid/Grid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export default observer(function Grid() {
pageSize: 10,
page: 0,
})
const { data: personList } = usePersonList()
const { data: { items: personList } = { items: [] } } = usePersonList()

const { isDetailsOpen } = ModalStore

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export default observer(function CampaignExpensesGrid({ slug }: Props) {
pageSize: 10,
page: 0,
})
const { data: personList } = usePersonList()
const { data: { items: personList } = { items: [] } } = usePersonList()

const columns: GridColDef[] = [
{ field: 'id', headerName: 'ID' },
Expand Down
11 changes: 2 additions & 9 deletions src/components/common/person/PersonAutocomplete.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,11 @@ export default function PersonAutocomplete({
autocompleteProps,
}: PersonAutocompleteProps) {
const { t } = useTranslation('person')
const {
data: personList,
isLoading,
refetch,
} = usePersonList({
enabled: false,
refetchOnWindowFocus: false,
})
const { data: { items: personList } = { items: [] }, isLoading, refetch } = usePersonList()
return (
<Autocomplete
isOptionEqualToValue={(option, value) => option.firstName === value.firstName}
options={personList || []}
options={personList}
getOptionLabel={(person) =>
showId
? `${person.firstName} ${person.lastName} (${person.id})`
Expand Down
4 changes: 2 additions & 2 deletions src/components/common/person/PersonSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ export default function PersonSelect({
...textFieldProps
}) {
const [field, meta] = useField(name)
const { data: personList } = usePersonList()
if (!personList) {
const { data: { items: personList } = { items: [] } } = usePersonList()
if (personList.length === 0) {
return null
}

Expand Down
68 changes: 57 additions & 11 deletions src/components/common/person/grid/Grid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,64 @@ import React, { useState } from 'react'
import { UseQueryResult } from '@tanstack/react-query'
import { useTranslation } from 'next-i18next'
import { Box, Avatar } from '@mui/material'
import { DataGrid, GridColDef, GridRenderCellParams } from '@mui/x-data-grid'
import {
DataGrid,
GridColDef,
GridPaginationModel,
GridRenderCellParams,
GridSortDirection,
GridSortModel,
} from '@mui/x-data-grid'
import CheckIcon from '@mui/icons-material/Check'
import CloseOutlinedIcon from '@mui/icons-material/CloseOutlined'

import GridActions from 'components/admin/GridActions'
import DeleteModal from './DeleteModal'
import DetailsModal from './DetailsModal'
import { ModalStore } from '../PersonGrid'
import { usePersonList } from 'common/hooks/person'
import { routes } from 'common/routes'
import { PersonResponse } from 'gql/person'
import CheckIcon from '@mui/icons-material/Check'
import CloseOutlinedIcon from '@mui/icons-material/CloseOutlined'
import { PersonPaginatedResponse } from 'gql/person'
import { PaginationData, SortData } from 'gql/types'

const defaultSort: SortData = {
sortBy: 'createdAt',
sortOrder: 'desc',
}

export default observer(function Grid() {
const { t } = useTranslation()

const { data }: UseQueryResult<PersonResponse[]> = usePersonList()

const { isDetailsOpen } = ModalStore

const [paginationModel, setPaginationModel] = useState({
const [paginationModel, setPaginationModel] = useState<PaginationData>({
pageIndex: 0,
pageSize: 10,
page: 0,
})
const [sortingModel, setSortingModel] = useState<SortData>(defaultSort)

const {
data: { items, total: totalCount } = { items: [], total: 0 },
}: UseQueryResult<PersonPaginatedResponse> = usePersonList(paginationModel, sortingModel)

const handlePaginationModelChange = React.useCallback((paginationModel: GridPaginationModel) => {
setPaginationModel({
pageIndex: paginationModel.page,
pageSize: paginationModel.pageSize,
})
}, [])

const handleSortModelChange = React.useCallback((sortModel: GridSortModel) => {
const sortData: SortData =
sortModel.length !== 0
? {
sortBy: sortModel[0].field === 'name' ? 'firstName' : sortModel[0].field,
sortOrder: sortModel[0].sort ?? 'desc',
}
: defaultSort

setSortingModel(sortData)
}, [])

const getColor = (initials: string): string => {
const colors = ['#0179a8', '#346cb0', '#5f4b8b', '#b76ba3', '#a7c796', '#00a28a', '#3686a0']
Expand Down Expand Up @@ -184,14 +220,24 @@ export default observer(function Grid() {
overflowX: 'hidden',
borderRadius: '0 0 13px 13px',
}}
rows={data || []}
rows={items}
columns={columns}
columnVisibilityModel={{
id: false,
}}
initialState={{
sorting: {
sortModel: [
{ field: defaultSort.sortBy, sort: defaultSort.sortOrder as GridSortDirection },
],
},
}}
pageSizeOptions={[5, 10]}
paginationModel={paginationModel}
onPaginationModelChange={setPaginationModel}
paginationModel={{ page: paginationModel.pageIndex, pageSize: paginationModel.pageSize }}
paginationMode="server"
rowCount={totalCount}
onPaginationModelChange={handlePaginationModelChange}
onSortModelChange={handleSortModelChange}
disableRowSelectionOnClick
/>
</Box>
Expand Down
5 changes: 5 additions & 0 deletions src/gql/person.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ export type PersonResponse = {
emailConfirmed: boolean
}

export type PersonPaginatedResponse = {
items: PersonResponse[]
total: number
}

export type PersonFormData = {
firstName: string
lastName: string
Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { endpoints } from 'service/apiEndpoints'

export const getServerSideProps = securedAdminProps(
['common', 'auth', 'person', 'admin', 'validation'],
() => endpoints.person.list.url,
() => endpoints.person.list({ pageIndex: 0, pageSize: 20 }).url,
)

export default PersonGrid
10 changes: 9 additions & 1 deletion src/service/apiEndpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,15 @@ export const endpoints = {
<Endpoint>{ url: `/campaign-types/${id}`, method: 'DELETE' },
},
person: {
list: <Endpoint>{ url: '/person', method: 'GET' },
list: (paginationData?: PaginationData, sort?: SortData, searchData?: string) => {
const { pageIndex, pageSize } = (paginationData as PaginationData) || {}
const { sortBy, sortOrder } = (sort as SortData) || {}

return <Endpoint>{
url: `/person?pageindex=${pageIndex}&pagesize=${pageSize}&sortBy=${sortBy}&sortOrder=${sortOrder}&search=${searchData}`,
method: 'GET',
}
},
createBeneficiary: <Endpoint>{ url: '/beneficiary/create-beneficiary', method: 'POST' },
viewPerson: (slug: string) => <Endpoint>{ url: `/person/${slug}`, method: 'GET' },
viewPersonByKeylockId: (sub: string) =>
Expand Down

0 comments on commit 260931a

Please sign in to comment.