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

Support for dynamic pagination #3386

Merged
merged 12 commits into from
May 9, 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
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");
webbnh marked this conversation as resolved.
Show resolved Hide resolved
params.append("offset", offset);
dbutenhof marked this conversation as resolved.
Show resolved Hide resolved
params.append("limit", limit);

if (searchKey) {
params.append("name", searchKey);
}
webbnh marked this conversation as resolved.
Show resolved Hide resolved
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)
webbnh marked this conversation as resolved.
Show resolved Hide resolved
);
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