mirror of https://github.com/portainer/portainer
fix(ui/datatables): sync page count with filtering [EE-5890] (#10009)
parent
f24555c6c9
commit
69f3670ce5
|
@ -8,29 +8,22 @@ interface Props {
|
|||
boundaryLinks?: boolean;
|
||||
currentPage: number;
|
||||
directionLinks?: boolean;
|
||||
itemsPerPage: number;
|
||||
onPageChange(page: number): void;
|
||||
totalCount: number;
|
||||
pageCount: number;
|
||||
maxSize: number;
|
||||
isInputVisible?: boolean;
|
||||
}
|
||||
|
||||
export function PageSelector({
|
||||
currentPage,
|
||||
totalCount,
|
||||
itemsPerPage,
|
||||
pageCount,
|
||||
onPageChange,
|
||||
maxSize = 5,
|
||||
directionLinks = true,
|
||||
boundaryLinks = false,
|
||||
isInputVisible = false,
|
||||
}: Props) {
|
||||
const pages = generatePagesArray(
|
||||
currentPage,
|
||||
totalCount,
|
||||
itemsPerPage,
|
||||
maxSize
|
||||
);
|
||||
const pages = generatePagesArray(currentPage, pageCount, maxSize);
|
||||
const last = pages[pages.length - 1];
|
||||
|
||||
if (pages.length <= 1) {
|
||||
|
@ -42,7 +35,7 @@ export function PageSelector({
|
|||
{isInputVisible && (
|
||||
<PageInput
|
||||
onChange={(page) => onPageChange(page)}
|
||||
totalPages={Math.ceil(totalCount / itemsPerPage)}
|
||||
totalPages={pageCount}
|
||||
/>
|
||||
)}
|
||||
<ul className="pagination">
|
||||
|
|
|
@ -9,7 +9,7 @@ interface Props {
|
|||
page: number;
|
||||
pageLimit: number;
|
||||
showAll?: boolean;
|
||||
totalCount: number;
|
||||
pageCount: number;
|
||||
isPageInputVisible?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ export function PaginationControls({
|
|||
onPageLimitChange,
|
||||
showAll,
|
||||
onPageChange,
|
||||
totalCount,
|
||||
pageCount,
|
||||
isPageInputVisible,
|
||||
className,
|
||||
}: Props) {
|
||||
|
@ -38,8 +38,7 @@ export function PaginationControls({
|
|||
maxSize={5}
|
||||
onPageChange={onPageChange}
|
||||
currentPage={page}
|
||||
itemsPerPage={pageLimit}
|
||||
totalCount={totalCount}
|
||||
pageCount={pageCount}
|
||||
isInputVisible={isPageInputVisible}
|
||||
/>
|
||||
)}
|
||||
|
|
|
@ -12,12 +12,10 @@ export /**
|
|||
*/
|
||||
function generatePagesArray(
|
||||
currentPage: number,
|
||||
collectionLength: number,
|
||||
rowsPerPage: number,
|
||||
totalPages: number,
|
||||
paginationRange: number
|
||||
): (number | '...')[] {
|
||||
const pages: (number | '...')[] = [];
|
||||
const totalPages = Math.ceil(collectionLength / rowsPerPage);
|
||||
const halfWay = Math.ceil(paginationRange / 2);
|
||||
|
||||
let position;
|
||||
|
|
|
@ -34,6 +34,20 @@ import { createSelectColumn } from './select-column';
|
|||
import { TableRow } from './TableRow';
|
||||
import { type TableState as GlobalTableState } from './useTableState';
|
||||
|
||||
export type PaginationProps =
|
||||
| {
|
||||
isServerSidePagination?: false;
|
||||
totalCount?: never;
|
||||
page?: never;
|
||||
onPageChange?: never;
|
||||
}
|
||||
| {
|
||||
isServerSidePagination: true;
|
||||
totalCount: number;
|
||||
page: number;
|
||||
onPageChange(page: number): void;
|
||||
};
|
||||
|
||||
export interface Props<
|
||||
D extends Record<string, unknown>,
|
||||
TMeta extends TableMeta<D> = TableMeta<D>
|
||||
|
@ -50,13 +64,8 @@ export interface Props<
|
|||
titleIcon?: IconProps['icon'];
|
||||
initialTableState?: Partial<TableState>;
|
||||
isLoading?: boolean;
|
||||
totalCount?: number;
|
||||
description?: ReactNode;
|
||||
pageCount?: number;
|
||||
highlightedItemId?: string;
|
||||
page?: number;
|
||||
onPageChange?(page: number): void;
|
||||
|
||||
settingsManager: GlobalTableState<BasicTableSettings>;
|
||||
renderRow?(row: Row<D>, highlightedItemId?: string): ReactNode;
|
||||
getRowCanExpand?(row: Row<D>): boolean;
|
||||
|
@ -80,11 +89,7 @@ export function Datatable<
|
|||
emptyContentLabel,
|
||||
initialTableState = {},
|
||||
isLoading,
|
||||
totalCount = dataset.length,
|
||||
description,
|
||||
pageCount,
|
||||
page,
|
||||
onPageChange = () => null,
|
||||
settingsManager: settings,
|
||||
renderRow = defaultRenderRow,
|
||||
highlightedItemId,
|
||||
|
@ -92,8 +97,16 @@ export function Datatable<
|
|||
getRowCanExpand,
|
||||
'data-cy': dataCy,
|
||||
meta,
|
||||
}: Props<D, TMeta>) {
|
||||
const isServerSidePagination = typeof pageCount !== 'undefined';
|
||||
onPageChange = () => {},
|
||||
page,
|
||||
totalCount = dataset.length,
|
||||
isServerSidePagination = false,
|
||||
}: Props<D, TMeta> & PaginationProps) {
|
||||
const pageCount = useMemo(
|
||||
() => Math.ceil(totalCount / settings.pageSize),
|
||||
[settings.pageSize, totalCount]
|
||||
);
|
||||
|
||||
const enableRowSelection = getIsSelectionEnabled(
|
||||
disableSelect,
|
||||
isRowSelectable
|
||||
|
@ -120,6 +133,7 @@ export function Datatable<
|
|||
defaultColumn: {
|
||||
enableColumnFilter: false,
|
||||
enableHiding: true,
|
||||
sortingFn: 'alphanumeric',
|
||||
},
|
||||
enableRowSelection,
|
||||
autoResetExpanded: false,
|
||||
|
@ -127,7 +141,6 @@ export function Datatable<
|
|||
getRowId,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
getFacetedRowModel: getFacetedRowModel(),
|
||||
getFacetedUniqueValues: getFacetedUniqueValues(),
|
||||
|
@ -135,7 +148,11 @@ export function Datatable<
|
|||
getExpandedRowModel: getExpandedRowModel(),
|
||||
getRowCanExpand,
|
||||
getColumnCanGlobalFilter,
|
||||
...(isServerSidePagination ? { manualPagination: true, pageCount } : {}),
|
||||
...(isServerSidePagination
|
||||
? { manualPagination: true, pageCount }
|
||||
: {
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
}),
|
||||
meta,
|
||||
});
|
||||
|
||||
|
@ -178,7 +195,7 @@ export function Datatable<
|
|||
onPageSizeChange={handlePageSizeChange}
|
||||
page={typeof page === 'number' ? page : tableState.pagination.pageIndex}
|
||||
pageSize={tableState.pagination.pageSize}
|
||||
totalCount={totalCount}
|
||||
pageCount={tableInstance.getPageCount()}
|
||||
totalSelected={selectedItems.length}
|
||||
/>
|
||||
</Table.Container>
|
||||
|
|
|
@ -8,7 +8,7 @@ interface Props {
|
|||
pageSize: number;
|
||||
page: number;
|
||||
onPageChange(page: number): void;
|
||||
totalCount: number;
|
||||
pageCount: number;
|
||||
onPageSizeChange(pageSize: number): void;
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,7 @@ export function DatatableFooter({
|
|||
pageSize,
|
||||
page,
|
||||
onPageChange,
|
||||
totalCount,
|
||||
pageCount,
|
||||
onPageSizeChange,
|
||||
}: Props) {
|
||||
return (
|
||||
|
@ -28,7 +28,7 @@ export function DatatableFooter({
|
|||
pageLimit={pageSize}
|
||||
page={page + 1}
|
||||
onPageChange={(page) => onPageChange(page - 1)}
|
||||
totalCount={totalCount}
|
||||
pageCount={pageCount}
|
||||
onPageLimitChange={onPageSizeChange}
|
||||
/>
|
||||
</Table.Footer>
|
||||
|
|
|
@ -2,7 +2,11 @@ import { Row } from '@tanstack/react-table';
|
|||
import { ReactNode } from 'react';
|
||||
|
||||
import { ExpandableDatatableTableRow } from './ExpandableDatatableRow';
|
||||
import { Datatable, Props as DatatableProps } from './Datatable';
|
||||
import {
|
||||
Datatable,
|
||||
Props as DatatableProps,
|
||||
PaginationProps,
|
||||
} from './Datatable';
|
||||
|
||||
interface Props<D extends Record<string, unknown>>
|
||||
extends Omit<DatatableProps<D>, 'renderRow' | 'expandable'> {
|
||||
|
@ -15,7 +19,7 @@ export function ExpandableDatatable<D extends Record<string, unknown>>({
|
|||
getRowCanExpand = () => true,
|
||||
expandOnRowClick,
|
||||
...props
|
||||
}: Props<D>) {
|
||||
}: Props<D> & PaginationProps) {
|
||||
return (
|
||||
<Datatable<D>
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
|
|
|
@ -17,7 +17,6 @@ export function buildNameColumn<T extends Record<string, unknown>>(
|
|||
cell,
|
||||
enableSorting: true,
|
||||
enableHiding: false,
|
||||
sortingFn: 'text',
|
||||
};
|
||||
|
||||
function createCell<T extends Record<string, unknown>>() {
|
||||
|
|
|
@ -51,10 +51,10 @@ export function EdgeGroupAssociationTable({
|
|||
onClickRow: (env: Environment) => void;
|
||||
} & AutomationTestingProps) {
|
||||
const tableState = useTableStateWithoutStorage('Name');
|
||||
const [page, setPage] = useState(1);
|
||||
const [page, setPage] = useState(0);
|
||||
const environmentsQuery = useEnvironmentList({
|
||||
pageLimit: tableState.pageSize,
|
||||
page,
|
||||
page: page + 1,
|
||||
search: tableState.search,
|
||||
sort: tableState.sortBy.id as 'Group' | 'Name',
|
||||
order: tableState.sortBy.desc ? 'desc' : 'asc',
|
||||
|
@ -87,8 +87,10 @@ export function EdgeGroupAssociationTable({
|
|||
columns={columns}
|
||||
settingsManager={tableState}
|
||||
dataset={environments}
|
||||
isServerSidePagination
|
||||
page={page}
|
||||
onPageChange={setPage}
|
||||
pageCount={Math.ceil(totalCount / tableState.pageSize)}
|
||||
totalCount={totalCount}
|
||||
renderRow={(row) => (
|
||||
<TableRow<DecoratedEnvironment>
|
||||
cells={row.getVisibleCells()}
|
||||
|
@ -98,7 +100,6 @@ export function EdgeGroupAssociationTable({
|
|||
emptyContentLabel={emptyContentLabel}
|
||||
data-cy={dataCy}
|
||||
disableSelect
|
||||
totalCount={totalCount}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -24,8 +24,6 @@ export function Datatable() {
|
|||
search: tableState.search,
|
||||
});
|
||||
|
||||
const pageCount = Math.ceil(totalCount / tableState.pageSize);
|
||||
|
||||
return (
|
||||
<GenericDatatable
|
||||
settingsManager={tableState}
|
||||
|
@ -37,10 +35,10 @@ export function Datatable() {
|
|||
<TableActions selectedRows={selectedRows} />
|
||||
)}
|
||||
isLoading={isLoading}
|
||||
totalCount={totalCount}
|
||||
pageCount={pageCount}
|
||||
isServerSidePagination
|
||||
page={page}
|
||||
onPageChange={setPage}
|
||||
totalCount={totalCount}
|
||||
description={<Filter />}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -40,7 +40,7 @@ export function EnvironmentsDatatable() {
|
|||
(value) => (value ? parseInt(value, 10) : undefined)
|
||||
);
|
||||
const tableState = useTableStateWithoutStorage('name');
|
||||
const endpointsQuery = useEnvironmentList({
|
||||
const environmentsQuery = useEnvironmentList({
|
||||
pageLimit: tableState.pageSize,
|
||||
page: page + 1,
|
||||
search: tableState.search,
|
||||
|
@ -56,7 +56,7 @@ export function EnvironmentsDatatable() {
|
|||
const gitConfigCommitHash = edgeStackQuery.data?.GitConfig?.ConfigHash || '';
|
||||
const environments: Array<EdgeStackEnvironment> = useMemo(
|
||||
() =>
|
||||
endpointsQuery.environments.map(
|
||||
environmentsQuery.environments.map(
|
||||
(env) =>
|
||||
({
|
||||
...env,
|
||||
|
@ -72,7 +72,7 @@ export function EnvironmentsDatatable() {
|
|||
[
|
||||
currentFileVersion,
|
||||
edgeStackQuery.data?.Status,
|
||||
endpointsQuery.environments,
|
||||
environmentsQuery.environments,
|
||||
gitConfigCommitHash,
|
||||
gitConfigURL,
|
||||
]
|
||||
|
@ -81,13 +81,15 @@ export function EnvironmentsDatatable() {
|
|||
return (
|
||||
<Datatable
|
||||
columns={columns}
|
||||
isLoading={endpointsQuery.isLoading}
|
||||
isLoading={environmentsQuery.isLoading}
|
||||
dataset={environments}
|
||||
settingsManager={tableState}
|
||||
title="Environments Status"
|
||||
titleIcon={HardDrive}
|
||||
isServerSidePagination
|
||||
page={page}
|
||||
onPageChange={setPage}
|
||||
totalCount={endpointsQuery.totalCount}
|
||||
totalCount={environmentsQuery.totalCount}
|
||||
emptyContentLabel="No environment available."
|
||||
disableSelect
|
||||
description={
|
||||
|
|
|
@ -28,7 +28,6 @@ export function EventsDatatable({ data, isLoading }: EventsDatatableProps) {
|
|||
dataset={data}
|
||||
titleIcon={History}
|
||||
title="Events"
|
||||
totalCount={data.length}
|
||||
getRowId={(row) => `${row.Date}-${row.Message}-${row.Type}`}
|
||||
disableSelect
|
||||
/>
|
||||
|
|
|
@ -83,7 +83,7 @@ export function AssociateAMTDialog({
|
|||
onPageChange={setPage}
|
||||
pageLimit={pageLimit}
|
||||
onPageLimitChange={setPageLimit}
|
||||
totalCount={totalCount}
|
||||
pageCount={Math.ceil(totalCount / pageLimit)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -239,7 +239,7 @@ export function EnvironmentList({ onClickBrowse, onRefresh }: Props) {
|
|||
pageLimit={pageLimit}
|
||||
page={page}
|
||||
onPageChange={setPage}
|
||||
totalCount={totalCount}
|
||||
pageCount={Math.ceil(totalCount / pageLimit)}
|
||||
onPageLimitChange={setPageLimit}
|
||||
/>
|
||||
</TableFooter>
|
||||
|
|
|
@ -111,7 +111,7 @@ export function KubeconfigPrompt({
|
|||
onPageChange={setPage}
|
||||
pageLimit={pageLimit}
|
||||
onPageLimitChange={setPageLimit}
|
||||
totalCount={totalCount}
|
||||
pageCount={Math.ceil(totalCount / pageLimit)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -12,6 +12,7 @@ import { useTableState } from '@@/datatables/useTableState';
|
|||
|
||||
import { isBE } from '../../feature-flags/feature-flags.service';
|
||||
import { isSortType } from '../queries/useEnvironmentList';
|
||||
import { EnvironmentStatus } from '../types';
|
||||
|
||||
import { columns } from './columns';
|
||||
import { EnvironmentListItem } from './types';
|
||||
|
@ -60,10 +61,14 @@ export function EnvironmentsDatatable({
|
|||
dataset={environmentsWithGroups}
|
||||
columns={columns}
|
||||
settingsManager={tableState}
|
||||
pageCount={Math.ceil(totalCount / tableState.pageSize)}
|
||||
isServerSidePagination
|
||||
page={page}
|
||||
onPageChange={setPage}
|
||||
isLoading={isLoading}
|
||||
totalCount={totalCount}
|
||||
isLoading={isLoading}
|
||||
isRowSelectable={(row) =>
|
||||
row.original.Status !== EnvironmentStatus.Provisioning
|
||||
}
|
||||
renderTableActions={(selectedRows) => (
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
|
|
|
@ -28,6 +28,7 @@ export function AssociatedEnvironmentsSelector({
|
|||
emptyContentLabel="No environment available"
|
||||
query={{
|
||||
groupIds: [1],
|
||||
excludeIds: value,
|
||||
}}
|
||||
onClickRow={(env) => {
|
||||
if (!value.includes(env.Id)) {
|
||||
|
|
|
@ -33,10 +33,10 @@ export function GroupAssociationTable({
|
|||
onClickRow?: (env: Environment) => void;
|
||||
} & AutomationTestingProps) {
|
||||
const tableState = useTableStateWithoutStorage('Name');
|
||||
const [page, setPage] = useState(1);
|
||||
const [page, setPage] = useState(0);
|
||||
const environmentsQuery = useEnvironmentList({
|
||||
pageLimit: tableState.pageSize,
|
||||
page,
|
||||
page: page + 1,
|
||||
search: tableState.search,
|
||||
sort: tableState.sortBy.id as 'Name',
|
||||
order: tableState.sortBy.desc ? 'desc' : 'asc',
|
||||
|
@ -51,8 +51,10 @@ export function GroupAssociationTable({
|
|||
columns={columns}
|
||||
settingsManager={tableState}
|
||||
dataset={environments}
|
||||
isServerSidePagination
|
||||
page={page}
|
||||
onPageChange={setPage}
|
||||
pageCount={Math.ceil(environmentsQuery.totalCount / tableState.pageSize)}
|
||||
totalCount={environmentsQuery.totalCount}
|
||||
renderRow={(row) => (
|
||||
<TableRow<Environment>
|
||||
cells={row.getVisibleCells()}
|
||||
|
@ -62,7 +64,6 @@ export function GroupAssociationTable({
|
|||
emptyContentLabel={emptyContentLabel}
|
||||
data-cy={dataCy}
|
||||
disableSelect
|
||||
totalCount={environmentsQuery.totalCount}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -53,7 +53,6 @@ export function ListView() {
|
|||
titleIcon={Clock}
|
||||
emptyContentLabel="No schedules found"
|
||||
isLoading={listQuery.isLoading}
|
||||
totalCount={listQuery.data.length}
|
||||
renderTableActions={(selectedRows) => (
|
||||
<TableActions selectedRows={selectedRows} />
|
||||
)}
|
||||
|
|
|
@ -48,7 +48,6 @@ export function NotificationsView() {
|
|||
dataset={userNotifications}
|
||||
settingsManager={tableState}
|
||||
emptyContentLabel="No notifications found"
|
||||
totalCount={userNotifications.length}
|
||||
renderTableActions={(selectedRows) => (
|
||||
<TableActions selectedRows={selectedRows} />
|
||||
)}
|
||||
|
|
Loading…
Reference in New Issue