Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Paging on jobs and datasets #2614

Merged
merged 5 commits into from
Sep 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 5 additions & 3 deletions web/src/__tests__/reducers/datasets.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

import * as actionTypes from '../../store/actionCreators/actionTypes'
import datasetsReducer, { initialState } from '../../store/reducers/datasets'
import datasetsReducer, {IDatasetsAction, initialState} from '../../store/reducers/datasets'

const datasets = require('../../../docker/db/data/datasets.json')

Expand All @@ -11,13 +11,15 @@ describe('datasets reducer', () => {
const action = {
type: actionTypes.FETCH_DATASETS_SUCCESS,
payload: {
datasets: datasets
datasets: datasets,
totalCount: 16
}
}
} as IDatasetsAction
expect(datasetsReducer(initialState, action)).toStrictEqual({
init: true,
isLoading: false,
result: datasets,
totalCount: 16,
deletedDatasetName: ''
})
})
Expand Down
10 changes: 6 additions & 4 deletions web/src/__tests__/reducers/jobs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
// SPDX-License-Identifier: Apache-2.0

import * as actionTypes from '../../store/actionCreators/actionTypes'
import jobsReducer, { initialState } from '../../store/reducers/jobs'
import jobsReducer, {IJobsAction, initialState} from '../../store/reducers/jobs'
import { stopWatchDuration } from "../../helpers/time";
import { Job } from "../../types/api";

const jobs = require('../../../docker/db/data/jobs.json')

Expand All @@ -13,10 +14,11 @@ describe('jobs reducer', () => {
const action = {
type: actionTypes.FETCH_JOBS_SUCCESS,
payload: {
jobs: jobs
totalCount: 13,
jobs: jobs as Job[]
}
}
expect(jobsReducer(initialState, action)).toStrictEqual({ isLoading: false, result: jobs, init: true, deletedJobName: '' })
} as IJobsAction
expect(jobsReducer(initialState, action)).toStrictEqual({ isLoading: false, result: jobs, totalCount: 13, init: true, deletedJobName: '' })
})
})

