Skip to content

Commit 00f4897

Browse files
author
Lasim
committed
feat(frontend): add featured MCP servers page and related components
1 parent 92a86a1 commit 00f4897

File tree

14 files changed

+453
-1
lines changed

14 files changed

+453
-1
lines changed

services/frontend/src/components/mcp-server/wizard/McpServerSelectionStep.vue

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,26 @@ onMounted(() => {
213213
</button>
214214
</div>
215215
</form>
216+
217+
<!-- Browse Buttons -->
218+
<div class="mt-4 flex items-center justify-center gap-3">
219+
<Button
220+
variant="outline"
221+
size="sm"
222+
class="bg-black text-white border-black hover:bg-black/90 hover:border-black hover:text-white"
223+
@click="router.push('/mcp-server/featured')"
224+
>
225+
{{ t('mcpInstallations.wizard.server.browseFeatured') }}
226+
</Button>
227+
<Button
228+
variant="outline"
229+
size="sm"
230+
class="bg-black text-white border-black hover:bg-black/90 hover:border-black hover:text-white"
231+
@click="router.push('/mcp-server/catalog')"
232+
>
233+
{{ t('mcpInstallations.wizard.server.viewAllServers') }}
234+
</Button>
235+
</div>
216236
</div>
217237

218238
<div v-if="isLoadingCategories" class="mt-4 text-center text-sm text-gray-500">
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<script setup lang="ts">
2+
import type { HTMLAttributes } from "vue"
3+
import { cn } from "@/lib/utils"
4+
5+
const props = defineProps<{
6+
class?: HTMLAttributes["class"]
7+
}>()
8+
</script>
9+
10+
<template>
11+
<div
12+
data-slot="empty"
13+
:class="cn(
14+
'flex min-w-0 flex-1 flex-col items-center justify-center gap-6 text-balance rounded-lg border-dashed p-6 text-center md:p-12',
15+
props.class,
16+
)"
17+
>
18+
<slot />
19+
</div>
20+
</template>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<script setup lang="ts">
2+
import type { HTMLAttributes } from "vue"
3+
import { cn } from "@/lib/utils"
4+
5+
const props = defineProps<{
6+
class?: HTMLAttributes["class"]
7+
}>()
8+
</script>
9+
10+
<template>
11+
<div
12+
data-slot="empty-content"
13+
:class="cn(
14+
'flex w-full min-w-0 max-w-sm flex-col items-center gap-4 text-balance text-sm',
15+
props.class,
16+
)"
17+
>
18+
<slot />
19+
</div>
20+
</template>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<script setup lang="ts">
2+
import type { HTMLAttributes } from "vue"
3+
import { cn } from "@/lib/utils"
4+
5+
const props = defineProps<{
6+
class?: HTMLAttributes["class"]
7+
}>()
8+
</script>
9+
10+
<template>
11+
<p
12+
data-slot="empty-description"
13+
:class="cn(
14+
'text-muted-foreground [&>a:hover]:text-primary text-sm/relaxed [&>a]:underline [&>a]:underline-offset-4',
15+
props.class,
16+
)"
17+
>
18+
<slot />
19+
</p>
20+
</template>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<script setup lang="ts">
2+
import type { HTMLAttributes } from "vue"
3+
import { cn } from "@/lib/utils"
4+
5+
const props = defineProps<{
6+
class?: HTMLAttributes["class"]
7+
}>()
8+
</script>
9+
10+
<template>
11+
<div
12+
data-slot="empty-header"
13+
:class="cn(
14+
'flex max-w-sm flex-col items-center gap-2 text-center',
15+
props.class,
16+
)"
17+
>
18+
<slot />
19+
</div>
20+
</template>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<script setup lang="ts">
2+
import type { HTMLAttributes } from "vue"
3+
import type { EmptyMediaVariants } from "."
4+
import { cn } from "@/lib/utils"
5+
import { emptyMediaVariants } from "."
6+
7+
const props = defineProps<{
8+
class?: HTMLAttributes["class"]
9+
variant?: EmptyMediaVariants["variant"]
10+
}>()
11+
</script>
12+
13+
<template>
14+
<div
15+
data-slot="empty-icon"
16+
:data-variant="variant"
17+
:class="cn(emptyMediaVariants({ variant }), props.class)"
18+
>
19+
<slot />
20+
</div>
21+
</template>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<script setup lang="ts">
2+
import type { HTMLAttributes } from "vue"
3+
import { cn } from "@/lib/utils"
4+
5+
const props = defineProps<{
6+
class?: HTMLAttributes["class"]
7+
}>()
8+
</script>
9+
10+
<template>
11+
<div
12+
data-slot="empty-title"
13+
:class="cn('text-lg font-medium tracking-tight', props.class)"
14+
>
15+
<slot />
16+
</div>
17+
</template>
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import type { VariantProps } from "class-variance-authority"
2+
import { cva } from "class-variance-authority"
3+
4+
export { default as Empty } from "./Empty.vue"
5+
export { default as EmptyContent } from "./EmptyContent.vue"
6+
export { default as EmptyDescription } from "./EmptyDescription.vue"
7+
export { default as EmptyHeader } from "./EmptyHeader.vue"
8+
export { default as EmptyMedia } from "./EmptyMedia.vue"
9+
export { default as EmptyTitle } from "./EmptyTitle.vue"
10+
11+
export const emptyMediaVariants = cva(
12+
"mb-2 flex shrink-0 items-center justify-center [&_svg]:pointer-events-none [&_svg]:shrink-0",
13+
{
14+
variants: {
15+
variant: {
16+
default: "bg-transparent",
17+
icon: "bg-muted text-foreground flex size-10 shrink-0 items-center justify-center rounded-lg [&_svg:not([class*='size-'])]:size-6",
18+
},
19+
},
20+
defaultVariants: {
21+
variant: "default",
22+
},
23+
},
24+
)
25+
26+
export type EmptyMediaVariants = VariantProps<typeof emptyMediaVariants>

