hooks#usePagination TypeScript Examples
The following examples show how to use
hooks#usePagination.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: APIListPage.tsx From one-platform with MIT License | 4 votes |
APIListPage = (): JSX.Element => {
const navigate = useNavigate();
// query param strings
const query = useQueryParams();
const mid = query.get('mid');
const defaultSearch = query.get('search');
// filters, search, sorting
const [isSortSelectOpen, setSortSelect] = useToggle();
const [sortOption, setSortOption] = useState(SortBy.RECENTLY_ADDED);
const [filters, setFilters] = useState<{ type: null | ApiCategory; search: string }>({
type: null,
search: defaultSearch || '',
});
const { pagination, onPerPageSelect, onSetPage, onResetPagination } = usePagination({
page: 1,
perPage: 20,
});
const debouncedSearch = useDebounce(filters.search);
// graphql query hooks
const { isLoading: isApiListLoading, data: namespaceList } = useGetNamespaceList({
limit: pagination.perPage,
offset: (pagination.page - 1) * pagination.perPage,
apiCategory: filters.type,
search: debouncedSearch,
sortBy: sortOption === SortBy.RECENTLY_ADDED ? 'CREATED_ON' : 'UPDATED_ON',
mid,
});
const { isLoading: isNamespaceStatLoading, data: namespaceStats } = useGetNamespaceStats({
search: debouncedSearch,
mid,
});
const handleApiOwnersRender = useCallback((owners: ApiOwnerType[]) => {
return owners.map((owner) =>
owner.group === ApiEmailGroup.USER ? owner.user.cn : owner.email
);
}, []);
const onStatCardClick = (cardType: 'total' | 'rest' | 'graphql') => {
onResetPagination();
if (cardType === 'total') {
setFilters((state) => ({ ...state, type: null }));
} else {
setFilters((state) => ({ ...state, type: cardType.toUpperCase() as ApiCategory }));
}
};
const onSearch = (search: string) => {
setFilters((state) => ({ ...state, search }));
};
const onSortSelect = (
event: React.MouseEvent | React.ChangeEvent,
value: string | SelectOptionObject,
isPlaceholder?: boolean
) => {
if (isPlaceholder) setSortOption(SortBy.RECENTLY_ADDED);
else setSortOption(value as SortBy);
setSortSelect.off(); // close the select
};
const onCardClick = (id: string) => {
navigate(id);
};
const namespaceCount = namespaceStats?.getApiCategoryCount;
const namespaces = namespaceList?.listNamespaces?.data;
const isNamespaceEmpty = !isApiListLoading && namespaces?.length === 0;
return (
<>
<Header />
<Divider />
<PageSection variant="light" isWidthLimited className="pf-m-align-center">
<Grid hasGutter>
<Grid hasGutter span={12}>
{stats.map(({ key, type, image }) => (
<GridItem
key={`api-select-${type}`}
span={4}
className={styles['api-list--stat-card']}
type={key}
>
<StatCard
value={namespaceCount?.[key]}
category={type}
isLoading={isNamespaceStatLoading}
onClick={callbackify(onStatCardClick, key)}
isSelected={filters.type ? filters.type.toLowerCase() === key : key === 'total'}
>
<img
src={`${config.baseURL}/images/${image}`}
alt={`api-select-${type}`}
style={{ height: '48px' }}
/>
</StatCard>
</GridItem>
))}
</Grid>
<GridItem className="pf-u-my-md">
<Split hasGutter className={styles['api-list--table-filter--container']}>
<SplitItem isFilled>
<Link to={ApiCatalogLinks.AddNewApiPage}>
<Button>Add API</Button>
</Link>
</SplitItem>
<SplitItem className="pf-u-w-33">
<Form>
<FormGroup fieldId="search">
<TextInput
aria-label="Search API"
placeholder="Search for APIs"
type="search"
iconVariant="search"
value={filters.search}
onChange={onSearch}
/>
</FormGroup>
</Form>
</SplitItem>
<SplitItem style={{ width: '180px' }}>
<Select
isOpen={isSortSelectOpen}
onToggle={setSortSelect.toggle}
selections={sortOption}
onSelect={onSortSelect}
>
{[
<SelectOption key="select-sort-placeholder" value="Sort by" isDisabled />,
<SelectOption
key={`select-sort:${SortBy.RECENTLY_ADDED}`}
value={SortBy.RECENTLY_ADDED}
/>,
<SelectOption
key={`select-sort:${SortBy.RECENTLY_MODIFIED}`}
value={SortBy.RECENTLY_MODIFIED}
/>,
]}
</Select>
</SplitItem>
</Split>
</GridItem>
{isApiListLoading ? (
<Bullseye className="pf-u-mt-lg">
<Spinner size="xl" />
</Bullseye>
) : (
namespaces?.map(({ id, name, updatedOn, owners, schemas, slug }) => (
<GridItem
span={12}
key={id}
className="catalog-nav-link"
onClick={callbackify(onCardClick, slug)}
>
<ApiDetailsCard
title={name}
owners={handleApiOwnersRender(owners)}
updatedAt={updatedOn}
schemas={schemas.map(({ name: schemaName, category }) => ({
name: schemaName,
type: category,
}))}
/>
</GridItem>
))
)}
{isNamespaceEmpty && (
<EmptyState>
<EmptyStateIcon icon={CubesIcon} />
<Title headingLevel="h4" size="lg">
No API found
</Title>
<EmptyStateBody>Add an API to fill this gap</EmptyStateBody>
</EmptyState>
)}
</Grid>
</PageSection>
<PageSection variant="light" isWidthLimited className="pf-m-align-center pf-u-pb-2xl">
<Pagination
itemCount={namespaceList?.listNamespaces?.count || 0}
widgetId="pagination-options-menu-bottom"
perPage={pagination.perPage}
page={pagination.page}
onSetPage={(_, page) => onSetPage(page)}
onPerPageSelect={(_, perPage) => onPerPageSelect(perPage)}
isCompact
/>
</PageSection>
</>
);
}
Example #2
Source File: HomePage.tsx From one-platform with MIT License | 4 votes |
HomePage = (): JSX.Element => {
const query = useQueryParams();
// modal state hooks
const { popUp, handlePopUpOpen, handlePopUpClose } = usePopUp([
{ name: 'detailView', isOpen: Boolean(query.get('id')) },
{ name: 'appList', isOpen: false },
]);
// hooks for api filtering, pagination
const [filters, setFilters] = useState<FeedbackFilters>({
selectedApps: null,
status: null,
category: null,
search: '',
});
const { pagination, onPerPageSelect, onSetPage } = usePagination();
const [isMyFeedback, setIsMyFeedback] = useToggle();
const [isExporting, setIsExporting] = useToggle();
const debouncedSearch = useDebounce(filters.search, 500);
// convert the filters to graphl variable format for api calls
const queryFilters = useMemo<GetFeedbackListQueryVariables>(() => {
const appIds = Object.keys(filters.selectedApps || {});
const userInfo = opcBase.auth?.getUserInfo();
return {
...filters,
search: debouncedSearch,
appId: appIds.length !== 0 ? appIds : null,
createdBy: isMyFeedback ? userInfo?.rhatUUID : null,
limit: pagination.perPage,
offset: (pagination.page - 1) * pagination.perPage,
};
}, [filters, debouncedSearch, isMyFeedback, pagination]);
// Get app list
const [{ data: appList, fetching: isAppListLoading }] = useQuery<GetAppsQuery>({
query: GetApps,
});
/**
* This query will be executed only when there is ?id=<feedback_id>
* To fetch a particular feedback
*/
const [{ data: feedbackById, fetching: isFeedbackByIdQueryLoading }] = useQuery<
GetFeedbackById,
{ id: string }
>({
query: GetFeedback,
variables: { id: query?.get('id') as string },
pause: !query?.get('id'),
});
// Get feedback list
const [{ data: fetchedFeedback, fetching: isFeedbackListLoading }] = useQuery<
GetFeedbackListQuery,
GetFeedbackListQueryVariables
>({
query: GetFeedbackList,
variables: queryFilters,
});
const feedbacks = fetchedFeedback?.listFeedbacks;
const selectedFeedback = (popUp?.detailView?.data as Feedback) || feedbackById?.getFeedbackById;
// To keep all selectedApps sorted to top
const selectedApps = useMemo(() => {
if (!appList?.apps) return [];
const apps = [...appList.apps];
return apps
.sort(({ id }) => (filters.selectedApps?.[id] ? -1 : 1)) // sort it with selected apps at top
.slice(0, Math.max(Object.keys(filters.selectedApps || {}).length, 5)); // either show 5 apps or all the selected ones
}, [appList?.apps, filters.selectedApps]);
/**
* To show the title of the ticket created
* Computes it based on JIRA, Github, Gitlab
*/
const formatePopupTitle = useCallback((type: string, url: string) => {
if (!type || !url) {
return '';
}
const splittedUrl = url.split('/');
const ticketName = splittedUrl[splittedUrl.length - 1];
if (type.toLowerCase() === 'jira') {
return ticketName;
}
return `${type} ${ticketName}`;
}, []);
const handleFeedbackFilterChange = useCallback(
<T extends unknown>(field: keyof FeedbackFilters, value: T) => {
setFilters((state) => ({ ...state, [field]: value }));
},
[]
);
const handleFeedbackFilterAppIdChange = useCallback(
(app: App) => {
const appsSelected = { ...filters.selectedApps };
if (appsSelected?.[app.id]) {
delete appsSelected[app.id];
} else {
appsSelected[app.id] = app;
}
setFilters((state) => ({ ...state, selectedApps: appsSelected }));
},
[filters.selectedApps]
);
const handleFeedbackFilterClear = useCallback((field: keyof FeedbackFilters) => {
setFilters((state) => ({ ...state, [field]: null }));
}, []);
const onExportToCSV = () => {
if (!feedbacks?.data) return;
setIsExporting.on();
/**
* Format the feedback list json response for the csv
* Set the headers and pick required fields only
*/
const formatedFeedbacks = feedbacks?.data.map((feedback: Record<string, unknown>) => {
const formatedFeedback: Record<string, unknown> = {};
EXPORT_FEEDBACK_CSV.forEach(({ title, field }) => {
const value = field.split('.').reduce((obj, i) => obj[i] as any, feedback);
formatedFeedback[title] = value;
});
return formatedFeedback;
});
jsonexport(formatedFeedbacks, (err, csv) => {
setIsExporting.off();
if (err) {
opcBase.toast.danger({ subject: 'Failed to export csv' });
} else {
// export to csv
let csvContent = 'data:text/csv;charset=utf-8,';
csvContent += csv;
const encodedCsv = encodeURI(csvContent);
const link = document.createElement('a');
link.setAttribute('href', encodedCsv);
link.setAttribute('download', 'Feedback.csv');
link.click();
opcBase.toast.success({
subject: 'Export sucessfully completed',
});
}
});
};
return (
<>
<PageSection isWidthLimited variant="light" className=" pf-m-align-center">
<Grid hasGutter style={{ '--pf-l-grid--m-gutter--GridGap': '2rem' } as CSSProperties}>
<GridItem span={3}>
<SearchInput
type="search"
id="search-feedback"
placeholder="Search via name"
value={filters.search || ''}
onChange={(value) => handleFeedbackFilterChange('search', value)}
/>
</GridItem>
<GridItem span={9}>
<Split>
<SplitItem isFilled>
<Button variant="primary" onClick={setIsMyFeedback.toggle}>
{`${isMyFeedback ? 'All' : 'My'} Feedback`}
</Button>
</SplitItem>
<SplitItem>
<Button
icon={<UploadIcon />}
variant="secondary"
onClick={onExportToCSV}
isLoading={isExporting}
isDisabled={!feedbacks?.count}
>
Export
</Button>
</SplitItem>
</Split>
</GridItem>
<GridItem span={3}>
<form>
<Stack hasGutter>
<StackItem>
<Stack hasGutter style={{ '--pf-global--gutter': '0.75rem' } as CSSProperties}>
<StackItem>
<FilterTitle
title="Applications"
onClear={() => handleFeedbackFilterClear('selectedApps')}
isClearable={Boolean(filters.selectedApps)}
/>
</StackItem>
{isAppListLoading ? (
<Bullseye>
<Spinner size="lg" label="Loading..." />
</Bullseye>
) : (
<>
{selectedApps.map((app) => (
<StackItem key={app.id}>
<Checkbox
id={app.id}
label={app.name}
className="capitalize"
isChecked={Boolean(filters.selectedApps?.[app.id])}
onChange={() => handleFeedbackFilterAppIdChange(app)}
/>
</StackItem>
))}
{(appList?.apps || [])?.length > 5 && (
<StackItem>
<Button
variant="link"
icon={<PlusIcon />}
onClick={() => handlePopUpOpen('appList')}
>
Expand to see more apps
</Button>
</StackItem>
)}
</>
)}
</Stack>
</StackItem>
<StackItem>
<Divider />
</StackItem>
<StackItem>
<Stack hasGutter style={{ '--pf-global--gutter': '0.75rem' } as CSSProperties}>
<StackItem>
<FilterTitle
title="Type"
onClear={() => handleFeedbackFilterClear('category')}
isClearable={Boolean(filters.category)}
/>
</StackItem>
<StackItem>
<Radio
id="feedback-type-1"
label="Bug"
name="type"
isChecked={filters?.category === FeedbackCategoryAPI.BUG}
onChange={() =>
handleFeedbackFilterChange('category', FeedbackCategoryAPI.BUG)
}
/>
</StackItem>
<StackItem>
<Radio
id="feedback-type-2"
label="Feedback"
name="type"
isChecked={filters?.category === FeedbackCategoryAPI.FEEDBACK}
onChange={() =>
handleFeedbackFilterChange('category', FeedbackCategoryAPI.FEEDBACK)
}
/>
</StackItem>
</Stack>
</StackItem>
<StackItem>
<Divider />
</StackItem>
<StackItem>
<Stack hasGutter style={{ '--pf-global--gutter': '0.75rem' } as CSSProperties}>
<StackItem>
<FilterTitle
title="Status"
onClear={() => handleFeedbackFilterClear('status')}
isClearable={Boolean(filters.status)}
/>
</StackItem>
<StackItem>
<Radio
id="feedback-status-1"
label="Open"
name="status"
isChecked={filters?.status === FeedbackStatusAPI.OPEN}
onChange={() =>
handleFeedbackFilterChange('status', FeedbackStatusAPI.OPEN)
}
/>
</StackItem>
<StackItem>
<Radio
id="feedback-status-2"
label="Closed"
name="status"
isChecked={filters?.status === FeedbackStatusAPI.CLOSED}
onChange={() =>
handleFeedbackFilterChange('status', FeedbackStatusAPI.CLOSED)
}
/>
</StackItem>
</Stack>
</StackItem>
<StackItem>
<Divider />
</StackItem>
</Stack>
</form>
</GridItem>
<GridItem span={9}>
<Stack hasGutter>
{isFeedbackListLoading || feedbacks?.count === 0 ? (
<EmptyState>
<EmptyStateIcon
variant={isFeedbackListLoading ? 'container' : 'icon'}
component={isFeedbackListLoading ? Spinner : undefined}
icon={CubesIcon}
/>
<Title size="lg" headingLevel="h4">
{isFeedbackListLoading ? 'Loading' : 'No feedback found!!'}
</Title>
</EmptyState>
) : (
feedbacks?.data?.map((feedback) => (
<StackItem key={feedback.id}>
<FeedbackCard
title={(feedback.createdBy as FeedbackUserProfileAPI)?.cn}
createdOn={feedback.createdOn}
description={feedback.summary}
experience={feedback.experience}
error={feedback.error}
module={feedback.module}
category={feedback.category}
state={feedback.state}
onClick={() => handlePopUpOpen('detailView', feedback)}
/>
</StackItem>
))
)}
<StackItem>
<Pagination
itemCount={feedbacks?.count}
isCompact
perPage={pagination.perPage}
page={pagination.page}
onSetPage={(_evt, newPage) => onSetPage(newPage)}
widgetId="feedback-pagination"
onPerPageSelect={(_evt, perPage) => onPerPageSelect(perPage)}
/>
</StackItem>
</Stack>
</GridItem>
</Grid>
</PageSection>
<Modal
variant={ModalVariant.small}
title={formatePopupTitle(selectedFeedback?.source, selectedFeedback?.ticketUrl)}
isOpen={popUp.detailView.isOpen}
onClose={() => handlePopUpClose('detailView')}
footer={
<Split hasGutter style={{ width: '100%' }}>
<SplitItem isFilled>
<a href={selectedFeedback?.ticketUrl} target="_blank" rel="noopener noreferrer">
<Button isSmall key="more" variant="danger">
See {selectedFeedback?.source} Issue
</Button>
</a>
</SplitItem>
<SplitItem>
<Button
key="close"
variant="tertiary"
isSmall
onClick={() => handlePopUpClose('detailView')}
>
Close
</Button>
</SplitItem>
</Split>
}
>
<FeedbackDetailCard feedback={selectedFeedback} isLoading={isFeedbackByIdQueryLoading} />
</Modal>
<Modal
variant={ModalVariant.medium}
title="All Applications"
isOpen={popUp.appList.isOpen}
onClose={() => handlePopUpClose('appList')}
>
<AppListCard
apps={appList?.apps}
filteredApps={filters.selectedApps}
onSubmit={(apps) => {
setFilters((state) => ({ ...state, selectedApps: apps }));
handlePopUpClose('appList');
}}
/>
</Modal>
</>
);
}