Skip to content

Commit 54c53f2

Browse files
authored
Support for dynamic pagination (#3386)
* PBENCH-732 Pagination for Browsing Page
1 parent 0a5a032 commit 54c53f2

File tree

10 files changed

+356
-173
lines changed

10 files changed

+356
-173
lines changed
Lines changed: 80 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,70 @@
1+
import * as CONSTANTS from "assets/constants/browsingPageConstants";
12
import * as TYPES from "./types";
23

4+
import { DANGER, ERROR_MSG } from "assets/constants/toastConstants";
5+
36
import API from "../utils/axiosInstance";
7+
import { showToast } from "./toastActions";
48
import { uriTemplate } from "utils/helper";
59

6-
export const fetchPublicDatasets = () => async (dispatch, getState) => {
10+
export const fetchPublicDatasets = (page) => async (dispatch, getState) => {
711
try {
812
dispatch({ type: TYPES.LOADING });
913
const endpoints = getState().apiEndpoint.endpoints;
14+
const { offset, limit, filter, searchKey, perPage } =
15+
getState().datasetlist;
16+
let publicData = [...getState().datasetlist.publicData];
17+
const params = new URLSearchParams();
18+
params.append("metadata", "dataset.uploaded");
19+
params.append("access", "public");
20+
params.append("offset", offset);
21+
params.append("limit", limit);
22+
23+
if (searchKey) {
24+
params.append("name", searchKey);
25+
}
26+
if (filter.startDate instanceof Date && !isNaN(filter.startDate)) {
27+
params.append("start", filter.startDate.toUTCString());
28+
}
29+
if (filter.endDate instanceof Date && !isNaN(filter.endDate)) {
30+
params.append("end", filter.endDate.toUTCString());
31+
}
32+
1033
const response = await API.get(
1134
uriTemplate(endpoints, "datasets_list", {}),
12-
{ params: { metadata: "dataset.uploaded", access: "public" } }
35+
{ params }
1336
);
37+
1438
if (response.status === 200 && response.data) {
39+
const startIdx = (page - 1) * perPage;
40+
41+
if (publicData.length !== response.data.total) {
42+
publicData = new Array(response.data.total);
43+
}
44+
publicData.splice(
45+
startIdx,
46+
response.data.results.length,
47+
...response.data.results
48+
);
49+
50+
dispatch({
51+
type: TYPES.UPDATE_PUBLIC_DATASETS,
52+
payload: publicData,
53+
});
54+
// in case of last page, next_url is empty
55+
const offset = response.data.next_url
56+
? new URLSearchParams(response.data.next_url).get("offset")
57+
: response.data.total;
1558
dispatch({
16-
type: "GET_PUBLIC_DATASETS",
17-
payload: response?.data?.results,
59+
type: TYPES.SET_RESULT_OFFSET,
60+
payload: Number(offset),
1861
});
1962
}
20-
dispatch({ type: TYPES.COMPLETED });
21-
dispatch(callLoading());
2263
} catch (error) {
23-
return error;
64+
dispatch(showToast(DANGER, ERROR_MSG));
65+
dispatch({ type: TYPES.NETWORK_ERROR });
2466
}
67+
dispatch({ type: TYPES.COMPLETED });
2568
};
2669

2770
export const getFavoritedDatasets = () => async (dispatch) => {
@@ -33,23 +76,39 @@ export const getFavoritedDatasets = () => async (dispatch) => {
3376
});
3477
};
3578

36-
export const updateFavoriteRepoNames = (favorites) => async (dispatch) => {
37-
dispatch({
38-
type: TYPES.FAVORITED_DATASETS,
39-
payload: [...favorites],
40-
});
41-
};
79+
export const updateFavoriteRepoNames = (favorites) => ({
80+
type: TYPES.FAVORITED_DATASETS,
81+
payload: [...favorites],
82+
});
83+
84+
export const setPageLimit = (newPerPage) => ({
85+
type: TYPES.SET_PAGE_LIMIT,
86+
payload: newPerPage,
87+
});
4288

43-
export const updateTblData = (data) => async (dispatch) => {
89+
export const setFilterKeys = (startDate, endDate) => ({
90+
type: TYPES.SET_DATE_RANGE,
91+
payload: { startDate, endDate },
92+
});
93+
94+
export const nameFilter = (value) => ({
95+
type: TYPES.SET_SEARCH_KEY,
96+
payload: value,
97+
});
98+
99+
export const applyFilter = () => (dispatch) => {
44100
dispatch({
45101
type: TYPES.UPDATE_PUBLIC_DATASETS,
46-
payload: [...data],
102+
payload: [],
47103
});
104+
dispatch({
105+
type: TYPES.SET_RESULT_OFFSET,
106+
payload: CONSTANTS.INITIAL_RESULT_OFFSET,
107+
});
108+
dispatch(fetchPublicDatasets(CONSTANTS.START_PAGE_NUMBER));
48109
};
49110

50-
export const callLoading = () => (dispatch) => {
51-
dispatch({ type: TYPES.LOADING });
52-
setTimeout(() => {
53-
dispatch({ type: TYPES.COMPLETED });
54-
}, 5000);
55-
};
111+
export const setPerPage = (value) => ({
112+
type: TYPES.SET_PER_PAGE,
113+
payload: value,
114+
});

dashboard/src/actions/types.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,13 @@ export const NAVBAR_OPEN = "NAVBAR_OPEN";
2222
export const NAVBAR_CLOSE = "NAVBAR_CLOSE";
2323

2424
/* PUBLIC DATASETS */
25-
export const GET_PUBLIC_DATASETS = "GET_PUBLIC_DATASETS";
26-
export const FAVORITED_DATASETS = "GET_FAVORITE_DATASETS";
2725
export const UPDATE_PUBLIC_DATASETS = "UPDATE_PUBLIC_DATASETS";
26+
export const FAVORITED_DATASETS = "GET_FAVORITE_DATASETS";
27+
export const SET_RESULT_OFFSET = "SET_RESULT_OFFSET";
28+
export const SET_PAGE_LIMIT = "SET_PAGE_LIMIT";
29+
export const SET_DATE_RANGE = "SET_DATE_RANGE";
30+
export const SET_SEARCH_KEY = "SET_SEARCH_KEY";
31+
export const SET_PER_PAGE = "SET_PER_PAGE";
2832

2933
/* DASHBOARD OVERVIEW */
3034
export const USER_RUNS = "USER_RUNS";
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
export const DEFAULT_PER_PAGE = 20;
2+
export const INITIAL_PAGE_LIMIT = 60;
3+
export const INITIAL_RESULT_OFFSET = 0;
4+
export const OVERFETCH_FACTOR = 3;
5+
export const PER_PAGE_OPTIONS = [
6+
{ title: "10", value: 10 },
7+
{ title: "20", value: 20 },
8+
{ title: "50", value: 50 },
9+
];
10+
export const START_PAGE_NUMBER = 1;

dashboard/src/modules/components/DatePickerComponent/index.jsx

Lines changed: 83 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,77 +1,101 @@
1-
import React, { useState } from "react";
1+
import "./index.less";
2+
3+
import * as CONSTANTS from "assets/constants/browsingPageConstants";
4+
25
import {
3-
InputGroup,
4-
InputGroupText,
6+
Button,
57
DatePicker,
8+
Split,
9+
SplitItem,
610
isValidDate,
7-
Button,
811
} from "@patternfly/react-core";
9-
import "./index.less";
10-
import { filterData } from "utils/filterDataset";
11-
import {
12-
bumpToDate,
13-
dateFromUTCString,
14-
getTodayMidnightUTCDate,
15-
} from "utils/dateFunctions";
12+
import React, { useState } from "react";
13+
import { applyFilter, setFilterKeys } from "actions/datasetListActions";
14+
import { useDispatch, useSelector } from "react-redux";
15+
16+
import { getTodayMidnightUTCDate } from "utils/dateFunctions";
17+
18+
const DatePickerWidget = (props) => {
19+
const dispatch = useDispatch();
20+
const { filter } = useSelector((state) => state.datasetlist);
21+
22+
const [isEndDateError, setIsEndDateError] = useState(false);
23+
24+
const fromValidator = (date) =>
25+
date <= getTodayMidnightUTCDate()
26+
? ""
27+
: "The Uploaded date cannot be in the future!";
1628

17-
const DatePickerWidget = ({
18-
dataArray,
19-
setPublicData,
20-
datasetName,
21-
setDateRange,
22-
}) => {
23-
const [fromDate, setFromDate] = useState({});
24-
const [toDate, setToDate] = useState(
25-
bumpToDate(getTodayMidnightUTCDate(), 1)
26-
);
27-
const [strDate, setStrDate] = useState(
28-
new Date().toLocaleDateString("fr-CA") // Return a YYYY-MM-DD string
29-
);
3029
const toValidator = (date) =>
31-
date >= fromDate
30+
isValidDate(filter.startDate) && date >= filter.startDate
3231
? ""
33-
: "To date must be greater than or equal to from date";
32+
: 'The "to" date must be after the "from" date';
3433

35-
const onFromChange = (_str, date) => {
36-
const selectedDate = dateFromUTCString(_str);
37-
setFromDate(isValidDate(date) ? selectedDate : {});
34+
const onFromChange = (_event, _str, date) => {
35+
dispatch(setFilterKeys(date, filter.endDate));
36+
if (filter.endDate) {
37+
checkEndDate(date, filter.endDate);
38+
} else {
39+
setIsEndDateError(true);
40+
}
41+
};
42+
43+
const onToChange = (_event, _str, date) => {
3844
if (isValidDate(date)) {
39-
if (date > new Date(strDate)) {
40-
setToDate(bumpToDate(dateFromUTCString(_str), 1));
41-
setStrDate(_str);
42-
}
45+
dispatch(setFilterKeys(filter.startDate, date));
46+
checkEndDate(filter.startDate, date);
47+
} else {
48+
setIsEndDateError(true);
4349
}
4450
};
51+
const checkEndDate = (fromDate, toDate) =>
52+
setIsEndDateError(fromDate >= toDate);
4553

4654
const filterByDate = () => {
47-
setPublicData(filterData(dataArray, fromDate, toDate, datasetName));
48-
setDateRange(fromDate, toDate);
55+
if (filter.startDate) {
56+
dispatch(applyFilter());
57+
props.setPage(CONSTANTS.START_PAGE_NUMBER);
58+
}
4959
};
60+
5061
return (
51-
<InputGroup className="filterInputGroup">
52-
<InputGroupText>Filter By Date</InputGroupText>
53-
<DatePicker
54-
onChange={onFromChange}
55-
aria-label="Start date"
56-
placeholder="YYYY-MM-DD"
57-
/>
58-
<InputGroupText>to</InputGroupText>
59-
<DatePicker
60-
value={strDate}
61-
onChange={(_str, _date) => {
62-
setStrDate(_str);
63-
setToDate(bumpToDate(dateFromUTCString(_str), 1));
64-
}}
65-
isDisabled={!isValidDate(fromDate)}
66-
rangeStart={fromDate}
67-
validators={[toValidator]}
68-
aria-label="End date"
69-
placeholder="YYYY-MM-DD"
70-
/>
71-
<Button variant="control" onClick={filterByDate}>
72-
Update
73-
</Button>
74-
</InputGroup>
62+
<>
63+
<Split className="browsing-page-date-picker">
64+
<SplitItem style={{ padding: "6px 12px 0 12px" }}>
65+
Filter by date
66+
</SplitItem>
67+
<SplitItem>
68+
<DatePicker
69+
onChange={onFromChange}
70+
aria-label="Start date"
71+
placeholder="YYYY-MM-DD"
72+
validators={[fromValidator]}
73+
/>
74+
</SplitItem>
75+
<SplitItem style={{ padding: "6px 12px 0 12px" }}>to</SplitItem>
76+
<SplitItem>
77+
<DatePicker
78+
onChange={onToChange}
79+
isDisabled={!isValidDate(filter.startDate)}
80+
rangeStart={filter.startDate}
81+
validators={[toValidator]}
82+
aria-label="End date"
83+
placeholder="YYYY-MM-DD"
84+
helperText={
85+
isEndDateError && `The "to" date must be after the "from" date`
86+
}
87+
/>
88+
</SplitItem>
89+
<Button
90+
variant="control"
91+
onClick={filterByDate}
92+
className="filter-btn"
93+
isDisabled={isEndDateError}
94+
>
95+
Update
96+
</Button>
97+
</Split>
98+
</>
7599
);
76100
};
77101

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
11
.filterInputGroup {
22
margin-left: 10px;
33
}
4+
.browsing-page-date-picker {
5+
.pf-c-date-picker__helper-text {
6+
color: #c9190b;
7+
}
8+
}

0 commit comments

Comments
 (0)