Skip to content

Commit c82ae2e

Browse files
author
Lasim
committed
refactor: streamline installation card layout and enhance empty state UI
1 parent d7e3d95 commit c82ae2e

File tree

1 file changed

+85
-92
lines changed

1 file changed

+85
-92
lines changed

services/frontend/src/components/mcp-server/McpInstallationsCard.vue

Lines changed: 85 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,6 @@
11
<script setup lang="ts">
22
import { computed, ref } from 'vue'
33
import { useI18n } from 'vue-i18n'
4-
import {
5-
Card,
6-
CardContent,
7-
CardDescription,
8-
CardHeader,
9-
CardTitle,
10-
} from '@/components/ui/card'
114
import { Button } from '@/components/ui/button'
125
import { Badge } from '@/components/ui/badge'
136
import {
@@ -34,7 +27,8 @@ import {
3427
Trash2,
3528
Server,
3629
Eye,
37-
AlertTriangle
30+
AlertTriangle,
31+
PackagePlus
3832
} from 'lucide-vue-next'
3933
import type { McpInstallation } from '@/types/mcp-installations'
4034
import { McpInstallationService } from '@/services/mcpInstallationService'
@@ -209,101 +203,100 @@ const handleInstallServer = () => {
209203
</script>
210204

211205
<template>
212-
<Card>
213-
<CardHeader>
214-
<CardTitle>{{ t('mcpInstallations.title') }}</CardTitle>
215-
<CardDescription>
216-
{{ t('mcpInstallations.description') }}
217-
</CardDescription>
218-
</CardHeader>
219-
<CardContent>
220-
<!-- Empty State -->
221-
<div v-if="!hasInstallations" class="text-center py-8">
222-
<Server class="h-12 w-12 mx-auto text-muted-foreground mb-4" />
223-
<h3 class="text-lg font-medium mb-2">
206+
<!-- Empty State -->
207+
<div v-if="!hasInstallations" class="pt-20">
208+
<button
209+
type="button"
210+
@click="handleInstallServer"
211+
class="relative block w-full max-w-2xl mx-auto rounded-lg border-2 border-dashed border-muted-foreground/25 p-12 text-center hover:border-muted-foreground/40 hover:bg-muted/20 focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:outline-hidden transition-all duration-200 group"
212+
>
213+
<div class="mx-auto size-16 text-muted-foreground/60 group-hover:text-muted-foreground/80 transition-colors duration-200">
214+
<PackagePlus class="w-full h-full" stroke-width="1.25" />
215+
</div>
216+
<div class="mt-4 space-y-2">
217+
<span class="block text-sm font-semibold text-foreground group-hover:text-foreground/90 transition-colors duration-200">
224218
{{ t('mcpInstallations.emptyState.title') }}
225-
</h3>
226-
<p class="text-muted-foreground mb-4">
219+
</span>
220+
<span class="block text-sm text-muted-foreground group-hover:text-muted-foreground/80 transition-colors duration-200">
227221
{{ t('mcpInstallations.emptyState.description') }}
228-
</p>
229-
<Button @click="handleInstallServer" class="flex items-center gap-2">
230-
<Plus class="h-4 w-4" />
222+
</span>
223+
<div class="mt-4 inline-flex items-center gap-1.5 text-xs text-primary font-medium group-hover:text-primary/80 transition-colors duration-200">
224+
<Plus class="h-3.5 w-3.5" />
231225
{{ t('mcpInstallations.actions.install') }}
232-
</Button>
226+
</div>
233227
</div>
234-
235-
<!-- Installations List -->
236-
<div v-else class="space-y-4">
237-
<div class="grid gap-4">
238-
<div
239-
v-for="installation in sortedInstallations"
240-
:key="installation.id"
241-
class="flex items-center justify-between space-x-4 p-4 border rounded-lg hover:bg-muted/50 transition-colors"
242-
>
243-
<!-- Left side: Server info -->
244-
<div class="flex items-center space-x-4 flex-1 min-w-0">
245-
<!-- Server Icon -->
246-
<div class="flex-shrink-0">
247-
<div class="w-10 h-10 bg-primary/10 rounded-lg flex items-center justify-center">
248-
<Server class="h-5 w-5 text-primary" />
249-
</div>
250-
</div>
251-
252-
<!-- Server Details -->
253-
<div class="flex-1 min-w-0">
254-
<div class="flex items-center gap-2 mb-1">
255-
<p class="text-sm font-medium leading-none truncate">
256-
{{ installation.installation_name }}
257-
</p>
258-
<Badge :variant="getStatusVariant(installation.installation_type)" class="text-xs">
259-
{{ getStatusText(installation.installation_type) }}
260-
</Badge>
261-
</div>
262-
<p class="text-sm text-muted-foreground truncate mb-1">
263-
{{ installation.server.description }}
264-
</p>
265-
<div class="flex items-center gap-4 text-xs text-muted-foreground">
266-
<span>{{ installation.server.language }} • {{ installation.server.runtime }}</span>
267-
<span>{{ getEnvironmentVariablesCount(installation.user_environment_variables) }} env vars</span>
268-
<span>{{ formatDate(installation.created_at) }}</span>
269-
</div>
270-
</div>
228+
</button>
229+
</div>
230+
231+
<!-- Installations List -->
232+
<div v-else class="space-y-4">
233+
<div class="grid gap-4">
234+
<div
235+
v-for="installation in sortedInstallations"
236+
:key="installation.id"
237+
class="flex items-center justify-between space-x-4 p-4 border rounded-lg hover:bg-muted/50 transition-colors"
238+
>
239+
<!-- Left side: Server info -->
240+
<div class="flex items-center space-x-4 flex-1 min-w-0">
241+
<!-- Server Icon -->
242+
<div class="flex-shrink-0">
243+
<div class="w-10 h-10 bg-primary/10 rounded-lg flex items-center justify-center">
244+
<Server class="h-5 w-5 text-primary" />
271245
</div>
246+
</div>
272247

273-
<!-- Right side: Actions -->
274-
<div class="flex-shrink-0">
275-
<DropdownMenu>
276-
<DropdownMenuTrigger as-child>
277-
<Button variant="ghost" class="h-8 w-8 p-0">
278-
<span class="sr-only">{{ t('mcpInstallations.actions.openMenu') }}</span>
279-
<MoreHorizontal class="h-4 w-4" />
280-
</Button>
281-
</DropdownMenuTrigger>
282-
<DropdownMenuContent align="end">
283-
<DropdownMenuItem @click="handleView(installation.server.id)">
284-
<Eye class="h-4 w-4 mr-2" />
285-
{{ t('mcpInstallations.actions.view') }}
286-
</DropdownMenuItem>
287-
<DropdownMenuItem @click="handleManage(installation.id)">
288-
<Settings class="h-4 w-4 mr-2" />
289-
{{ t('mcpInstallations.actions.configure') }}
290-
</DropdownMenuItem>
291-
<DropdownMenuItem
292-
@click="handleRemove(installation.id)"
293-
class="text-destructive focus:text-destructive"
294-
>
295-
<Trash2 class="h-4 w-4 mr-2" />
296-
{{ t('mcpInstallations.actions.remove') }}
297-
</DropdownMenuItem>
298-
</DropdownMenuContent>
299-
</DropdownMenu>
248+
<!-- Server Details -->
249+
<div class="flex-1 min-w-0">
250+
<div class="flex items-center gap-2 mb-1">
251+
<p class="text-sm font-medium leading-none truncate">
252+
{{ installation.installation_name }}
253+
</p>
254+
<Badge :variant="getStatusVariant(installation.installation_type)" class="text-xs">
255+
{{ getStatusText(installation.installation_type) }}
256+
</Badge>
257+
</div>
258+
<p class="text-sm text-muted-foreground truncate mb-1">
259+
{{ installation.server.description }}
260+
</p>
261+
<div class="flex items-center gap-4 text-xs text-muted-foreground">
262+
<span>{{ installation.server.language }} • {{ installation.server.runtime }}</span>
263+
<span>{{ getEnvironmentVariablesCount(installation.user_environment_variables) }} env vars</span>
264+
<span>{{ formatDate(installation.created_at) }}</span>
300265
</div>
301266
</div>
302267
</div>
303268

269+
<!-- Right side: Actions -->
270+
<div class="flex-shrink-0">
271+
<DropdownMenu>
272+
<DropdownMenuTrigger as-child>
273+
<Button variant="ghost" class="h-8 w-8 p-0">
274+
<span class="sr-only">{{ t('mcpInstallations.actions.openMenu') }}</span>
275+
<MoreHorizontal class="h-4 w-4" />
276+
</Button>
277+
</DropdownMenuTrigger>
278+
<DropdownMenuContent align="end">
279+
<DropdownMenuItem @click="handleView(installation.server.id)">
280+
<Eye class="h-4 w-4 mr-2" />
281+
{{ t('mcpInstallations.actions.view') }}
282+
</DropdownMenuItem>
283+
<DropdownMenuItem @click="handleManage(installation.id)">
284+
<Settings class="h-4 w-4 mr-2" />
285+
{{ t('mcpInstallations.actions.configure') }}
286+
</DropdownMenuItem>
287+
<DropdownMenuItem
288+
@click="handleRemove(installation.id)"
289+
class="text-destructive focus:text-destructive"
290+
>
291+
<Trash2 class="h-4 w-4 mr-2" />
292+
{{ t('mcpInstallations.actions.remove') }}
293+
</DropdownMenuItem>
294+
</DropdownMenuContent>
295+
</DropdownMenu>
296+
</div>
304297
</div>
305-
</CardContent>
306-
</Card>
298+
</div>
299+
</div>
307300

308301
<!-- Delete Confirmation Modal -->
309302
<AlertDialog v-model:open="showDeleteModal">

0 commit comments

Comments
 (0)