Skip to content

Commit

Permalink
Support for dynamic pagination (#3386)
Browse files Browse the repository at this point in the history
* PBENCH-732

Pagination for Browsing Page
  • Loading branch information
MVarshini authored May 9, 2023
1 parent 0a5a032 commit 54c53f2
Show file tree
Hide file tree
Showing 10 changed files with 356 additions and 173 deletions.
101 changes: 80 additions & 21 deletions dashboard/src/actions/datasetListActions.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,70 @@
import * as CONSTANTS from "assets/constants/browsingPageConstants";
import * as TYPES from "./types";

import { DANGER, ERROR_MSG } from "assets/constants/toastConstants";

import API from "../utils/axiosInstance";
import { showToast } from "./toastActions";
import { uriTemplate } from "utils/helper";

export const fetchPublicDatasets = () => async (dispatch, getState) => {
export const fetchPublicDatasets = (page) => async (dispatch, getState) => {
try {
dispatch({ type: TYPES.LOADING });
const endpoints = getState().apiEndpoint.endpoints;
const { offset, limit, filter, searchKey, perPage } =
getState().datasetlist;
let publicData = [...getState().datasetlist.publicData];
const params = new URLSearchParams();
params.append("metadata", "dataset.uploaded");
params.append("access", "public");
params.append("offset", offset);
params.append("limit", limit);

if (searchKey) {
params.append("name", searchKey);
}
if (filter.startDate instanceof Date && !isNaN(filter.startDate)) {
params.append("start", filter.startDate.toUTCString());
}
if (filter.endDate instanceof Date && !isNaN(filter.endDate)) {
params.append("end", filter.endDate.toUTCString());
}

const response = await API.get(
uriTemplate(endpoints, "datasets_list", {}),
{ params: { metadata: "dataset.uploaded", access: "public" } }
{ params }
);

if (response.status === 200 && response.data) {
const startIdx = (page - 1) * perPage;

if (publicData.length !== response.data.total) {
publicData = new Array(response.data.total);
}
publicData.splice(
startIdx,
response.data.results.length,
...response.data.results
);

dispatch({
type: TYPES.UPDATE_PUBLIC_DATASETS,
payload: publicData,
});
// in case of last page, next_url is empty
const offset = response.data.next_url
? new URLSearchParams(response.data.next_url).get("offset")
: response.data.total;
dispatch({
type: "GET_PUBLIC_DATASETS",
payload: response?.data?.results,
type: TYPES.SET_RESULT_OFFSET,
payload: Number(offset),
});
}
dispatch({ type: TYPES.COMPLETED });
dispatch(callLoading());
} catch (error) {
return error;
dispatch(showToast(DANGER, ERROR_MSG));
dispatch({ type: TYPES.NETWORK_ERROR });
}
dispatch({ type: TYPES.COMPLETED });
};

export const getFavoritedDatasets = () => async (dispatch) => {
Expand All @@ -33,23 +76,39 @@ export const getFavoritedDatasets = () => async (dispatch) => {
});
};

export const updateFavoriteRepoNames = (favorites) => async (dispatch) => {
dispatch({
type: TYPES.FAVORITED_DATASETS,
payload: [...favorites],
});
};
export const updateFavoriteRepoNames = (favorites) => ({
type: TYPES.FAVORITED_DATASETS,
payload: [...favorites],
});

export const setPageLimit = (newPerPage) => ({
type: TYPES.SET_PAGE_LIMIT,
payload: newPerPage,
});

export const updateTblData = (data) => async (dispatch) => {
export const setFilterKeys = (startDate, endDate) => ({
type: TYPES.SET_DATE_RANGE,
payload: { startDate, endDate },
});

export const nameFilter = (value) => ({
type: TYPES.SET_SEARCH_KEY,
payload: value,
});

export const applyFilter = () => (dispatch) => {
dispatch({
type: TYPES.UPDATE_PUBLIC_DATASETS,
payload: [...data],
payload: [],
});
dispatch({
type: TYPES.SET_RESULT_OFFSET,
payload: CONSTANTS.INITIAL_RESULT_OFFSET,
});
dispatch(fetchPublicDatasets(CONSTANTS.START_PAGE_NUMBER));
};

export const callLoading = () => (dispatch) => {
dispatch({ type: TYPES.LOADING });
setTimeout(() => {
dispatch({ type: TYPES.COMPLETED });
}, 5000);
};
export const setPerPage = (value) => ({
type: TYPES.SET_PER_PAGE,
payload: value,
});
8 changes: 6 additions & 2 deletions dashboard/src/actions/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,13 @@ export const NAVBAR_OPEN = "NAVBAR_OPEN";
export const NAVBAR_CLOSE = "NAVBAR_CLOSE";

/* PUBLIC DATASETS */
export const GET_PUBLIC_DATASETS = "GET_PUBLIC_DATASETS";
export const FAVORITED_DATASETS = "GET_FAVORITE_DATASETS";
export const UPDATE_PUBLIC_DATASETS = "UPDATE_PUBLIC_DATASETS";
export const FAVORITED_DATASETS = "GET_FAVORITE_DATASETS";
export const SET_RESULT_OFFSET = "SET_RESULT_OFFSET";
export const SET_PAGE_LIMIT = "SET_PAGE_LIMIT";
export const SET_DATE_RANGE = "SET_DATE_RANGE";
export const SET_SEARCH_KEY = "SET_SEARCH_KEY";
export const SET_PER_PAGE = "SET_PER_PAGE";

/* DASHBOARD OVERVIEW */
export const USER_RUNS = "USER_RUNS";
Expand Down
10 changes: 10 additions & 0 deletions dashboard/src/assets/constants/browsingPageConstants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export const DEFAULT_PER_PAGE = 20;
export const INITIAL_PAGE_LIMIT = 60;
export const INITIAL_RESULT_OFFSET = 0;
export const OVERFETCH_FACTOR = 3;
export const PER_PAGE_OPTIONS = [
{ title: "10", value: 10 },
{ title: "20", value: 20 },
{ title: "50", value: 50 },
];
export const START_PAGE_NUMBER = 1;
142 changes: 83 additions & 59 deletions dashboard/src/modules/components/DatePickerComponent/index.jsx
Original file line number Diff line number Diff line change
@@ -1,77 +1,101 @@
import React, { useState } from "react";
import "./index.less";

import * as CONSTANTS from "assets/constants/browsingPageConstants";

import {
InputGroup,
InputGroupText,
Button,
DatePicker,
Split,
SplitItem,
isValidDate,
Button,
} from "@patternfly/react-core";
import "./index.less";
import { filterData } from "utils/filterDataset";
import {
bumpToDate,
dateFromUTCString,
getTodayMidnightUTCDate,
} from "utils/dateFunctions";
import React, { useState } from "react";
import { applyFilter, setFilterKeys } from "actions/datasetListActions";
import { useDispatch, useSelector } from "react-redux";

import { getTodayMidnightUTCDate } from "utils/dateFunctions";

const DatePickerWidget = (props) => {
const dispatch = useDispatch();
const { filter } = useSelector((state) => state.datasetlist);

const [isEndDateError, setIsEndDateError] = useState(false);

const fromValidator = (date) =>
date <= getTodayMidnightUTCDate()
? ""
: "The Uploaded date cannot be in the future!";

const DatePickerWidget = ({
dataArray,
setPublicData,
datasetName,
setDateRange,
}) => {
const [fromDate, setFromDate] = useState({});
const [toDate, setToDate] = useState(
bumpToDate(getTodayMidnightUTCDate(), 1)
);
const [strDate, setStrDate] = useState(
new Date().toLocaleDateString("fr-CA") // Return a YYYY-MM-DD string
);
const toValidator = (date) =>
date >= fromDate
isValidDate(filter.startDate) && date >= filter.startDate
? ""
: "To date must be greater than or equal to from date";
: 'The "to" date must be after the "from" date';

const onFromChange = (_str, date) => {
const selectedDate = dateFromUTCString(_str);
setFromDate(isValidDate(date) ? selectedDate : {});
const onFromChange = (_event, _str, date) => {
dispatch(setFilterKeys(date, filter.endDate));
if (filter.endDate) {
checkEndDate(date, filter.endDate);
} else {
setIsEndDateError(true);
}
};

const onToChange = (_event, _str, date) => {
if (isValidDate(date)) {
if (date > new Date(strDate)) {
setToDate(bumpToDate(dateFromUTCString(_str), 1));
setStrDate(_str);
}
dispatch(setFilterKeys(filter.startDate, date));
checkEndDate(filter.startDate, date);
} else {
setIsEndDateError(true);
}
};
const checkEndDate = (fromDate, toDate) =>
setIsEndDateError(fromDate >= toDate);

const filterByDate = () => {
setPublicData(filterData(dataArray, fromDate, toDate, datasetName));
setDateRange(fromDate, toDate);
if (filter.startDate) {
dispatch(applyFilter());
props.setPage(CONSTANTS.START_PAGE_NUMBER);
}
};

return (
<InputGroup className="filterInputGroup">
<InputGroupText>Filter By Date</InputGroupText>
<DatePicker
onChange={onFromChange}
aria-label="Start date"
placeholder="YYYY-MM-DD"
/>
<InputGroupText>to</InputGroupText>
<DatePicker
value={strDate}
onChange={(_str, _date) => {
setStrDate(_str);
setToDate(bumpToDate(dateFromUTCString(_str), 1));
}}
isDisabled={!isValidDate(fromDate)}
rangeStart={fromDate}
validators={[toValidator]}
aria-label="End date"
placeholder="YYYY-MM-DD"
/>
<Button variant="control" onClick={filterByDate}>
Update
</Button>
</InputGroup>
<>
<Split className="browsing-page-date-picker">
<SplitItem style={{ padding: "6px 12px 0 12px" }}>
Filter by date
</SplitItem>
<SplitItem>
<DatePicker
onChange={onFromChange}
aria-label="Start date"
placeholder="YYYY-MM-DD"
validators={[fromValidator]}
/>
</SplitItem>
<SplitItem style={{ padding: "6px 12px 0 12px" }}>to</SplitItem>
<SplitItem>
<DatePicker
onChange={onToChange}
isDisabled={!isValidDate(filter.startDate)}
rangeStart={filter.startDate}
validators={[toValidator]}
aria-label="End date"
placeholder="YYYY-MM-DD"
helperText={
isEndDateError && `The "to" date must be after the "from" date`
}
/>
</SplitItem>
<Button
variant="control"
onClick={filterByDate}
className="filter-btn"
isDisabled={isEndDateError}
>
Update
</Button>
</Split>
</>
);
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
.filterInputGroup {
margin-left: 10px;
}
.browsing-page-date-picker {
.pf-c-date-picker__helper-text {
color: #c9190b;
}
}
Loading

0 comments on commit 54c53f2

Please sign in to comment.