services/frontend/src/i18n/locales/en/mcp-installations.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,25 @@ export default {
1313
},
1414

1515
featured: {
16-
title: 'Featured MCP Servers',
16+
title: 'Featured Catalog',
1717
description: 'Discover our curated selection of high-quality MCP servers',
1818
loading: 'Loading featured servers...',
1919
noServers: 'No featured servers available at the moment',
20+
categories: 'Categories',
21+
noCategories: 'No categories found',
22+
emptyState: {
23+
title: 'No Featured Servers',
24+
description: 'Featured MCP servers need to be configured by an administrator.',
25+
},
26+
},
27+
28+
catalog: {
29+
title: 'MCP Server Catalog',
30+
description: 'Browse all available MCP servers',
31+
emptyState: {
32+
title: 'MCP Server Catalog',
33+
description: 'Browse all available MCP servers. Coming soon.',
34+
},
2035
},
2136

2237
status: {
@@ -115,6 +130,8 @@ export default {
115130
loadingCategories: 'Loading categories...',
116131
categoriesError: 'Failed to load categories',
117132
errorTitle: 'Error loading servers',
133+
browseFeatured: 'Browse Featured',
134+
viewAllServers: 'View All Servers',
118135
requiredEnvVars: 'Required Environment Variables',
119136
details: 'Details',
120137
install: 'Install',

services/frontend/src/router/index.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,18 @@ const routes = [
117117
component: () => import('../views/mcp-server/installation/[id].vue'),
118118
meta: { requiresSetup: true },
119119
},
120+
{
121+
path: '/mcp-server/featured',
122+
name: 'McpServerFeatured',
123+
component: () => import('../views/mcp-server/featured.vue'),
124+
meta: { requiresSetup: true },
125+
},
126+
{
127+
path: '/mcp-server/catalog',
128+
name: 'McpServerCatalog',
129+
component: () => import('../views/mcp-server/catalog.vue'),
130+
meta: { requiresSetup: true },
131+
},
120132
{
121133
path: '/client-configuration',
122134
name: 'ClientConfiguration',

0 commit comments

Comments
 (0)