Expand Down
139 changes: 58 additions & 81 deletions web/src/routes/datasets/Datasets.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import * as Redux from 'redux'
import { ChevronLeftRounded, ChevronRightRounded } from '@mui/icons-material'
import {
Chip,
Container,
Table,
TableBody,
Expand Down Expand Up @@ -35,12 +36,11 @@ interface StateProps {
isDatasetsLoading: boolean
isDatasetsInit: boolean
selectedNamespace: Nullable<string>
totalCount: number
}

interface DatasetsState {
datasets: Dataset[]
page: number
pageIsLast: boolean
}

interface DispatchProps {
Expand All @@ -50,52 +50,29 @@ interface DispatchProps {

type DatasetsProps = StateProps & DispatchProps

const PAGE_SIZE = 20

const Datasets: React.FC<DatasetsProps> = ({
datasets,
totalCount,
isDatasetsLoading,
isDatasetsInit,
selectedNamespace,
fetchDatasets,
resetDatasets,
}) => {
const PAGE_SIZE = 20
const mounted = React.useRef<boolean>(false)
const prevSelectedNamespace = React.useRef<Nullable<string>>()

const defaultState = {
datasets: [],
page: 1,
pageIsLast: false,
page: 0,
}
const [state, setState] = React.useState<DatasetsState>(defaultState)

const theme = createTheme(useTheme())

React.useEffect(() => {
if (!mounted.current) {
// on mount
if (selectedNamespace) {
fetchDatasets(selectedNamespace, PAGE_SIZE)
}
mounted.current = true
} else {
// on update
if (prevSelectedNamespace.current !== selectedNamespace && selectedNamespace) {
fetchDatasets(selectedNamespace, PAGE_SIZE)
setState(defaultState)
}

if (datasets !== state.datasets) {
setState({
...state,
datasets,
pageIsLast: datasets.length < state.page * PAGE_SIZE,
})
}

prevSelectedNamespace.current = selectedNamespace
if (selectedNamespace) {
fetchDatasets(selectedNamespace, PAGE_SIZE, state.page * PAGE_SIZE)
}
})
}, [selectedNamespace, state.page])

React.useEffect(() => {
return () => {
Expand All @@ -104,28 +81,13 @@ const Datasets: React.FC<DatasetsProps> = ({
}
}, [])

const pageNavigation = () => {
const { datasets, page, pageIsLast } = state
const titlePos =
datasets.length < PAGE_SIZE && page === 1
? `1 - ${datasets.length}`
: datasets.length > PAGE_SIZE && page === 1
? `1 - ${PAGE_SIZE}`
: datasets.length && page > 1 && pageIsLast === false
? `${PAGE_SIZE * page - PAGE_SIZE + 1} - ${PAGE_SIZE * page}`
: datasets.length && page > 1 && pageIsLast
? `${PAGE_SIZE * page - PAGE_SIZE + 1} - ${datasets.length}`
: `${datasets.length}`
return `${page} (${titlePos})`
}

const handleClickPage = (direction: 'prev' | 'next') => {
const directionPage = direction === 'next' ? state.page + 1 : state.page - 1

if (selectedNamespace) {
fetchDatasets(selectedNamespace, PAGE_SIZE * directionPage)
setState({ ...state, page: directionPage })
}
fetchDatasets(selectedNamespace || '', PAGE_SIZE, directionPage * PAGE_SIZE)
// reset page scroll
window.scrollTo(0, 0)
setState({ ...state, page: directionPage })
}

const i18next = require('i18next')
Expand All @@ -141,36 +103,15 @@ const Datasets: React.FC<DatasetsProps> = ({
</Box>
) : (
<>
<Box display={'flex'} justifyContent={'space-between'} p={2}>
<Box>
<MqText heading>{i18next.t('datasets_route.heading')}</MqText>
Page: {pageNavigation()}
</Box>
<Box>
<Tooltip title={i18next.t('events_route.previous_page')}>
<IconButton
sx={{
marginLeft: theme.spacing(2),
}}
color='primary'
disabled={state.page === 1}
onClick={() => handleClickPage('prev')}
size='large'
>
<ChevronLeftRounded />
</IconButton>
</Tooltip>
<Tooltip title={i18next.t('events_route.next_page')}>
<IconButton
color='primary'
disabled={state.pageIsLast}
onClick={() => handleClickPage('next')}
size='large'
>
<ChevronRightRounded />
</IconButton>
</Tooltip>
</Box>
<Box p={2} display={'flex'}>
<MqText heading>{i18next.t('datasets_route.heading')}</MqText>
<Chip
size={'small'}
variant={'outlined'}
color={'primary'}
sx={{ marginLeft: 1 }}
label={totalCount + ' total'}
></Chip>
</Box>
<Table size='small'>
<TableHead>
Expand Down Expand Up @@ -233,6 +174,41 @@ const Datasets: React.FC<DatasetsProps> = ({
})}
</TableBody>
</Table>
<Box display={'flex'} justifyContent={'flex-end'} alignItems={'center'} mb={2}>
<MqText subdued>
<>
{PAGE_SIZE * state.page + 1} -{' '}
{Math.min(PAGE_SIZE * (state.page + 1), totalCount)} of {totalCount}
</>
</MqText>
<Tooltip title={i18next.t('events_route.previous_page')}>
<span>
<IconButton
sx={{
marginLeft: theme.spacing(2),
}}
color='primary'
disabled={state.page === 0}
onClick={() => handleClickPage('prev')}
size='large'
>
<ChevronLeftRounded />
</IconButton>
</span>
</Tooltip>
<Tooltip title={i18next.t('events_route.next_page')}>
<span>
<IconButton
color='primary'
onClick={() => handleClickPage('next')}
size='large'
disabled={state.page === Math.ceil(totalCount / PAGE_SIZE) - 1}
>
<ChevronRightRounded />
</IconButton>
</span>
</Tooltip>
</Box>
</>
)}
</>
Expand All @@ -243,6 +219,7 @@ const Datasets: React.FC<DatasetsProps> = ({

const mapStateToProps = (state: IState) => ({
datasets: state.datasets.result,
totalCount: state.datasets.totalCount,
isDatasetsLoading: state.datasets.isLoading,
isDatasetsInit: state.datasets.init,
selectedNamespace: state.namespaces.selectedNamespace,
Expand Down
8 changes: 5 additions & 3 deletions web/src/routes/events/Events.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,14 @@ import { useTheme } from '@emotion/react'
import Box from '@mui/material/Box'
import CircularProgress from '@mui/material/CircularProgress/CircularProgress'
import IconButton from '@mui/material/IconButton'
import MqCopy from '../../components/core/copy/MqCopy'
import MqDatePicker from '../../components/core/date-picker/MqDatePicker'
import MqEmpty from '../../components/core/empty/MqEmpty'
import MqJsonView from '../../components/core/json-view/MqJsonView'
import MqStatus from '../../components/core/status/MqStatus'
import MqText from '../../components/core/text/MqText'
import React, { useEffect, useRef } from 'react'
import moment from 'moment'
import MqCopy from '../../components/core/copy/MqCopy'

interface StateProps {
events: Event[]
Expand Down Expand Up @@ -143,6 +143,8 @@ const Events: React.FC<EventsProps> = ({
PAGE_SIZE,
directionPage * PAGE_SIZE
)
// reset page scroll
window.scrollTo(0, 0)
setState({ ...state, page: directionPage, rowExpanded: null })
}

Expand Down Expand Up @@ -249,9 +251,9 @@ const Events: React.FC<EventsProps> = ({
}}
>
<TableCell align='left'>
<Box display={"flex"} alignItems={"center"}>
<Box display={'flex'} alignItems={'center'}>
<MqText font={'mono'}>{event.run.runId}</MqText>
<MqCopy string={event.run.runId}/>
<MqCopy string={event.run.runId} />
</Box>
</TableCell>
<TableCell align='left'>
Expand Down
Loading
Loading