Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { Alert, AlertDescription, AlertTitle } from '@/vdb/components/ui/alert.j
import { Button } from '@/vdb/components/ui/button.js';
import { addCustomFields } from '@/vdb/framework/document-introspection/add-custom-fields.js';
import { graphql } from '@/vdb/graphql/graphql.js';
import { useUserSettings } from '@/vdb/hooks/use-user-settings.js';
import { validatePerPageValue } from '@/vdb/utils/pagination.js';
import { useQuery } from '@tanstack/react-query';
import { Link } from '@tanstack/react-router';
import { ColumnFiltersState, SortingState } from '@tanstack/react-table';
Expand Down Expand Up @@ -37,9 +39,10 @@ export function CollectionContentsPreviewTable({
filters: collectionFilters,
inheritFilters,
}: CollectionContentsPreviewTableProps) {
const { settings, setItemsPerPage } = useUserSettings();
const [sorting, setSorting] = useState<SortingState>([]);
const [page, setPage] = useState(1);
const [pageSize, setPageSize] = useState(10);
const [pageSize, setPageSize] = useState(validatePerPageValue(settings.itemsPerPage));
const [filters, setFilters] = useState<ColumnFiltersState>([]);
const { data: filterDefs } = useQuery(getCollectionFiltersQueryOptions);

Expand Down Expand Up @@ -106,6 +109,7 @@ export function CollectionContentsPreviewTable({
onPageChange={(_, page, perPage) => {
setPage(page);
setPageSize(perPage);
setItemsPerPage(perPage);
}}
onSortChange={(_, sorting) => {
setSorting(sorting);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { PaginatedListDataTable } from '@/vdb/components/shared/paginated-list-d
import { Button } from '@/vdb/components/ui/button.js';
import { addCustomFields } from '@/vdb/framework/document-introspection/add-custom-fields.js';
import { graphql } from '@/vdb/graphql/graphql.js';
import { useUserSettings } from '@/vdb/hooks/use-user-settings.js';
import { validatePerPageValue } from '@/vdb/utils/pagination.js';
import { Link } from '@tanstack/react-router';
import { ColumnFiltersState, SortingState } from '@tanstack/react-table';
import { useState } from 'react';
Expand Down Expand Up @@ -29,9 +31,10 @@ export interface CollectionContentsTableProps {
}

export function CollectionContentsTable({ collectionId }: Readonly<CollectionContentsTableProps>) {
const { settings, setItemsPerPage } = useUserSettings();
const [sorting, setSorting] = useState<SortingState>([]);
const [page, setPage] = useState(1);
const [pageSize, setPageSize] = useState(10);
const [pageSize, setPageSize] = useState(validatePerPageValue(settings.itemsPerPage));
const [filters, setFilters] = useState<ColumnFiltersState>([]);

return (
Expand Down Expand Up @@ -64,6 +67,7 @@ export function CollectionContentsTable({ collectionId }: Readonly<CollectionCon
onPageChange={(_, page, perPage) => {
setPage(page);
setPageSize(perPage);
setItemsPerPage(perPage);
}}
onSortChange={(_, sorting) => {
setSorting(sorting);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { Button } from '@/vdb/components/ui/button.js';
import { addCustomFields } from '@/vdb/framework/document-introspection/add-custom-fields.js';
import { api } from '@/vdb/graphql/api.js';
import { graphql } from '@/vdb/graphql/graphql.js';
import { useUserSettings } from '@/vdb/hooks/use-user-settings.js';
import { validatePerPageValue } from '@/vdb/utils/pagination.js';
import { Trans, useLingui } from '@lingui/react/macro';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { Link } from '@tanstack/react-router';
Expand Down Expand Up @@ -42,9 +44,10 @@ export function CustomerGroupMembersTable({
customerGroupId,
canAddCustomers = true,
}: CustomerGroupMembersTableProps) {
const { settings, setItemsPerPage } = useUserSettings();
const [sorting, setSorting] = useState<SortingState>([]);
const [page, setPage] = useState(1);
const [pageSize, setPageSize] = useState(10);
const [pageSize, setPageSize] = useState(validatePerPageValue(settings.itemsPerPage));
const [filters, setFilters] = useState<ColumnFiltersState>([]);
const { t } = useLingui();
const queryClient = useQueryClient();
Expand Down Expand Up @@ -77,6 +80,7 @@ export function CustomerGroupMembersTable({
onPageChange={(_, page, perPage) => {
setPage(page);
setPageSize(perPage);
setItemsPerPage(perPage);
}}
onSortChange={(_, sorting) => {
setSorting(sorting);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,17 @@ import { Link } from '@tanstack/react-router';
import { ColumnFiltersState, SortingState } from '@tanstack/react-table';
import { useState } from 'react';
import { customerOrderListDocument } from '../customers.graphql.js';
import { useUserSettings } from '@/vdb/hooks/use-user-settings.js';
import { validatePerPageValue } from '@/vdb/utils/pagination.js';

interface CustomerOrderTableProps {
customerId: string;
}

export function CustomerOrderTable({ customerId }: Readonly<CustomerOrderTableProps>) {
const { settings, setItemsPerPage } = useUserSettings();
const [page, setPage] = useState(1);
const [pageSize, setPageSize] = useState(10);
const [pageSize, setPageSize] = useState(validatePerPageValue(settings.itemsPerPage));
const [sorting, setSorting] = useState<SortingState>([{ id: 'orderPlacedAt', desc: true }]);
const [filters, setFilters] = useState<ColumnFiltersState>([]);

Expand Down Expand Up @@ -78,6 +81,7 @@ export function CustomerOrderTable({ customerId }: Readonly<CustomerOrderTablePr
onPageChange={(_, page, perPage) => {
setPage(page);
setPageSize(perPage);
setItemsPerPage(perPage);
}}
onSortChange={(_, sorting) => {
setSorting(sorting);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { PaginatedListDataTable } from '@/vdb/components/shared/paginated-list-d
import { Button } from '@/vdb/components/ui/button.js';
import { addCustomFields } from '@/vdb/framework/document-introspection/add-custom-fields.js';
import { graphql } from '@/vdb/graphql/graphql.js';
import { validatePerPageValue } from '@/vdb/utils/pagination.js';
import { useUserSettings } from '@/vdb/hooks/use-user-settings.js';
import { Trans } from '@lingui/react/macro';
import { Link } from '@tanstack/react-router';
Expand Down Expand Up @@ -35,10 +36,10 @@ export interface FacetValuesTableProps {
}

export function FacetValuesTable({ facetId, registerRefresher }: Readonly<FacetValuesTableProps>) {
const { settings, setItemsPerPage, setTableSettings } = useUserSettings();
const [sorting, setSorting] = useState<SortingState>([]);
const [page, setPage] = useState(1);
const [pageSize, setPageSize] = useState(10);
const { setTableSettings, settings } = useUserSettings();
const [pageSize, setPageSize] = useState(validatePerPageValue(settings.itemsPerPage));
const refreshRef = useRef<() => void>(() => {});

const tableSettings = pageId ? settings.tableSettings?.[pageId] : undefined;
Expand Down Expand Up @@ -67,6 +68,7 @@ export function FacetValuesTable({ facetId, registerRefresher }: Readonly<FacetV
if (pageId) {
setPageSize(perPage);
setPage(page);
setItemsPerPage(perPage);
}
}}
onSortChange={(table, sorting) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { DetailPageButton } from '@/vdb/components/shared/detail-page-button.js'
import { PaginatedListDataTable } from '@/vdb/components/shared/paginated-list-data-table.js';
import { Button } from '@/vdb/components/ui/button.js';
import { graphql } from '@/vdb/graphql/graphql.js';
import { useUserSettings } from '@/vdb/hooks/use-user-settings.js';
import { validatePerPageValue } from '@/vdb/utils/pagination.js';
import { Trans } from '@lingui/react/macro';
import { Link } from '@tanstack/react-router';
import { ColumnFiltersState, SortingState, VisibilityState } from '@tanstack/react-table';
Expand Down Expand Up @@ -33,9 +35,10 @@ export function ProductOptionsTable({
productOptionGroupId,
registerRefresher,
}: Readonly<ProductOptionsTableProps>) {
const { settings, setItemsPerPage } = useUserSettings();
const [sorting, setSorting] = useState<SortingState>([]);
const [page, setPage] = useState(1);
const [pageSize, setPageSize] = useState(10);
const [pageSize, setPageSize] = useState(validatePerPageValue(settings.itemsPerPage));
const [filters, setFilters] = useState<ColumnFiltersState>([]);
const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({
name: true,
Expand All @@ -55,6 +58,7 @@ export function ProductOptionsTable({
onPageChange={(_, page, perPage) => {
setPage(page);
setPageSize(perPage);
setItemsPerPage(perPage);
}}
onSortChange={(_, sorting) => {
setSorting(sorting);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import {
RemoveProductVariantsFromChannelBulkAction,
} from '../../_product-variants/components/product-variant-bulk-actions.js';
import { productVariantListDocument } from '../products.graphql.js';
import { useUserSettings } from '@/vdb/hooks/use-user-settings.js';
import { validatePerPageValue } from '@/vdb/utils/pagination.js';

interface ProductVariantsTableProps {
productId: string;
Expand All @@ -27,9 +29,10 @@ export function ProductVariantsTable({
registerRefresher,
fromProductDetailPage,
}: ProductVariantsTableProps) {
const { settings, setItemsPerPage } = useUserSettings();
const { formatCurrencyName } = useLocalFormat();
const [page, setPage] = useState(1);
const [pageSize, setPageSize] = useState(10);
const [pageSize, setPageSize] = useState(validatePerPageValue(settings.itemsPerPage));
const [sorting, setSorting] = useState<SortingState>([]);
const [filters, setFilters] = useState<ColumnFiltersState>([]);

Expand Down Expand Up @@ -102,6 +105,7 @@ export function ProductVariantsTable({
onPageChange={(_, page, perPage) => {
setPage(page);
setPageSize(perPage);
setItemsPerPage(perPage);
}}
onSortChange={(_, sorting) => {
setSorting(sorting);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import {
removeCountryFromZoneMutation,
zoneMembersQuery,
} from '../zones.graphql.js';
import { useUserSettings } from '@/vdb/hooks/use-user-settings.js';
import { validatePerPageValue } from '@/vdb/utils/pagination.js';

interface ZoneCountriesTableProps {
zoneId: string;
Expand All @@ -27,9 +29,10 @@ export function ZoneCountriesTable({ zoneId, canAddCountries = false }: Readonly
refetch();
},
});


const { settings, setItemsPerPage } = useUserSettings();
const [page, setPage] = useState(1);
const [pageSize, setPageSize] = useState(10);
const [pageSize, setPageSize] = useState(validatePerPageValue(settings.itemsPerPage));

const paginatedItems = useMemo(() => {
return data?.zone?.members?.slice((page - 1) * pageSize, page * pageSize);
Expand Down Expand Up @@ -58,6 +61,7 @@ export function ZoneCountriesTable({ zoneId, canAddCountries = false }: Readonly
onPageChange={(table, page, itemsPerPage) => {
setPage(page);
setPageSize(itemsPerPage);
setItemsPerPage(itemsPerPage);
}}
totalItems={data?.zone?.members?.length ?? 0}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight } from 'lucide-r

import { Button } from '@/vdb/components/ui/button.js';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/vdb/components/ui/select.js';
import { ALLOWED_PER_PAGE_VALUES } from '@/vdb/constants.js';

interface DataTablePaginationProps<TData> {
table: Table<TData>;
Expand All @@ -27,11 +28,11 @@ export function DataTablePagination<TData>({ table }: DataTablePaginationProps<T
table.setPageSize(Number(value));
}}
>
<SelectTrigger className="h-8 w-[70px]">
<SelectTrigger className="h-8 max-w-20">
<SelectValue placeholder={table.getState().pagination.pageSize} />
</SelectTrigger>
<SelectContent side="top">
{[10, 20, 30, 40, 50].map(pageSize => (
{ALLOWED_PER_PAGE_VALUES.map(pageSize => (
<SelectItem key={pageSize} value={`${pageSize}`}>
{pageSize}
</SelectItem>
Expand All @@ -40,7 +41,7 @@ export function DataTablePagination<TData>({ table }: DataTablePaginationProps<T
</Select>
</div>
<div className=" flex items-center justify-center text-sm font-medium">
<span className="hidden md:block w-[100px] ">
<span className="hidden md:block max-w-28 ">
<Trans>
Page {table.getState().pagination.pageIndex + 1} of {table.getPageCount() || 1}
</Trans>
Expand Down
25 changes: 23 additions & 2 deletions packages/dashboard/src/lib/components/data-table/data-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { DataTableBulkActions } from './data-table-bulk-actions.js';
import { DataTableProvider } from './data-table-context.js';
import { DataTableFacetedFilter, DataTableFacetedFilterOption } from './data-table-faceted-filter.js';
import { DataTableFilterBadgeEditable } from './data-table-filter-badge-editable.js';
import { DEFAULT_PER_PAGE } from '@/vdb/constants.js';

export interface FacetedFilter {
title: string;
Expand Down Expand Up @@ -120,9 +121,16 @@ export function DataTable<TData>({
const savedViewsResult = useSavedViews();
const globalViews = pageId && onFilterChange ? savedViewsResult.globalViews : [];
const { t } = useLingui();

// Calculate safe page value with validation
const pageSize = itemsPerPage ?? DEFAULT_PER_PAGE;
const totalPages = Math.max(1, Math.ceil(totalItems / pageSize));
const requestedPage = page ?? 1;
const safePage = Math.min(Math.max(requestedPage, 1), totalPages);

const [pagination, setPagination] = React.useState<PaginationState>({
pageIndex: (page ?? 1) - 1,
pageSize: itemsPerPage ?? 10,
pageIndex: safePage - 1,
pageSize: pageSize,
});
const [columnVisibility, setColumnVisibility] = React.useState<VisibilityState>(
defaultColumnVisibility ?? {},
Expand Down Expand Up @@ -173,6 +181,19 @@ export function DataTable<TData>({

const table = useReactTable(tableOptions);

// Sync pagination state when props change
useEffect(() => {
const newPageIndex = safePage - 1;
const newPageSize = pageSize;

if (pagination.pageIndex !== newPageIndex || pagination.pageSize !== newPageSize) {
setPagination({
pageIndex: newPageIndex,
pageSize: newPageSize,
});
}
}, [safePage, pageSize]);

useEffect(() => {
onPageChange?.(table, pagination.pageIndex + 1, pagination.pageSize);
}, [pagination]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { ColumnDef, Row, TableOptions, VisibilityState } from '@tanstack/table-c
import React from 'react';
import { getColumnVisibility, getStandardizedDefaultColumnOrder } from '../data-table/data-table-utils.js';
import { useGeneratedColumns } from '../data-table/use-generated-columns.js';
import { validatePageValue, validatePerPageValue } from '@/vdb/utils/pagination.js';

// Type that identifies a paginated list structure (has items array and totalItems)
type IsPaginatedList<T> = T extends { items: any[]; totalItems: number } ? true : false;
Expand Down Expand Up @@ -379,6 +380,10 @@ export function PaginatedListDataTable<
transformData,
registerRefresher,
}: Readonly<PaginatedListDataTableProps<T, U, V, AC>>) {
// Validate pagination props to prevent invalid values
const validatedPage = validatePageValue(page);
const validatedItemsPerPage = validatePerPageValue(itemsPerPage);

const [searchTerm, setSearchTerm] = React.useState<string>('');
const debouncedSearchTerm = useDebounce(searchTerm, 500);
const queryClient = useQueryClient();
Expand All @@ -394,6 +399,17 @@ export function PaginatedListDataTable<
return { ...acc, [field]: direction };
}, {});

const filter = columnFilters?.length
? {
_and: columnFilters.map(f => {
if (Array.isArray(f.value)) {
return { [f.id]: { in: f.value } };
}
return { [f.id]: f.value };
}),
}
: undefined;

function refetchPaginatedList() {
queryClient.invalidateQueries({ queryKey });
}
Expand Down Expand Up @@ -427,23 +443,12 @@ export function PaginatedListDataTable<
}));
const minimalListQuery = includeOnlySelectedListFields(extendedListQuery, visibleColumns);

const filter = columnFilters?.length
? {
_and: columnFilters.map(f => {
if (Array.isArray(f.value)) {
return { [f.id]: { in: f.value } };
}
return { [f.id]: f.value };
}),
}
: undefined;

const defaultQueryKey = [
PaginatedListDataTableKey,
minimalListQuery,
visibleColumns,
page,
itemsPerPage,
validatedPage,
validatedItemsPerPage,
sorting,
filter,
debouncedSearchTerm,
Expand All @@ -456,8 +461,8 @@ export function PaginatedListDataTable<
const mergedFilter = { ...filter, ...searchFilter };
const variables = {
options: {
take: Math.min(itemsPerPage, 100),
skip: (page - 1) * itemsPerPage,
take: Math.min(validatedItemsPerPage, 100),
skip: (validatedPage - 1) * validatedItemsPerPage,
sort,
filter: mergedFilter,
},
Expand All @@ -482,8 +487,8 @@ export function PaginatedListDataTable<
columns={columns}
data={transformedData}
isLoading={isFetching}
page={page}
itemsPerPage={itemsPerPage}
page={validatedPage}
itemsPerPage={validatedItemsPerPage}
sorting={sorting}
columnFilters={columnFilters}
totalItems={listData?.totalItems ?? 0}
Expand Down
Loading
Loading