Skip to content

Commit 6635348

Browse files
author
Lasim
committed
feat(frontend): enhance MCP server installation views and add stats component
1 parent dc55c62 commit 6635348

File tree

4 files changed

+121
-30
lines changed

4 files changed

+121
-30
lines changed

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

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -76,12 +76,14 @@ const handleRemoveInstallation = (installationId: string) => {
7676
</div>
7777

7878
<!-- Installations List -->
79-
<McpInstallationsList
80-
v-else
81-
:installations="installations"
82-
:show-walkthrough="showWalkthrough"
83-
@view-installation="handleViewInstallation"
84-
@manage-installation="handleManageInstallation"
85-
@remove-installation="handleRemoveInstallation"
86-
/>
79+
<div v-else class="mt-18">
80+
<McpInstallationsList
81+
:installations="installations"
82+
:show-walkthrough="showWalkthrough"
83+
@view-installation="handleViewInstallation"
84+
@manage-installation="handleManageInstallation"
85+
@remove-installation="handleRemoveInstallation"
86+
/>
87+
</div>
88+
8789
</template>

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,8 +209,8 @@ onUnmounted(() => {
209209
class="fixed inset-0 bg-black/40 backdrop-blur-[2px] z-[9998]"
210210
/>
211211

212-
<div class="min-h-screen bg-gray-50">
213-
<div class="mx-auto max-w-4xl space-y-8 py-16">
212+
<div class="min-h-screen">
213+
<div>
214214
<!-- Installations List -->
215215
<div v-if="sortedInstallations.length > 0" class="relative">
216216
<ul
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<script setup lang="ts">
2+
import { ref, computed, onMounted } from 'vue'
3+
import { LineChart } from '@/components/ui/chart'
4+
5+
const isLoading = ref(true)
6+
const requestData = ref<number[]>([])
7+
const timeLabels = ref<string[]>([])
8+
9+
const totalRequests = computed(() =>
10+
requestData.value.reduce((sum, val) => sum + val, 0)
11+
)
12+
13+
const averageRequests = computed(() =>
14+
requestData.value.length > 0
15+
? Math.round(totalRequests.value / requestData.value.length)
16+
: 0
17+
)
18+
19+
const peakRequests = computed(() =>
20+
requestData.value.length > 0
21+
? Math.max(...requestData.value)
22+
: 0
23+
)
24+
25+
const generateDummyData = () => {
26+
const now = new Date()
27+
const data: number[] = []
28+
const labels: string[] = []
29+
30+
for (let i = 11; i >= 0; i--) {
31+
const time = new Date(now.getTime() - i * 5 * 60 * 1000)
32+
const hour = time.getHours().toString().padStart(2, '0')
33+
const minute = time.getMinutes().toString().padStart(2, '0')
34+
labels.push(`${hour}:${minute}`)
35+
36+
const baseValue = 20 + Math.random() * 30
37+
const variation = Math.sin(i / 2) * 15
38+
data.push(Math.max(0, Math.round(baseValue + variation)))
39+
}
40+
41+
return { data, labels }
42+
}
43+
44+
onMounted(() => {
45+
setTimeout(() => {
46+
const { data, labels } = generateDummyData()
47+
requestData.value = data
48+
timeLabels.value = labels
49+
isLoading.value = false
50+
}, 500)
51+
})
52+
</script>
53+
54+
<template>
55+
<div class="space-y-4">
56+
<div>
57+
<h3 class="text-lg font-semibold">MCP Server Usage</h3>
58+
</div>
59+
60+
<div class="grid grid-cols-1 gap-4 lg:grid-cols-[75%_1fr]">
61+
<LineChart
62+
:data="requestData"
63+
:labels="timeLabels"
64+
name="MCP Requests"
65+
size="sm"
66+
:loading="isLoading"
67+
color="#0f766e"
68+
area-color="rgba(15, 118, 110, 0.3)"
69+
/>
70+
71+
<div class="flex flex-col justify-between h-[200px]">
72+
<div class="space-y-1">
73+
<p class="text-sm text-muted-foreground">Total Requests</p>
74+
<p class="text-xl font-semibold">{{ totalRequests }}</p>
75+
</div>
76+
<div class="space-y-1">
77+
<p class="text-sm text-muted-foreground">Average per Interval</p>
78+
<p class="text-xl font-semibold">{{ averageRequests }}</p>
79+
</div>
80+
<div class="space-y-1">
81+
<p class="text-sm text-muted-foreground">Peak Requests</p>
82+
<p class="text-xl font-semibold">{{ peakRequests }}</p>
83+
</div>
84+
</div>
85+
</div>
86+
</div>
87+
</template>

services/frontend/src/views/mcp-server/index.vue

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { Plus } from 'lucide-vue-next'
88
import { toast } from 'vue-sonner'
99
import { useEventBus } from '@/composables/useEventBus'
1010
import McpInstallationsCard from '@/components/mcp-server/McpInstallationsCard.vue'
11+
import McpStats from '@/components/mcp-server/McpStats.vue'
1112
import ClientConfigurationModal from '@/components/gateway-config/ClientConfigurationModal.vue'
1213
import UserWalkthroughPopover from '@/components/walkthrough/UserWalkthroughPopover.vue'
1314
import type { McpInstallation } from '@/types/mcp-installations'
@@ -99,24 +100,24 @@ const checkWalkthroughSetting = async (): Promise<boolean> => {
99100
try {
100101
// Step 1: Check if walkthrough is globally enabled
101102
const globalWalkthroughEnabled = await GlobalSettingsService.shouldShowUserWalkthrough()
102-
103+
103104
if (!globalWalkthroughEnabled) {
104105
return false
105106
}
106107
107108
// Step 2: Check user's personal walkthrough completion status via API
108109
const userPreferences = await UserPreferencesService.getUserPreferences()
109110
const isWalkthroughCompleted = userPreferences.walkthrough_completed || false
110-
111+
111112
if (isWalkthroughCompleted) {
112113
// Also sync to local storage for consistency
113114
eventBus.setState('walkthrough_completed', true)
114115
return false
115116
}
116-
117+
117118
// Step 3: Return true if walkthrough should be shown
118119
return true
119-
120+
120121
} catch (error) {
121122
console.error('Error checking walkthrough setting:', error)
122123
return false
@@ -132,10 +133,10 @@ const fetchInstallations = async (): Promise<void> => {
132133
error.value = null
133134
134135
installations.value = await McpInstallationService.getTeamInstallations(selectedTeam.value.id)
135-
136+
136137
// UPDATED: Check and show walkthrough after installations are loaded
137138
await checkAndShowWalkthrough()
138-
139+
139140
} catch (err) {
140141
error.value = err instanceof Error ? err.message : 'An unknown error occurred'
141142
installations.value = []
@@ -155,15 +156,15 @@ const checkAndShowWalkthrough = async (): Promise<void> => {
155156
156157
// Check if walkthrough should be shown
157158
const shouldShowWalkthrough = await checkWalkthroughSetting()
158-
159+
159160
if (!shouldShowWalkthrough) {
160161
showUserWalkthrough.value = false
161162
return
162163
}
163164
164165
// Wait for DOM to update with the installations list
165166
await nextTick()
166-
167+
167168
// Add a small delay to ensure the list is fully rendered
168169
setTimeout(() => {
169170
// Verify the target element exists before showing walkthrough
@@ -177,7 +178,7 @@ const checkAndShowWalkthrough = async (): Promise<void> => {
177178
showUserWalkthrough.value = false
178179
}
179180
}, 100)
180-
181+
181182
} catch (error) {
182183
console.error('Error checking and showing walkthrough:', error)
183184
showUserWalkthrough.value = false
@@ -274,37 +275,37 @@ const handleWalkthroughFinish = async () => {
274275
try {
275276
// Step 1: Use generic preference endpoint (avoids Content-Type header issue)
276277
await UserPreferencesService.setUserPreference('walkthrough_completed', true)
277-
278+
278279
// Step 2: Update local storage for consistency
279280
eventBus.setState('walkthrough_completed', true)
280-
281+
281282
// Step 3: Hide all walkthrough UI elements
282283
showUserWalkthrough.value = false
283284
showWalkthroughStep2.value = false
284285
showStep2ButtonHighZIndex.value = false
285286
walkthroughStep.value = 1
286-
287+
287288
// Step 4: Emit completion event for any listening components
288289
eventBus.emit('walkthrough-completed')
289-
290+
290291
// Optional: Show success toast
291292
toast.success('Welcome tour completed!')
292-
293+
293294
} catch (error) {
294295
console.error('Error updating walkthrough completion status:', error)
295-
296+
296297
// Still update local storage as fallback
297298
eventBus.setState('walkthrough_completed', true)
298-
299+
299300
// Hide walkthrough UI
300301
showUserWalkthrough.value = false
301302
showWalkthroughStep2.value = false
302303
showStep2ButtonHighZIndex.value = false
303304
walkthroughStep.value = 1
304-
305+
305306
// Emit completion event
306307
eventBus.emit('walkthrough-completed')
307-
308+
308309
// Show error toast
309310
toast.error('Walkthrough completed, but failed to save preference')
310311
}
@@ -408,7 +409,6 @@ onUnmounted(() => {
408409
<!-- Header -->
409410
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
410411
<div class="flex-1">
411-
<p class="text-muted-foreground">{{ t('mcpInstallations.description') }}</p>
412412
</div>
413413
<div v-if="selectedTeam" class="flex flex-col gap-2 sm:flex-row sm:items-center sm:gap-3">
414414
<Button
@@ -450,7 +450,9 @@ onUnmounted(() => {
450450
</div>
451451

452452
<!-- Main Content -->
453-
<div v-else>
453+
<div v-else class="space-y-6">
454+
<McpStats />
455+
454456
<McpInstallationsCard
455457
:installations="installations"
456458
:has-installations="hasInstallations"

0 commit comments

Comments
 (0)