@@ -20,6 +20,7 @@ import { t } from "#providers/I18NProvider"
2020import { Button } from "#ui/atoms/Button"
2121import { Card } from "#ui/atoms/Card"
2222import { EmptyState } from "#ui/atoms/EmptyState"
23+ import { Pagination } from "#ui/atoms/Pagination"
2324import { Section , type SectionProps } from "#ui/atoms/Section"
2425import {
2526 SkeletonTemplate ,
@@ -30,7 +31,7 @@ import { Table, Th, Tr } from "#ui/atoms/Table"
3031import type { ThProps } from "#ui/atoms/Table/Th"
3132import { Text } from "#ui/atoms/Text"
3233import { InputFeedback } from "#ui/forms/InputFeedback"
33- import { infiniteFetcher , type Resource } from "./infiniteFetcher "
34+ import { listFetcher , type Resource } from "./listFetcher "
3435import { useMetricsSdkProvider } from "./metricsApiClient"
3536import { initialState , reducer } from "./reducer"
3637import { computeTitleWithTotalCount } from "./utils"
@@ -114,21 +115,27 @@ export interface UseResourceListConfig<TResource extends ListableResourceType> {
114115 }
115116 filter : Record < string , unknown >
116117 }
118+ /**
119+ * Pagination type: 'infinite' for infinite scrolling (default), 'pagination' for classic prev/next pagination.
120+ * Note: 'pagination' mode is only supported for Core API (not Metrics API).
121+ */
122+ paginationType ?: "infinite" | "pagination"
117123}
118124
119125/**
120- * Renders a list of resources of a given type with infinite scrolling.
126+ * Renders a list of resources of a given type with infinite scrolling or classic pagination .
121127 * It's possible to specify a query to filter the list and either
122128 * a React component (`ItemTemplate`) to be used as item template for the list or a function as `children` to render a custom element.
123129 */
124130export function useResourceList < TResource extends ListableResourceType > ( {
125131 type,
126132 query,
127133 metricsQuery,
134+ paginationType = "infinite" ,
128135} : UseResourceListConfig < TResource > ) : {
129- /** The component that renders the list with infinite scrolling functionality */
136+ /** The component that renders the list with infinite scrolling or pagination functionality */
130137 ResourceList : FC < ResourceListProps < TResource > >
131- /** The raw array of fetched resources, which grows each time a new page is fetched */
138+ /** The raw array of fetched resources, which grows each time a new page is fetched (infinite mode) or shows current page only (pagination mode) */
132139 list ?: Array < Resource < TResource > >
133140 /** Metadata related to pagination, as returned by the SDK */
134141 meta ?: ListMeta
@@ -156,27 +163,40 @@ export function useResourceList<TResource extends ListableResourceType>({
156163 reducer ,
157164 initialState ,
158165 )
166+ const [ currentPage , setCurrentPage ] = React . useState ( 1 )
167+
168+ // Validate that pagination mode is not used with metrics API
169+ if ( paginationType === "pagination" && metricsQuery != null ) {
170+ throw new Error (
171+ "Pagination mode is not supported with Metrics API. Please use infinite scrolling (default) or switch to Core API." ,
172+ )
173+ }
159174
160175 const isQueryChanged = useIsChanged ( {
161176 value : query ,
162177 onChange : ( ) => {
178+ setCurrentPage ( 1 )
163179 dispatch ( { type : "reset" } )
164- void fetchMore ( { query } )
180+ void fetchMore ( { query, pageNumber : 1 } )
165181 } ,
166182 } )
167183
168184 const fetchMore = useCallback (
169185 async ( {
170186 query,
187+ pageNumber,
171188 } : {
172189 query ?: Omit < QueryParamsList < ResourceFields [ TResource ] > , "pageNumber" >
190+ pageNumber ?: number
173191 } ) : Promise < void > => {
174192 dispatch ( { type : "prepare" } )
175193 try {
176- const listResponse = await infiniteFetcher ( {
194+ const listResponse = await listFetcher ( {
177195 // when is new query, we don't want to pass existing data
178196 currentData : isQueryChanged ? undefined : data ,
179197 resourceType : type ,
198+ mode : paginationType ,
199+ pageNumber,
180200 ...( metricsQuery != null
181201 ? {
182202 clientType : "metricsClient" ,
@@ -194,12 +214,15 @@ export function useResourceList<TResource extends ListableResourceType>({
194214 dispatch ( { type : "error" , payload : parseApiErrorMessage ( err ) } )
195215 }
196216 } ,
197- [ sdkClient , data , isQueryChanged ] ,
217+ [ sdkClient , data , isQueryChanged , paginationType , metricsQuery , type ] ,
198218 )
199219
200220 useEffect (
201221 function initialFetch ( ) {
202- void fetchMore ( { query } )
222+ void fetchMore ( {
223+ query,
224+ pageNumber : paginationType === "pagination" ? 1 : undefined ,
225+ } )
203226 } ,
204227 [ sdkClient ] ,
205228 )
@@ -221,9 +244,21 @@ export function useResourceList<TResource extends ListableResourceType>({
221244 } , [ ] )
222245
223246 const refresh = useCallback ( ( ) => {
247+ setCurrentPage ( 1 )
224248 dispatch ( { type : "reset" } )
225- void fetchMore ( { query } )
226- } , [ ] )
249+ void fetchMore ( {
250+ query,
251+ pageNumber : paginationType === "pagination" ? 1 : undefined ,
252+ } )
253+ } , [ query , paginationType ] )
254+
255+ const handlePageChange = useCallback (
256+ ( newPage : number ) => {
257+ setCurrentPage ( newPage )
258+ void fetchMore ( { query, pageNumber : newPage } )
259+ } ,
260+ [ query , fetchMore ] ,
261+ )
227262
228263 const ResourceList = useCallback < FC < ResourceListProps < TResource > > > (
229264 ( {
@@ -311,7 +346,13 @@ export function useResourceList<TResource extends ListableResourceType>({
311346 < ErrorLine
312347 message = { error . message }
313348 onRetry = { ( ) => {
314- void fetchMore ( { query } )
349+ void fetchMore ( {
350+ query,
351+ pageNumber :
352+ paginationType === "pagination"
353+ ? currentPage
354+ : undefined ,
355+ } )
315356 } }
316357 />
317358 ) : isLoading ? (
@@ -321,7 +362,18 @@ export function useResourceList<TResource extends ListableResourceType>({
321362 // biome-ignore lint/suspicious/noArrayIndexKey: Using index as key is acceptable here since items are static
322363 < ItemTemplate isLoading delayMs = { 0 } key = { idx } />
323364 ) )
324- ) : (
365+ ) : paginationType === "pagination" &&
366+ data != null &&
367+ data . meta . pageCount > 1 ? (
368+ < Spacer top = "6" >
369+ < Pagination
370+ currentPage = { currentPage }
371+ pageCount = { data . meta . pageCount }
372+ isDisabled = { isLoading }
373+ onChangePageRequest = { handlePageChange }
374+ />
375+ </ Spacer >
376+ ) : paginationType === "infinite" ? (
325377 < VisibilityTrigger
326378 enabled = { hasMorePages }
327379 callback = { ( entry ) => {
@@ -330,7 +382,7 @@ export function useResourceList<TResource extends ListableResourceType>({
330382 }
331383 } }
332384 />
333- )
385+ ) : null
334386 }
335387 >
336388 { data ?. list . map ( ( resource ) => {
@@ -358,6 +410,11 @@ export function useResourceList<TResource extends ListableResourceType>({
358410 isLoading ,
359411 isFirstLoading ,
360412 error ,
413+ paginationType ,
414+ currentPage ,
415+ handlePageChange ,
416+ query ,
417+ fetchMore ,
361418 ] ,
362419 )
363420
0 commit comments