mirror of
https://github.com/luxfi/explore.git
synced 2026-09-24 00:28:18 +00:00
blocks and block txs
This commit is contained in:
+2
-2
@@ -7,7 +7,7 @@ import type { ApiResource } from './resources';
|
||||
export default function buildUrl(
|
||||
resource: ApiResource,
|
||||
pathParams?: Record<string, string>,
|
||||
queryParams?: Record<string, string | undefined>,
|
||||
queryParams?: Record<string, string | number | undefined>,
|
||||
) {
|
||||
// FIXME
|
||||
// 1. I was not able to figure out how to send CORS with credentials from localhost
|
||||
@@ -28,7 +28,7 @@ export default function buildUrl(
|
||||
const url = new URL(compile(path)(pathParams), baseUrl);
|
||||
|
||||
queryParams && Object.entries(queryParams).forEach(([ key, value ]) => {
|
||||
value && url.searchParams.append(key, value);
|
||||
value && url.searchParams.append(key, String(value));
|
||||
});
|
||||
|
||||
return url.toString();
|
||||
|
||||
+25
-1
@@ -1,3 +1,5 @@
|
||||
import type ArrayElement from 'types/utils/ArrayElement';
|
||||
|
||||
import appConfig from 'configs/app/config';
|
||||
|
||||
export interface ApiResource {
|
||||
@@ -7,7 +9,7 @@ export interface ApiResource {
|
||||
}
|
||||
|
||||
export const RESOURCES = {
|
||||
// account
|
||||
// ACCOUNT
|
||||
user_info: {
|
||||
path: '/api/account/v1/user/info',
|
||||
},
|
||||
@@ -42,12 +44,34 @@ export const RESOURCES = {
|
||||
basePath: appConfig.statsApi.basePath,
|
||||
},
|
||||
|
||||
// BLOCKS, TXS
|
||||
blocks: {
|
||||
path: '/api/v2/blocks',
|
||||
paginationFields: [ 'block_number' as const, 'items_count' as const ],
|
||||
filterFields: [ 'type' as const ],
|
||||
},
|
||||
block_txs: {
|
||||
path: '/api/v2/blocks/:id/transactions',
|
||||
paginationFields: [ 'block_number' as const, 'items_count' as const, 'index' as const ],
|
||||
filterFields: [],
|
||||
},
|
||||
|
||||
// DEPRECATED
|
||||
old_api: {
|
||||
path: '/api',
|
||||
},
|
||||
};
|
||||
|
||||
export type ResourceName = keyof typeof RESOURCES;
|
||||
|
||||
export type ResourceFiltersKey<R extends ResourceName> = typeof RESOURCES[R] extends {filterFields: Array<unknown>} ?
|
||||
ArrayElement<typeof RESOURCES[R]['filterFields']> :
|
||||
never;
|
||||
|
||||
export type ResourcePaginationKey<R extends ResourceName> = typeof RESOURCES[R] extends {paginationFields: Array<unknown>} ?
|
||||
ArrayElement<typeof RESOURCES[R]['paginationFields']> :
|
||||
never;
|
||||
|
||||
export const resourceKey = (x: keyof typeof RESOURCES) => x;
|
||||
|
||||
export interface ResourceError<T = unknown> {
|
||||
|
||||
@@ -10,7 +10,7 @@ import type { ResourceError, ApiResource } from './resources';
|
||||
|
||||
export interface Params {
|
||||
pathParams?: Record<string, string>;
|
||||
queryParams?: Record<string, string | undefined>;
|
||||
queryParams?: Record<string, string | number | undefined>;
|
||||
fetchParams?: Pick<FetchParams, 'body' | 'method'>;
|
||||
}
|
||||
|
||||
|
||||
+16
-5
@@ -2,24 +2,33 @@ import type { UseQueryOptions } from '@tanstack/react-query';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
import type { UserInfo, CustomAbis, PublicTags, AddressTags, TransactionTags, ApiKeys, WatchlistAddress } from 'types/api/account';
|
||||
import type { BlocksResponse, BlockTransactionsResponse } from 'types/api/block';
|
||||
import type { Stats, Charts } from 'types/api/stats';
|
||||
|
||||
import type { RESOURCES, ResourceError } from './resources';
|
||||
import type { RESOURCES, ResourceError, ResourceName } from './resources';
|
||||
import type { Params as ApiFetchParams } from './useApiFetch';
|
||||
import useApiFetch from './useApiFetch';
|
||||
|
||||
interface Params<R extends keyof typeof RESOURCES> extends ApiFetchParams {
|
||||
export interface Params<R extends ResourceName> extends ApiFetchParams {
|
||||
queryOptions?: Omit<UseQueryOptions<unknown, ResourceError, ResourcePayload<R>>, 'queryKey' | 'queryFn'>;
|
||||
}
|
||||
|
||||
export default function useApiQuery<R extends keyof typeof RESOURCES>(
|
||||
export function getResourceKey<R extends ResourceName>(resource: R, { pathParams, queryParams }: Params<R> = {}) {
|
||||
if (pathParams || queryParams) {
|
||||
return [ resource, { ...pathParams, ...queryParams } ];
|
||||
}
|
||||
|
||||
return [ resource ];
|
||||
}
|
||||
|
||||
export default function useApiQuery<R extends ResourceName>(
|
||||
resource: R,
|
||||
{ queryOptions, pathParams, queryParams, fetchParams }: Params<R> = {},
|
||||
) {
|
||||
const apiFetch = useApiFetch();
|
||||
|
||||
return useQuery<unknown, ResourceError, ResourcePayload<R>>(
|
||||
pathParams || queryParams ? [ resource, { ...pathParams, ...queryParams } ] : [ resource ],
|
||||
getResourceKey(resource, { pathParams, queryParams }),
|
||||
async() => {
|
||||
return apiFetch<R, ResourcePayload<R>, ResourceError>(resource, { pathParams, queryParams, fetchParams });
|
||||
}, queryOptions);
|
||||
@@ -35,4 +44,6 @@ export type ResourcePayload<Q extends keyof typeof RESOURCES> =
|
||||
Q extends 'watchlist' ? Array<WatchlistAddress> :
|
||||
Q extends 'stats_counters' ? Stats :
|
||||
Q extends 'stats_charts' ? Charts :
|
||||
never;
|
||||
Q extends 'blocks' ? BlocksResponse :
|
||||
Q extends 'block_txs' ? BlockTransactionsResponse :
|
||||
never;
|
||||
|
||||
@@ -1,66 +1,63 @@
|
||||
import type { UseQueryOptions } from '@tanstack/react-query';
|
||||
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import { pick, omit } from 'lodash';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import mapValues from 'lodash/mapValues';
|
||||
import omit from 'lodash/omit';
|
||||
import pick from 'lodash/pick';
|
||||
import { useRouter } from 'next/router';
|
||||
import React, { useCallback } from 'react';
|
||||
import { animateScroll, scroller } from 'react-scroll';
|
||||
|
||||
import { PAGINATION_FIELDS, PAGINATION_FILTERS_FIELDS } from 'types/api/pagination';
|
||||
import type { PaginationParams, PaginatedResponse, PaginatedQueryKeys, PaginationFilters } from 'types/api/pagination';
|
||||
import type { PaginatedResponseX, PaginatedResources, PaginationFiltersX } from 'types/api/pagination';
|
||||
|
||||
import useFetch from 'lib/hooks/useFetch';
|
||||
import { RESOURCES } from 'lib/api/resources';
|
||||
import type { Params as UseApiQueryParams } from 'lib/api/useApiQuery';
|
||||
import useApiQuery from 'lib/api/useApiQuery';
|
||||
|
||||
interface Params<QueryName extends PaginatedQueryKeys> {
|
||||
apiPath: string;
|
||||
queryName: QueryName;
|
||||
queryIds?: Array<string>;
|
||||
filters?: PaginationFilters<QueryName>;
|
||||
options?: Omit<UseQueryOptions<unknown, unknown, PaginatedResponse<QueryName>>, 'queryKey' | 'queryFn'>;
|
||||
interface Params<Resource extends PaginatedResources> {
|
||||
resourceName: Resource;
|
||||
options?: UseApiQueryParams<Resource>['queryOptions'];
|
||||
pathParams?: Record<string, string>;
|
||||
filters?: PaginationFiltersX<Resource>;
|
||||
scroll?: { elem: string; offset: number };
|
||||
}
|
||||
|
||||
export default function useQueryWithPages<QueryName extends PaginatedQueryKeys>({
|
||||
queryName,
|
||||
export default function useQueryWithPages<Resource extends PaginatedResources>({
|
||||
resourceName,
|
||||
filters,
|
||||
options,
|
||||
apiPath,
|
||||
queryIds,
|
||||
pathParams,
|
||||
scroll,
|
||||
}: Params<QueryName>) {
|
||||
const paginationFields = PAGINATION_FIELDS[queryName];
|
||||
}: Params<Resource>) {
|
||||
const resource = RESOURCES[resourceName];
|
||||
const queryClient = useQueryClient();
|
||||
const router = useRouter();
|
||||
|
||||
type NextPageParams = {
|
||||
[K in keyof PaginatedResponseX<Resource>['next_page_params']]: string;
|
||||
}
|
||||
const currPageParams = mapValues(pick(router.query, resource.paginationFields), (value) => value?.toString()) as NextPageParams;
|
||||
|
||||
const [ page, setPage ] = React.useState<number>(router.query.page && !Array.isArray(router.query.page) ? Number(router.query.page) : 1);
|
||||
const currPageParams = pick(router.query, paginationFields);
|
||||
const [ pageParams, setPageParams ] = React.useState<Array<PaginationParams<QueryName>>>([ ]);
|
||||
const fetch = useFetch();
|
||||
const isMounted = React.useRef(false);
|
||||
const canGoBackwards = React.useRef(!router.query.page);
|
||||
const [ pageParams, setPageParams ] = React.useState<Record<number, NextPageParams>>({
|
||||
[page]: currPageParams,
|
||||
});
|
||||
const [ hasPagination, setHasPagination ] = React.useState(page > 1);
|
||||
|
||||
const queryKey = [ queryName, ...(queryIds || []), { page, filters } ];
|
||||
const isMounted = React.useRef(false);
|
||||
const canGoBackwards = React.useRef(!router.query.page);
|
||||
const queryParams = { ...filters, ...pageParams[page] };
|
||||
|
||||
const scrollToTop = useCallback(() => {
|
||||
scroll ? scroller.scrollTo(scroll.elem, { offset: scroll.offset }) : animateScroll.scrollToTop({ duration: 0 });
|
||||
}, [ scroll ]);
|
||||
|
||||
const queryResult = useQuery<unknown, unknown, PaginatedResponse<QueryName>>(
|
||||
queryKey,
|
||||
async() => {
|
||||
const params: Array<string> = [];
|
||||
|
||||
Object.entries({ ...filters, ...currPageParams }).forEach(([ key, val ]) => {
|
||||
if (Array.isArray(val)) {
|
||||
val.length && params.push(`${ key }=${ val.join(',') }`);
|
||||
} else if (val) {
|
||||
params.push(`${ key }=${ val }`);
|
||||
}
|
||||
});
|
||||
|
||||
return fetch(`${ apiPath }${ params.length ? '?' + params.join('&') : '' }`);
|
||||
const queryResult = useApiQuery(resourceName, {
|
||||
pathParams,
|
||||
queryParams,
|
||||
queryOptions: {
|
||||
staleTime: page === 1 ? 0 : Infinity,
|
||||
...options,
|
||||
},
|
||||
{ staleTime: page === 1 ? 0 : Infinity, ...options },
|
||||
);
|
||||
});
|
||||
const { data } = queryResult;
|
||||
|
||||
const onNextPageClick = useCallback(() => {
|
||||
@@ -69,9 +66,13 @@ export default function useQueryWithPages<QueryName extends PaginatedQueryKeys>(
|
||||
return;
|
||||
}
|
||||
const nextPageParams = data.next_page_params;
|
||||
if (page >= pageParams.length && data?.next_page_params) {
|
||||
setPageParams(prev => [ ...prev, nextPageParams ]);
|
||||
}
|
||||
|
||||
setPageParams((prev) => ({
|
||||
...prev,
|
||||
[page + 1]: mapValues(nextPageParams, (value) => value?.toString()) as NextPageParams,
|
||||
}));
|
||||
setPage(prev => prev + 1);
|
||||
|
||||
const nextPageQuery = { ...router.query };
|
||||
Object.entries(nextPageParams).forEach(([ key, val ]) => nextPageQuery[key] = val.toString());
|
||||
nextPageQuery.page = String(page + 1);
|
||||
@@ -80,53 +81,52 @@ export default function useQueryWithPages<QueryName extends PaginatedQueryKeys>(
|
||||
router.push({ pathname: router.pathname, query: nextPageQuery }, undefined, { shallow: true })
|
||||
.then(() => {
|
||||
scrollToTop();
|
||||
setPage(prev => prev + 1);
|
||||
});
|
||||
}, [ data?.next_page_params, page, pageParams.length, router, scrollToTop ]);
|
||||
}, [ data?.next_page_params, page, router, scrollToTop ]);
|
||||
|
||||
const onPrevPageClick = useCallback(() => {
|
||||
// returning to the first page
|
||||
// we dont have pagination params for the first page
|
||||
let nextPageQuery: typeof router.query = { ...router.query };
|
||||
if (page === 2) {
|
||||
nextPageQuery = omit(router.query, paginationFields, 'page');
|
||||
nextPageQuery = omit(router.query, resource.paginationFields, 'page');
|
||||
canGoBackwards.current = true;
|
||||
} else {
|
||||
const nextPageParams = { ...pageParams[page - 2] };
|
||||
nextPageParams && Object.entries(nextPageParams).forEach(([ key, val ]) => nextPageQuery[key] = val.toString());
|
||||
const nextPageParams = pageParams[page - 1];
|
||||
nextPageParams && Object.entries(nextPageParams).forEach(([ key, val ]) => nextPageQuery[key] = String(val));
|
||||
nextPageQuery.page = String(page - 1);
|
||||
}
|
||||
router.query = nextPageQuery;
|
||||
|
||||
router.push({ pathname: router.pathname, query: nextPageQuery }, undefined, { shallow: true })
|
||||
.then(() => {
|
||||
scrollToTop();
|
||||
setPage(prev => prev - 1);
|
||||
page === 2 && queryClient.removeQueries({ queryKey: [ queryName ] });
|
||||
page === 2 && queryClient.removeQueries({ queryKey: [ resourceName ] });
|
||||
});
|
||||
setHasPagination(true);
|
||||
}, [ router, page, paginationFields, pageParams, queryClient, scrollToTop, queryName ]);
|
||||
}, [ router, page, resource.paginationFields, pageParams, scrollToTop, queryClient, resourceName ]);
|
||||
|
||||
const resetPage = useCallback(() => {
|
||||
queryClient.removeQueries({ queryKey: [ queryName ] });
|
||||
queryClient.removeQueries({ queryKey: [ resourceName ] });
|
||||
|
||||
router.push({ pathname: router.pathname, query: omit(router.query, paginationFields, 'page') }, undefined, { shallow: true }).then(() => {
|
||||
queryClient.removeQueries({ queryKey: [ queryName ] });
|
||||
router.push({ pathname: router.pathname, query: omit(router.query, resource.paginationFields, 'page') }, undefined, { shallow: true }).then(() => {
|
||||
queryClient.removeQueries({ queryKey: [ resourceName ] });
|
||||
scrollToTop();
|
||||
setPage(1);
|
||||
setPageParams([ ]);
|
||||
setPageParams({});
|
||||
canGoBackwards.current = true;
|
||||
window.setTimeout(() => {
|
||||
// FIXME after router is updated we still have inactive queries for previously visited page (e.g third), where we came from
|
||||
// so have to remove it but with some delay :)
|
||||
queryClient.removeQueries({ queryKey: [ queryName ], type: 'inactive' });
|
||||
queryClient.removeQueries({ queryKey: [ resourceName ], type: 'inactive' });
|
||||
}, 100);
|
||||
});
|
||||
|
||||
setHasPagination(true);
|
||||
}, [ queryClient, queryName, router, paginationFields, scrollToTop ]);
|
||||
}, [ queryClient, resourceName, router, resource.paginationFields, scrollToTop ]);
|
||||
|
||||
const onFilterChange = useCallback((newFilters: PaginationFilters<QueryName> | undefined) => {
|
||||
const newQuery = omit(router.query, PAGINATION_FIELDS[queryName], 'page', PAGINATION_FILTERS_FIELDS[queryName]);
|
||||
const onFilterChange = useCallback((newFilters: PaginationFiltersX<Resource> | undefined) => {
|
||||
const newQuery = omit(router.query, resource.paginationFields, 'page', resource.filterFields);
|
||||
if (newFilters) {
|
||||
Object.entries(newFilters).forEach(([ key, value ]) => {
|
||||
if (value && value.length) {
|
||||
@@ -143,12 +143,12 @@ export default function useQueryWithPages<QueryName extends PaginatedQueryKeys>(
|
||||
{ shallow: true },
|
||||
).then(() => {
|
||||
setPage(1);
|
||||
setPageParams([ ]);
|
||||
setPageParams({});
|
||||
scrollToTop();
|
||||
});
|
||||
}, [ queryName, router, scrollToTop, setPageParams, setPage ]);
|
||||
}, [ router, resource.paginationFields, resource.filterFields, scrollToTop ]);
|
||||
|
||||
const hasPaginationParams = Object.keys(currPageParams).length > 0;
|
||||
const hasPaginationParams = Object.keys(currPageParams || {}).length > 0;
|
||||
const nextPageParams = data?.next_page_params;
|
||||
|
||||
const pagination = {
|
||||
@@ -165,12 +165,12 @@ export default function useQueryWithPages<QueryName extends PaginatedQueryKeys>(
|
||||
|
||||
React.useEffect(() => {
|
||||
if (page !== 1 && isMounted.current) {
|
||||
queryClient.cancelQueries({ queryKey });
|
||||
queryClient.cancelQueries({ queryKey: [ resourceName ] });
|
||||
setPage(1);
|
||||
}
|
||||
// hook should run only when queryName has changed
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [ queryName ]);
|
||||
}, [ resourceName ]);
|
||||
|
||||
React.useEffect(() => {
|
||||
window.setTimeout(() => {
|
||||
|
||||
@@ -32,6 +32,17 @@ export type PaginatedQueryKeys =
|
||||
QueryKeys.addressCoinBalanceHistory |
|
||||
QueryKeys.addressBlocksValidated;
|
||||
|
||||
export type PaginatedResources = 'blocks' | 'block_txs';
|
||||
|
||||
export type PaginatedResponseX<Q extends PaginatedResources> =
|
||||
Q extends 'blocks' ? BlocksResponse :
|
||||
Q extends 'block_txs' ? BlockTransactionsResponse :
|
||||
never;
|
||||
|
||||
export type PaginationFiltersX<Q extends PaginatedResources> =
|
||||
Q extends 'blocks' ? BlockFilters :
|
||||
never;
|
||||
|
||||
export type PaginatedResponse<Q extends PaginatedQueryKeys> =
|
||||
Q extends QueryKeys.addressInternalTxs ? AddressInternalTxsResponse :
|
||||
Q extends QueryKeys.addressTxs ? AddressTransactionsResponse :
|
||||
|
||||
@@ -21,6 +21,7 @@ import SkeletonTable from 'ui/shared/skeletons/SkeletonTable';
|
||||
|
||||
type QueryResult = UseQueryResult<BlocksResponse> & {
|
||||
pagination: PaginationProps;
|
||||
isPaginationVisible: boolean;
|
||||
};
|
||||
|
||||
interface Props {
|
||||
@@ -110,11 +111,9 @@ const BlocksContent = ({ type, query }: Props) => {
|
||||
|
||||
})();
|
||||
|
||||
const isPaginatorHidden = !query.isLoading && !query.isError && query.pagination.page === 1 && !query.pagination.hasNextPage;
|
||||
|
||||
return (
|
||||
<>
|
||||
{ isMobile && !isPaginatorHidden && (
|
||||
{ isMobile && query.isPaginationVisible && (
|
||||
<ActionBar mt={ -6 }>
|
||||
<Pagination ml="auto" { ...query.pagination }/>
|
||||
</ActionBar>
|
||||
|
||||
+1
-3
@@ -2,7 +2,6 @@ import { Flex, Icon, Link, Tooltip } from '@chakra-ui/react';
|
||||
import { useRouter } from 'next/router';
|
||||
import React from 'react';
|
||||
|
||||
import { QueryKeys } from 'types/client/queries';
|
||||
import type { RoutedTab } from 'ui/shared/RoutedTabs/types';
|
||||
|
||||
import eastArrowIcon from 'icons/arrows/east.svg';
|
||||
@@ -30,8 +29,7 @@ const BlockPageContent = () => {
|
||||
const appProps = useAppContext();
|
||||
|
||||
const blockTxsQuery = useQueryWithPages({
|
||||
apiPath: `/node-api/blocks/${ router.query.id }/transactions`,
|
||||
queryName: QueryKeys.blockTxs,
|
||||
resourceName: 'block_txs',
|
||||
options: {
|
||||
enabled: Boolean(router.query.id && router.query.tab === 'txs'),
|
||||
},
|
||||
|
||||
+1
-10
@@ -2,7 +2,6 @@ import { useRouter } from 'next/router';
|
||||
import React from 'react';
|
||||
|
||||
import type { BlockType } from 'types/api/block';
|
||||
import { QueryKeys } from 'types/client/queries';
|
||||
import type { RoutedTab } from 'ui/shared/RoutedTabs/types';
|
||||
|
||||
import useIsMobile from 'lib/hooks/useIsMobile';
|
||||
@@ -19,12 +18,6 @@ const TAB_TO_TYPE: Record<string, BlockType> = {
|
||||
uncles: 'uncle',
|
||||
};
|
||||
|
||||
const TAB_TO_QUERY: Record<string, QueryKeys.blocks | QueryKeys.blocksReorgs | QueryKeys.blocksUncles> = {
|
||||
blocks: QueryKeys.blocks,
|
||||
reorgs: QueryKeys.blocksReorgs,
|
||||
uncles: QueryKeys.blocksUncles,
|
||||
};
|
||||
|
||||
const TAB_LIST_PROPS = {
|
||||
marginBottom: 0,
|
||||
py: 5,
|
||||
@@ -35,11 +28,9 @@ const BlocksPageContent = () => {
|
||||
const router = useRouter();
|
||||
const isMobile = useIsMobile();
|
||||
const type = router.query.tab && !Array.isArray(router.query.tab) ? TAB_TO_TYPE[router.query.tab] : 'block';
|
||||
const queryName = router.query.tab && !Array.isArray(router.query.tab) ? TAB_TO_QUERY[router.query.tab] : QueryKeys.blocks;
|
||||
|
||||
const blocksQuery = useQueryWithPages({
|
||||
apiPath: '/node-api/blocks',
|
||||
queryName,
|
||||
resourceName: 'blocks',
|
||||
filters: { type },
|
||||
});
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ import useTxsSort from './useTxsSort';
|
||||
|
||||
type QueryResult = UseQueryResult<TxsResponse> & {
|
||||
pagination: PaginationProps;
|
||||
isPaginationVisible: boolean;
|
||||
};
|
||||
|
||||
type Props = {
|
||||
@@ -31,7 +32,6 @@ type Props = {
|
||||
|
||||
const TxsContent = ({ filter, query, showBlockInfo = true, showSocketInfo = true, currentAddress, enableTimeIncrement }: Props) => {
|
||||
const { data, isLoading, isError, setSortByField, setSortByValue, sorting } = useTxsSort(query);
|
||||
const isPaginatorHidden = !isLoading && !isError && query.pagination.page === 1 && !query.pagination.hasNextPage;
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
const content = (() => {
|
||||
@@ -86,7 +86,7 @@ const TxsContent = ({ filter, query, showBlockInfo = true, showSocketInfo = true
|
||||
sorting={ sorting }
|
||||
showBlockInfo={ showBlockInfo }
|
||||
showSocketInfo={ showSocketInfo }
|
||||
top={ isPaginatorHidden ? 0 : 80 }
|
||||
top={ query.isPaginationVisible ? 80 : 0 }
|
||||
currentAddress={ currentAddress }
|
||||
enableTimeIncrement={ enableTimeIncrement }
|
||||
/>
|
||||
@@ -103,7 +103,7 @@ const TxsContent = ({ filter, query, showBlockInfo = true, showSocketInfo = true
|
||||
sorting={ sorting }
|
||||
setSorting={ setSortByValue }
|
||||
paginationProps={ query.pagination }
|
||||
showPagination={ !isPaginatorHidden }
|
||||
showPagination={ query.isPaginationVisible }
|
||||
filterComponent={ filter }
|
||||
/>
|
||||
) }
|
||||
|
||||
Reference in New Issue
Block a user