Skip to content

Commit

Permalink
Update pagination component (#10395)
Browse files Browse the repository at this point in the history
  • Loading branch information
gustavlrsn committed May 16, 2024
1 parent 0d1fb0d commit c8a8e2a
Show file tree
Hide file tree
Showing 43 changed files with 357 additions and 335 deletions.
2 changes: 2 additions & 0 deletions components/Pagination.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// use `components/dashboard/filters/Pagination` when `useQueryFilter` is used
import qs from 'querystring';

import React from 'react';
Expand Down Expand Up @@ -152,6 +153,7 @@ function InputPagination(props: InputPaginationProps & CommonVariantProps) {
</StyledButton>
</props.PageComponent>
)}

<Container display="inline-block" mx={2}>
<FormattedMessage
id="Pagination.Count"
Expand Down
10 changes: 5 additions & 5 deletions components/dashboard/DashboardSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ const DashboardSection = ({ account, isLoading, section, subpath }) => {

if (isLoading) {
return (
<div className="w-full">
<div className="w-full pb-6">
<OCFBannerWithData isDashboard collective={account} hideNextSteps={section === 'host'} />
<LoadingPlaceholder height={26} mb={4} maxWidth={500} />
<LoadingPlaceholder height={300} />
Expand All @@ -92,7 +92,7 @@ const DashboardSection = ({ account, isLoading, section, subpath }) => {
}
}
return (
<div className="w-full">
<div className="w-full pb-6">
<OCFBannerWithData isDashboard collective={account} hideNextSteps={section === 'host'} />
<DashboardComponent accountSlug={account.slug} subpath={subpath} isDashboard />
</div>
Expand All @@ -101,7 +101,7 @@ const DashboardSection = ({ account, isLoading, section, subpath }) => {

if (values(LEGACY_SECTIONS).includes(section)) {
return (
<div className="w-full max-w-screen-lg">
<div className="w-full max-w-screen-lg pb-6">
<OCFBannerWithData isDashboard collective={account} hideNextSteps={section === 'host'} />
{SECTION_LABELS[section] && <DashboardHeader className="mb-2" title={formatMessage(SECTION_LABELS[section])} />}

Expand All @@ -115,7 +115,7 @@ const DashboardSection = ({ account, isLoading, section, subpath }) => {
if (SettingsComponent) {
return (
// <div className="flex max-w-screen-lg justify-center">
<div className="max-w-screen-md flex-1">
<div className="max-w-screen-md flex-1 pb-6">
<OCFBannerWithData isDashboard collective={account} hideNextSteps={section === 'host'} />
<SettingsComponent account={account} accountSlug={account.slug} subpath={subpath} />
</div>
Expand All @@ -125,7 +125,7 @@ const DashboardSection = ({ account, isLoading, section, subpath }) => {
if (values(LEGACY_SETTINGS_SECTIONS).includes(section)) {
return (
// <div className="flex max-w-screen-lg justify-center">
<div className="max-w-screen-md flex-1">
<div className="max-w-screen-md flex-1 pb-6">
<OCFBannerWithData isDashboard collective={account} hideNextSteps={section === 'host'} />
{SECTION_LABELS[section] && <DashboardHeader className="mb-2" title={formatMessage(SECTION_LABELS[section])} />}

Expand Down
176 changes: 176 additions & 0 deletions components/dashboard/filters/Pagination.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
import React from 'react';
import * as SelectPrimitive from '@radix-ui/react-select';
import { uniq } from 'lodash';
import { ChevronDown } from 'lucide-react';
import { FormattedMessage } from 'react-intl';

import { usePrevious } from '../../../lib/hooks/usePrevious';

import { Button } from '../../ui/Button';
import {
Pagination as UIPagination,
PaginationButton,
PaginationContent,
PaginationEllipsis,
PaginationItem,
PaginationNext,
PaginationPrevious,
} from '../../ui/Pagination';
import { Select, SelectContent, SelectItem } from '../../ui/Select';

function SelectLimit({ limit, setLimit, defaultLimit }) {
const options = uniq([20, 40, 60, 80, 100, limit, defaultLimit].filter(Boolean)).sort((a, b) => a - b);

return (
<Select value={String(limit)} onValueChange={v => setLimit(v)}>
<SelectPrimitive.Trigger asChild>
<Button variant="outline" size="sm" className="shrink-0">
<FormattedMessage defaultMessage="{rowCount} per page" id="Pagination.PerPage" values={{ rowCount: limit }} />
<SelectPrimitive.Icon asChild>
<ChevronDown className="h-4 w-4 flex-shrink-0 opacity-50" />
</SelectPrimitive.Icon>
</Button>
</SelectPrimitive.Trigger>

<SelectContent>
{options.map(option => (
<SelectItem key={option} value={String(option)} onClick={() => setLimit(option)}>
{option}
</SelectItem>
))}
</SelectContent>
</Select>
);
}

export function Pagination({ total, queryFilter }) {
const { offset, limit } = queryFilter.values;
const defaultLimit = queryFilter.defaultSchemaValues.limit;
const prevTotalCount = usePrevious(total);
total = total ?? prevTotalCount;
const currentPage = offset / limit + 1;
const totalPages = Math.ceil((total || 1) / limit);
const hasPrevious = currentPage > 1;
const hasNext = currentPage < totalPages;
const goToPage = page => {
if (page < 1 || page > totalPages) {
return;
}
const offset = (page - 1) * limit;
queryFilter.setFilter('offset', offset);
};

// Show pagination if there is more than 1 page or limit is not default (i.e. the user has changed it)
const showPagination = totalPages > 1 || limit !== defaultLimit;
if (!showPagination) {
return null;
}

const renderPageNumbers = () => {
const pages = [];

if (totalPages < 1) {
return pages;
}

// Always show the first page
pages.push(
<PaginationItem key={1}>
<PaginationButton size="icon-sm" onClick={() => goToPage(1)} isActive={currentPage === 1}>
1
</PaginationButton>
</PaginationItem>,
);

let startPage, endPage;
if (totalPages <= 7) {
startPage = 2;
endPage = totalPages - 1;
} else {
if (currentPage <= 4) {
startPage = 2;
endPage = 5;
} else if (currentPage + 3 >= totalPages) {
startPage = totalPages - 4;
endPage = totalPages - 1;
} else {
startPage = currentPage - 1;
endPage = currentPage + 1;
}
}

if (startPage > 2) {
pages.push(
<PaginationItem key="start-ellipsis">
<PaginationEllipsis />
</PaginationItem>,
);
}

for (let i = startPage; i <= endPage; i++) {
pages.push(
<PaginationItem key={i}>
<PaginationButton size="icon-sm" onClick={() => goToPage(i)} isActive={currentPage === i}>
{i}
</PaginationButton>
</PaginationItem>,
);
}

if (endPage < totalPages - 1) {
pages.push(
<PaginationItem key="end-ellipsis">
<PaginationEllipsis />
</PaginationItem>,
);
}

// Always show the last page
if (totalPages > 1) {
pages.push(
<PaginationItem key={totalPages}>
<PaginationButton size="icon-sm" onClick={() => goToPage(totalPages)} isActive={currentPage === totalPages}>
{totalPages}
</PaginationButton>
</PaginationItem>,
);
}

return pages;
};
return (
<div className="flex items-center justify-between gap-1">
<UIPagination className="flex-1 items-center justify-between sm:flex">
<div className="hidden lg:block">
<SelectLimit limit={limit} defaultLimit={defaultLimit} setLimit={l => queryFilter.setFilter('limit', l)} />
</div>

<div className="block text-sm font-medium text-muted-foreground lg:hidden">
<FormattedMessage
id="Pagination.Count"
defaultMessage="Page {current} of {total}"
values={{
current: currentPage,
total: totalPages,
}}
/>
</div>

<PaginationContent className="hidden lg:flex">{renderPageNumbers()}</PaginationContent>
<PaginationContent>
<PaginationItem>
<PaginationPrevious
size="sm"
variant="outline"
onClick={() => goToPage(currentPage - 1)}
disabled={!hasPrevious}
/>
</PaginationItem>
<PaginationItem>
<PaginationNext size="sm" variant="outline" onClick={() => goToPage(currentPage + 1)} disabled={!hasNext} />
</PaginationItem>
</PaginationContent>
</UIPagination>
</div>
);
}
14 changes: 2 additions & 12 deletions components/dashboard/sections/Contributors.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React from 'react';
import { gql, useQuery } from '@apollo/client';
import { toNumber } from 'lodash';
import { defineMessage, FormattedMessage, useIntl } from 'react-intl';
import { z } from 'zod';

Expand All @@ -19,13 +18,13 @@ import MessageBoxGraphqlError from '../../MessageBoxGraphqlError';
import { DataTable } from '../../table/DataTable';
import { Span } from '../../Text';
import { Button } from '../../ui/Button';
import { Pagination } from '../../ui/Pagination';
import DashboardHeader from '../DashboardHeader';
import { EmptyResults } from '../EmptyResults';
import ComboSelectFilter from '../filters/ComboSelectFilter';
import { emailFilter } from '../filters/EmailFilter';
import { Filterbar } from '../filters/Filterbar';
import { orderByFilter } from '../filters/OrderFilter';
import { Pagination } from '../filters/Pagination';
import { DashboardSectionProps } from '../types';

// type FilterMemberRole = MemberRole.FOLLOWER | MemberRole.BACKER | MemberRole.CONTRIBUTOR
Expand Down Expand Up @@ -257,7 +256,6 @@ const Contributors = ({ accountSlug }: ContributorsProps) => {

const {
data,
previousData,
loading: queryLoading,
error: queryError,
} = useQuery(dashboardContributorsQuery, {
Expand All @@ -269,10 +267,6 @@ const Contributors = ({ accountSlug }: ContributorsProps) => {
context: API_V2_CONTEXT,
});

const { limit, offset } = queryFilter.values;
const pages = Math.ceil(((data || previousData)?.account?.members.totalCount || 1) / limit);
const currentPage = toNumber(offset + limit) / limit;

const contributors = data?.account?.members.nodes || [];

const loading = metadataLoading || queryLoading;
Expand Down Expand Up @@ -329,11 +323,7 @@ const Contributors = ({ accountSlug }: ContributorsProps) => {
mobileTableView
nbPlaceholders={nbPlaceholders}
/>
<Pagination
totalPages={pages}
page={currentPage}
onChange={page => queryFilter.setFilter('offset', (page - 1) * limit)}
/>
<Pagination queryFilter={queryFilter} total={data?.account?.members.totalCount} />
</div>
)}
</div>
Expand Down
26 changes: 3 additions & 23 deletions components/dashboard/sections/HostDashboardAgreements.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,13 @@ import AgreementDrawer from '../../agreements/AgreementDrawer';
import AgreementsTable from '../../agreements/AgreementsTable';
import { AGREEMENT_VIEW_FIELDS_FRAGMENT } from '../../agreements/fragments';
import FilesViewerModal from '../../FilesViewerModal';
import { Flex } from '../../Grid';
import MessageBoxGraphqlError from '../../MessageBoxGraphqlError';
import Pagination from '../../Pagination';
import { Button } from '../../ui/Button';
import DashboardHeader from '../DashboardHeader';
import { EmptyResults } from '../EmptyResults';
import { Filterbar } from '../filters/Filterbar';
import { hostedAccountFilter } from '../filters/HostedAccountFilter';
import { Pagination } from '../filters/Pagination';
import { DashboardSectionProps } from '../types';

const hostDashboardAgreementsQuery = gql`
Expand Down Expand Up @@ -63,13 +62,6 @@ const filters: FilterComponentConfigs<z.infer<typeof schema>, FilterMeta> = {
account: hostedAccountFilter.filter,
};

const IGNORED_QUERY_PARAMS = ['slug', 'section'];

const hasPagination = (data, queryVariables): boolean => {
const totalCount = data?.host?.hostedAccountAgreements?.totalCount;
return Boolean(queryVariables.offset || (totalCount && totalCount > NB_AGREEMENTS_DISPLAYED));
};

const HostDashboardAgreements = ({ accountSlug: hostSlug }: DashboardSectionProps) => {
const { LoggedInUser, loadingLoggedInUser } = useLoggedInUser();
const [agreementDrawerOpen, setAgreementDrawerOpen] = React.useState(false);
Expand All @@ -83,7 +75,7 @@ const HostDashboardAgreements = ({ accountSlug: hostSlug }: DashboardSectionProp
meta: { hostSlug },
});

const { data, previousData, error, variables, loading, refetch } = useQuery(hostDashboardAgreementsQuery, {
const { data, error, variables, loading, refetch } = useQuery(hostDashboardAgreementsQuery, {
variables: { hostSlug, ...queryFilter.variables },
context: API_V2_CONTEXT,
});
Expand Down Expand Up @@ -137,19 +129,7 @@ const HostDashboardAgreements = ({ accountSlug: hostSlug }: DashboardSectionProp
setAgreementInDrawer(agreement);
}}
/>

<Flex my={4} justifyContent="center">
{hasPagination(data || previousData, variables) && (
<Pagination
variant="input"
offset={variables.offset}
limit={variables.limit}
total={(data || previousData)?.host?.hostedAccountAgreements?.totalCount || 0}
ignoredQueryParams={IGNORED_QUERY_PARAMS}
isDisabled={loading}
/>
)}
</Flex>
<Pagination queryFilter={queryFilter} total={data?.host?.hostedAccountAgreements?.totalCount} />
</React.Fragment>
)}
<AgreementDrawer
Expand Down
21 changes: 2 additions & 19 deletions components/dashboard/sections/HostVirtualCardRequests.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,8 @@ import { i18nVirtualCardRequestStatus } from '../../../lib/i18n/virtual-card-req
import { sortSelectOptions } from '../../../lib/utils';

import { accountHoverCardFields } from '../../AccountHoverCard';
import { Flex } from '../../Grid';
import { getI18nLink } from '../../I18nFormatters';
import MessageBoxGraphqlError from '../../MessageBoxGraphqlError';
import Pagination from '../../Pagination';
import { P } from '../../Text';
import { VirtualCardRequestDrawer } from '../../virtual-card-requests/VirtualCardRequestDrawer';
import { VirtualCardRequestsTable } from '../../virtual-card-requests/VirtualCardRequestsTable';
import { StripeVirtualCardComplianceStatement } from '../../virtual-cards/StripeVirtualCardComplianceStatement';
Expand All @@ -31,6 +28,7 @@ import { EmptyResults } from '../EmptyResults';
import ComboSelectFilter from '../filters/ComboSelectFilter';
import { Filterbar } from '../filters/Filterbar';
import { hostedAccountFilter } from '../filters/HostedAccountFilter';
import { Pagination } from '../filters/Pagination';
import { DashboardSectionProps } from '../types';

const schema = z.object({
Expand Down Expand Up @@ -241,22 +239,7 @@ export default function HostVirtualCardRequests({ accountSlug: hostSlug }: Dashb
loading={query.loading}
virtualCardRequests={query.data?.virtualCardRequests.nodes}
/>
<Flex mt={5} alignItems="center" flexDirection="column" justifyContent="center">
<Pagination
total={query.data?.virtualCardRequests.totalCount}
limit={queryFilter.values.limit}
offset={queryFilter.values.offset}
onPageChange={page => queryFilter.setFilter('offset', (page - 1) * queryFilter.values.limit)}
/>
<P mt={1} fontSize="12px">
<FormattedMessage
id="withColon"
defaultMessage="{item}:"
values={{ item: <FormattedMessage id="TotalItems" defaultMessage="Total Items" /> }}
/>{' '}
{query.data?.virtualCardRequests.totalCount}
</P>
</Flex>
<Pagination queryFilter={queryFilter} total={query.data?.virtualCardRequests.totalCount} />

<VirtualCardRequestDrawer
open={!!queryFilter.values.virtualCardRequest}
Expand Down

0 comments on commit c8a8e2a

Please sign in to comment.