diff --git a/packages/backend/src/graphql/queries/get-executions.ts b/packages/backend/src/graphql/queries/get-executions.ts index 2a94442e4..c32bfd752 100644 --- a/packages/backend/src/graphql/queries/get-executions.ts +++ b/packages/backend/src/graphql/queries/get-executions.ts @@ -1,3 +1,4 @@ +import paginate from '@/helpers/pagination' import Execution from '@/models/execution' import ExtendedQueryBuilder from '@/models/query-builder' import Context from '@/types/express/context' @@ -27,7 +28,7 @@ const getExecutions = async ( } } - const results = context.currentUser + const executionsQuery = context.currentUser .$relatedQuery('executions') .withGraphFetched({ flow: { @@ -35,27 +36,8 @@ const getExecutions = async ( }, }) .where(filterBuilder) - .limit(params.limit) - .offset(params.offset) .orderBy('created_at', 'desc') - const resultSize = context.currentUser - .$relatedQuery('executions') - .where(filterBuilder) - .resultSize() - - const [records, count] = await Promise.all([results, resultSize]) - - return { - pageInfo: { - currentPage: Math.ceil(params.offset / params.limit + 1), - totalPages: Math.ceil(count / params.limit), - }, - edges: records.map((record: Execution) => { - return { - node: record, - } - }), - } + return paginate(executionsQuery, params.limit, params.offset) } export default getExecutions diff --git a/packages/backend/src/graphql/schema.graphql b/packages/backend/src/graphql/schema.graphql index 8f376e2c9..c0fed9ecb 100644 --- a/packages/backend/src/graphql/schema.graphql +++ b/packages/backend/src/graphql/schema.graphql @@ -482,7 +482,7 @@ type User { type PageInfo { currentPage: Int! - totalPages: Int! + totalCount: Int! } type ExecutionEdge { diff --git a/packages/backend/src/helpers/pagination.ts b/packages/backend/src/helpers/pagination.ts index b10fdf82c..b168460ce 100644 --- a/packages/backend/src/helpers/pagination.ts +++ b/packages/backend/src/helpers/pagination.ts @@ -19,7 +19,7 @@ const paginate = async ( return { pageInfo: { currentPage: Math.ceil(offset / limit + 1), - totalPages: Math.ceil(count / limit), + totalCount: count, }, edges: records.map((record: Model) => { return { diff --git a/packages/frontend/src/components/AppBar/index.tsx b/packages/frontend/src/components/AppBar/index.tsx index 18727af1b..0e5d95639 100644 --- a/packages/frontend/src/components/AppBar/index.tsx +++ b/packages/frontend/src/components/AppBar/index.tsx @@ -8,8 +8,8 @@ import * as URLS from 'config/urls' export default function AppBar(): React.ReactElement { return ( - - + + diff --git a/packages/frontend/src/components/AppFlows/index.tsx b/packages/frontend/src/components/AppFlows/index.tsx index 1790739fa..571c8c923 100644 --- a/packages/frontend/src/components/AppFlows/index.tsx +++ b/packages/frontend/src/components/AppFlows/index.tsx @@ -1,9 +1,9 @@ import type { IFlow } from '@plumber/types' -import { Link, useSearchParams } from 'react-router-dom' +import { useSearchParams } from 'react-router-dom' import { useQuery } from '@apollo/client' -import Pagination from '@mui/material/Pagination' -import PaginationItem from '@mui/material/PaginationItem' +import { Flex } from '@chakra-ui/react' +import { Pagination } from '@opengovsg/design-system-react' import AppFlowRow from 'components/FlowRow' import NoResultFound from 'components/NoResultFound' import * as URLS from 'config/urls' @@ -55,20 +55,17 @@ export default function AppFlows(props: AppFlowsProps): React.ReactElement { ))} - {pageInfo && pageInfo.totalPages > 1 && ( - setSearchParams({ page: page.toString() })} - renderItem={(item) => ( - - )} - /> + {pageInfo && pageInfo.totalCount > FLOW_PER_PAGE && ( + + + setSearchParams(page === 1 ? {} : { page: page.toString() }) + } + pageSize={FLOW_PER_PAGE} + totalCount={pageInfo?.totalCount} + > + )} ) diff --git a/packages/frontend/src/components/ExecutionStep/index.tsx b/packages/frontend/src/components/ExecutionStep/index.tsx index d3558276e..d4173907d 100644 --- a/packages/frontend/src/components/ExecutionStep/index.tsx +++ b/packages/frontend/src/components/ExecutionStep/index.tsx @@ -19,11 +19,13 @@ import AppIcon from 'components/AppIcon' import ErrorResult from 'components/ErrorResult' import JSONViewer from 'components/JSONViewer' import { GET_APP } from 'graphql/queries/get-app' +import { EXECUTION_STEP_PER_PAGE } from 'pages/Execution' import RetryButton from './RetryButton' type ExecutionStepProps = { index: number + page: number executionStep: IExecutionStep } @@ -41,9 +43,13 @@ const errorIcon = ( color="interaction.critical.default" /> ) +const getStepPosition = (page: number, index: number) => { + return (page - 1) * EXECUTION_STEP_PER_PAGE + index + 1 +} export default function ExecutionStep({ index, + page, executionStep, }: ExecutionStepProps): React.ReactElement | null { const { data } = useQuery(GET_APP, { @@ -91,11 +97,11 @@ export default function ExecutionStep({ - {index === 0 ? 'Trigger' : 'Action'} + {index === 0 && page === 1 ? 'Trigger' : 'Action'} - {index + 1}. {app?.name} + {getStepPosition(page, index)}. {app?.name} diff --git a/packages/frontend/src/graphql/pagination.ts b/packages/frontend/src/graphql/pagination.ts index f6c40b87e..dfd49ac69 100644 --- a/packages/frontend/src/graphql/pagination.ts +++ b/packages/frontend/src/graphql/pagination.ts @@ -10,7 +10,7 @@ export type TEdge = export type TPageInfo = { currentPage: number - totalPages: number + totalCount: number } export type TExisting = Readonly<{ @@ -34,7 +34,7 @@ const makeEmptyData = (): TExisting => { edges: [], pageInfo: { currentPage: 1, - totalPages: 1, + totalCount: 0, }, } } diff --git a/packages/frontend/src/graphql/queries/get-execution-steps.ts b/packages/frontend/src/graphql/queries/get-execution-steps.ts index d0b4f9c95..b5d10a7dc 100644 --- a/packages/frontend/src/graphql/queries/get-execution-steps.ts +++ b/packages/frontend/src/graphql/queries/get-execution-steps.ts @@ -9,7 +9,7 @@ export const GET_EXECUTION_STEPS = gql` ) { pageInfo { currentPage - totalPages + totalCount } edges { node { diff --git a/packages/frontend/src/graphql/queries/get-executions.ts b/packages/frontend/src/graphql/queries/get-executions.ts index 76c3610cb..070c55f80 100644 --- a/packages/frontend/src/graphql/queries/get-executions.ts +++ b/packages/frontend/src/graphql/queries/get-executions.ts @@ -15,7 +15,7 @@ export const GET_EXECUTIONS = gql` ) { pageInfo { currentPage - totalPages + totalCount } edges { node { diff --git a/packages/frontend/src/graphql/queries/get-flows.ts b/packages/frontend/src/graphql/queries/get-flows.ts index b96369670..b004c1208 100644 --- a/packages/frontend/src/graphql/queries/get-flows.ts +++ b/packages/frontend/src/graphql/queries/get-flows.ts @@ -17,7 +17,7 @@ export const GET_FLOWS = gql` ) { pageInfo { currentPage - totalPages + totalCount } edges { node { diff --git a/packages/frontend/src/pages/Execution/index.tsx b/packages/frontend/src/pages/Execution/index.tsx index adb60e208..f42de8614 100644 --- a/packages/frontend/src/pages/Execution/index.tsx +++ b/packages/frontend/src/pages/Execution/index.tsx @@ -1,10 +1,10 @@ import type { IExecutionStep } from '@plumber/types' import * as React from 'react' -import { useParams } from 'react-router-dom' +import { useParams, useSearchParams } from 'react-router-dom' import { useQuery } from '@apollo/client' -import { Box, Grid, Text } from '@chakra-ui/react' -import { Infobox } from '@opengovsg/design-system-react' +import { Box, Flex, Grid, Text } from '@chakra-ui/react' +import { Infobox, Pagination } from '@opengovsg/design-system-react' import Container from 'components/Container' import ExecutionHeader from 'components/ExecutionHeader' import ExecutionStep from 'components/ExecutionStep' @@ -15,23 +15,25 @@ type ExecutionParams = { executionId: string } -const EXECUTION_PER_PAGE = 100 +export const EXECUTION_STEP_PER_PAGE = 100 const getLimitAndOffset = (page: number) => ({ - limit: EXECUTION_PER_PAGE, - offset: (page - 1) * EXECUTION_PER_PAGE, + limit: EXECUTION_STEP_PER_PAGE, + offset: (page - 1) * EXECUTION_STEP_PER_PAGE, }) export default function Execution(): React.ReactElement { const { executionId } = useParams() as ExecutionParams + const [searchParams, setSearchParams] = useSearchParams() + const page = parseInt(searchParams.get('page') || '', 10) || 1 const { data: execution } = useQuery(GET_EXECUTION, { variables: { executionId }, }) const { data, loading } = useQuery(GET_EXECUTION_STEPS, { - variables: { executionId, ...getLimitAndOffset(1) }, + variables: { executionId, ...getLimitAndOffset(page) }, }) - const { edges } = data?.getExecutionSteps || {} + const { pageInfo, edges } = data?.getExecutionSteps || {} const executionSteps: IExecutionStep[] = edges?.map( (edge: { node: IExecutionStep }) => edge.node, ) @@ -62,9 +64,25 @@ export default function Execution(): React.ReactElement { key={executionStep.id} executionStep={executionStep} index={i} + page={page} /> ))} + + {!loading && + pageInfo && + pageInfo.totalCount > EXECUTION_STEP_PER_PAGE && ( + + + setSearchParams(page === 1 ? {} : { page: page.toString() }) + } + pageSize={EXECUTION_STEP_PER_PAGE} + totalCount={pageInfo?.totalCount} + > + + )} ) } diff --git a/packages/frontend/src/pages/Executions/index.tsx b/packages/frontend/src/pages/Executions/index.tsx index e944b9200..e671c912d 100644 --- a/packages/frontend/src/pages/Executions/index.tsx +++ b/packages/frontend/src/pages/Executions/index.tsx @@ -24,7 +24,7 @@ import { InputLeftElement, InputRightElement, } from '@chakra-ui/react' -import Pagination from '@mui/material/Pagination' +import { Pagination } from '@opengovsg/design-system-react' import Container from 'components/Container' import ExecutionRow from 'components/ExecutionRow' import ExecutionStatusMenu, { StatusType } from 'components/ExecutionStatusMenu' @@ -77,7 +77,7 @@ export default function Executions(): ReactElement { // page handling const handlePageChange = useCallback( - (event: ChangeEvent, page: number) => + (page: number) => formatSearchParams({ page, status: filterStatus, @@ -157,7 +157,7 @@ export default function Executions(): ReactElement { - + @@ -204,13 +204,15 @@ export default function Executions(): ReactElement { ))} - {!loading && pageInfo && pageInfo.totalPages > 1 && ( - + {!loading && pageInfo && pageInfo.totalCount > EXECUTION_PER_PAGE && ( + + + )} diff --git a/packages/frontend/src/pages/Flows/index.tsx b/packages/frontend/src/pages/Flows/index.tsx index a885fa165..c164e5e98 100644 --- a/packages/frontend/src/pages/Flows/index.tsx +++ b/packages/frontend/src/pages/Flows/index.tsx @@ -4,13 +4,13 @@ import { forwardRef, useCallback, useMemo } from 'react' import type { LinkProps } from 'react-router-dom' import { Link, useSearchParams } from 'react-router-dom' import { useQuery } from '@apollo/client' -import { Hide } from '@chakra-ui/react' +import { Flex, Hide } from '@chakra-ui/react' import AddIcon from '@mui/icons-material/Add' import Box from '@mui/material/Box' import CircularProgress from '@mui/material/CircularProgress' import Divider from '@mui/material/Divider' import Grid from '@mui/material/Grid' -import Pagination from '@mui/material/Pagination' +import { Pagination } from '@opengovsg/design-system-react' import ConditionalIconButton from 'components/ConditionalIconButton' import Container from 'components/Container' import EmptyFlowsTemplate from 'components/EmptyFlows' @@ -54,7 +54,7 @@ export default function Flows(): React.ReactElement { ) const handlePageChange = useCallback( - (_event: React.ChangeEvent, page: number) => { + (page: number) => { formatSearchParams({ page, input: flowName, @@ -91,7 +91,6 @@ export default function Flows(): React.ReactElement { }) const { pageInfo, edges } = data?.getFlows || {} - const flows: IFlow[] = edges?.map(({ node }: { node: IFlow }) => node) const hasFlows = flows?.length @@ -170,13 +169,15 @@ export default function Flows(): React.ReactElement { /> )} - {!loading && pageInfo && pageInfo.totalPages > 1 && ( - + {!loading && pageInfo && pageInfo.totalCount > FLOW_PER_PAGE && ( + + + )}