@@ -7,6 +7,7 @@ import { Button } from '@/components/ui/button'
77import { Alert , AlertDescription } from ' @/components/ui/alert'
88import McpServerSquareCard from ' @/components/mcp-server/McpServerSquareCard.vue'
99import FeaturedMcpServers from ' @/components/mcp-server/FeaturedMcpServers.vue'
10+ import PaginationControls from ' @/components/ui/pagination/PaginationControls.vue'
1011import { McpCatalogService } from ' @/services/mcpCatalogService'
1112import { McpCategoriesService } from ' @/services/mcpCategoriesService'
1213import type { McpServerSearchParams , McpServerSearchResponse } from ' @/types/mcp-catalog'
@@ -36,26 +37,23 @@ const selectedServerId = ref<string | null>(null)
3637const selectedCategory = ref (' all' )
3738const searchResults = ref <McpServerSearchResponse | null >(null )
3839
39- // Constants
40- const MAX_RESULTS_TO_SHOW = 50
40+ // Pagination state
41+ const currentPage = ref (1 )
42+ const pageSize = ref (15 )
43+ const totalItems = ref (0 )
44+ const pageSizeOptions = ref ([15 , 30 , 45 , 60 ])
4145
4246// Computed
4347const filteredServers = computed (() => {
4448 return searchResults .value ?.servers || []
4549})
4650
47- const showTooManyResultsWarning = computed (() => {
48- const pagination = searchResults .value ?.pagination
49- return pagination && pagination .total > MAX_RESULTS_TO_SHOW
51+ const hasSearched = computed (() => {
52+ return searchQuery .value .trim ().length > 0
5053})
5154
5255const shouldShowResults = computed (() => {
53- const pagination = searchResults .value ?.pagination
54- return pagination && pagination .total <= MAX_RESULTS_TO_SHOW && filteredServers .value .length > 0
55- })
56-
57- const hasSearched = computed (() => {
58- return searchQuery .value .trim ().length > 0
56+ return hasSearched .value && filteredServers .value .length > 0
5957})
6058
6159// Methods
@@ -101,10 +99,12 @@ const performSearch = async () => {
10199 error .value = null
102100 searchQuery .value = searchTerm .value
103101
102+ const offset = (currentPage .value - 1 ) * pageSize .value
103+
104104 const searchParams: McpServerSearchParams = {
105105 q: searchTerm .value .trim (),
106- limit: MAX_RESULTS_TO_SHOW + 1 , // Get one extra to detect if there are more
107- offset: 0
106+ limit: pageSize . value ,
107+ offset: offset
108108 }
109109
110110 // Add category filter if selected
@@ -113,21 +113,13 @@ const performSearch = async () => {
113113 }
114114
115115 const response = await McpCatalogService .searchServers (searchParams )
116-
117- // Check if we got too many results
118- if (response .pagination .total > MAX_RESULTS_TO_SHOW ) {
119- searchResults .value = {
120- servers: [],
121- pagination: response .pagination ,
122- filters: response .filters
123- }
124- } else {
125- searchResults .value = response
126- }
116+ searchResults .value = response
117+ totalItems .value = response .pagination .total
127118
128119 } catch (err ) {
129120 error .value = err instanceof Error ? err .message : ' Failed to search servers'
130121 searchResults .value = null
122+ totalItems .value = 0
131123 } finally {
132124 isLoading .value = false
133125 }
@@ -138,6 +130,20 @@ const clearSearch = () => {
138130 searchQuery .value = ' '
139131 searchResults .value = null
140132 error .value = null
133+ currentPage .value = 1
134+ totalItems .value = 0
135+ }
136+
137+ // Pagination event handlers
138+ const handlePageChange = async (page : number ) => {
139+ currentPage .value = page
140+ await performSearch ()
141+ }
142+
143+ const handlePageSizeChange = async (newPageSize : number ) => {
144+ pageSize .value = newPageSize
145+ currentPage .value = 1
146+ await performSearch ()
141147}
142148
143149onMounted (() => {
@@ -147,9 +153,10 @@ onMounted(() => {
147153 </script >
148154
149155<template >
150- <div class =" pt-10 " >
156+ <div : class =" shouldShowResults ? ' pt-2' : 'pt-10' " >
151157 <div class =" mx-auto max-w-2xl" >
152- <div class =" text-center" >
158+ <!-- Header - hide when we have search results -->
159+ <div v-if =" !shouldShowResults" class =" text-center" >
153160 <div class =" mx-auto size-16 text-gray-400" >
154161 <PackagePlus class =" w-full h-full" stroke-width =" 1.25" aria-hidden =" true" />
155162 </div >
@@ -253,31 +260,8 @@ onMounted(() => {
253260 <span class =" text-gray-600" >{{ t('messages.loading') }}</span >
254261 </div >
255262
256- <!-- Too Many Results Warning -->
257- <div v-else-if =" showTooManyResultsWarning" class =" mt-14" >
258- <Alert class =" border-orange-200 bg-orange-50 text-orange-800" >
259- <AlertTriangle class =" h-4 w-4" />
260- <AlertDescription >
261- {{ t('mcpInstallations.wizard.server.tooManyResults', { total: searchResults?.pagination.total }) }}
262- </AlertDescription >
263- </Alert >
264- <div class =" mt-4 text-center" >
265- <Button variant =" outline" @click =" clearSearch" >
266- {{ t('actions.clearSearch') }}
267- </Button >
268- </div >
269- </div >
270-
271- <!-- Server Grid (only show when there's a search query and results fit criteria) -->
263+ <!-- Server Grid (only show when there's a search query and results) -->
272264 <div v-else-if =" shouldShowResults" class =" mt-14 space-y-6" >
273- <!-- Results info -->
274- <div v-if =" searchResults?.pagination" class =" text-sm text-gray-600 text-center" >
275- {{ t('mcpInstallations.wizard.server.maxResultsReached', {
276- shown: filteredServers.length,
277- total: searchResults.pagination.total
278- }) }}
279- </div >
280-
281265 <!-- 3-tier grid layout -->
282266 <div class =" grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8" >
283267 <McpServerSquareCard
@@ -288,10 +272,22 @@ onMounted(() => {
288272 @click =" handleDetailsClick"
289273 />
290274 </div >
275+
276+ <!-- Pagination Controls -->
277+ <PaginationControls
278+ v-if =" totalItems > 0"
279+ :current-page =" currentPage"
280+ :page-size =" pageSize"
281+ :total-items =" totalItems"
282+ :is-loading =" isLoading"
283+ :page-size-options =" pageSizeOptions"
284+ @page-change =" handlePageChange"
285+ @page-size-change =" handlePageSizeChange"
286+ />
291287 </div >
292288
293289 <!-- No Results -->
294- <div v-else-if =" hasSearched && filteredServers.length === 0 && !showTooManyResultsWarning && ! isLoading" class =" mt-14 text-center py-8" >
290+ <div v-else-if =" hasSearched && filteredServers.length === 0 && !isLoading" class =" mt-14 text-center py-8" >
295291 <p class =" text-gray-500" >{{ t('mcpInstallations.wizard.server.noServersFound') }}</p >
296292 <div class =" mt-4" >
297293 <Button variant =" outline" @click =" clearSearch" >
0 commit comments