Skip to content

Commit 8bdccc7

Browse files
author
Lasim
committed
feat(frontend): implement sync form with configurable options
1 parent e530a17 commit 8bdccc7

File tree

6 files changed

+148
-27
lines changed

6 files changed

+148
-27
lines changed

package-lock.json

Lines changed: 24 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

services/frontend/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
"marked": "^16.3.0",
2525
"mitt": "^3.0.1",
2626
"pinia": "^3.0.3",
27-
"reka-ui": "^2.4.1",
27+
"reka-ui": "^2.5.1",
2828
"tailwind-merge": "^3.3.1",
2929
"tailwindcss-animate": "^1.0.7",
3030
"vee-validate": "^4.15.1",
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<script setup lang="ts">
2+
import type { CheckboxRootEmits, CheckboxRootProps } from "reka-ui"
3+
import type { HTMLAttributes } from "vue"
4+
import { reactiveOmit } from "@vueuse/core"
5+
import { Check } from "lucide-vue-next"
6+
import { CheckboxIndicator, CheckboxRoot, useForwardPropsEmits } from "reka-ui"
7+
import { cn } from "@/lib/utils"
8+
9+
const props = defineProps<CheckboxRootProps & { class?: HTMLAttributes["class"] }>()
10+
const emits = defineEmits<CheckboxRootEmits>()
11+
12+
const delegatedProps = reactiveOmit(props, "class")
13+
14+
const forwarded = useForwardPropsEmits(delegatedProps, emits)
15+
</script>
16+
17+
<template>
18+
<CheckboxRoot
19+
data-slot="checkbox"
20+
v-bind="forwarded"
21+
:class="
22+
cn('peer border-input data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50',
23+
props.class)"
24+
>
25+
<CheckboxIndicator
26+
data-slot="checkbox-indicator"
27+
class="flex items-center justify-center text-current transition-none"
28+
>
29+
<slot>
30+
<Check class="size-3.5" />
31+
</slot>
32+
</CheckboxIndicator>
33+
</CheckboxRoot>
34+
</template>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default as Checkbox } from "./Checkbox.vue"

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

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -777,12 +777,23 @@ export default {
777777
registryUrl: 'https://registry.modelcontextprotocol.io',
778778
explanation: 'The sync process will:',
779779
steps: [
780-
'Fetch up to 25 servers for testing',
781-
'Process each server with rate limiting (2 seconds between servers)',
782-
'Skip servers that already exist in your catalog',
783780
'Run in the background without blocking other operations'
784781
],
785-
note: 'Note: This is a test sync limited to 25 servers. You can check the progress in the backend logs.',
782+
form: {
783+
maxServers: {
784+
label: 'Maximum Servers',
785+
placeholder: '25',
786+
description: 'Maximum number of servers to fetch from the registry'
787+
},
788+
skipExisting: {
789+
label: 'Skip Existing Servers'
790+
},
791+
rateLimitDelay: {
792+
label: 'Rate Limit Delay (seconds)',
793+
placeholder: '2',
794+
description: 'Delay in seconds between processing each server'
795+
}
796+
},
786797
cancel: 'Cancel',
787798
confirm: 'Start Sync',
788799
syncing: 'Syncing...'

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

Lines changed: 73 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
<script setup lang="ts">
2-
import { ref, onMounted, onUnmounted, computed } from 'vue'
2+
import { ref, onMounted, onUnmounted, computed, watch } from 'vue'
33
import { useI18n } from 'vue-i18n'
44
import { useRouter, useRoute } from 'vue-router'
55
import { toast } from 'vue-sonner'
66
import { Input } from '@/components/ui/input'
7+
import { Label } from '@/components/ui/label'
8+
import { Checkbox } from '@/components/ui/checkbox'
79
import { Button } from '@/components/ui/button'
810
import {
911
AlertDialog,
@@ -39,6 +41,24 @@ const searchQuery = ref('')
3941
const isSyncModalOpen = ref(false)
4042
const isSyncing = ref(false)
4143
44+
// Sync form data
45+
const syncFormData = ref({
46+
maxServers: 25,
47+
skipExisting: true,
48+
rateLimitDelay: 2
49+
})
50+
51+
// Reset form data when modal opens
52+
watch(isSyncModalOpen, (isOpen) => {
53+
if (isOpen) {
54+
syncFormData.value = {
55+
maxServers: 25,
56+
skipExisting: true,
57+
rateLimitDelay: 2
58+
}
59+
}
60+
})
61+
4262
// Pagination state
4363
const currentPage = ref(1)
4464
const pageSize = ref(20)
@@ -98,15 +118,15 @@ const handleSyncRegistry = async () => {
98118
99119
const baseUrl = getEnv('VITE_DEPLOYSTACK_BACKEND_URL')
100120
101-
// Call backend sync endpoint with test limit
121+
// Call backend sync endpoint with form data
102122
const response = await fetch(`${baseUrl}/api/admin/mcp-registry/sync`, {
103123
method: 'POST',
104124
headers: { 'Content-Type': 'application/json' },
105125
credentials: 'include',
106126
body: JSON.stringify({
107-
maxServers: 3,
108-
skipExisting: true,
109-
rateLimitDelay: 2
127+
maxServers: syncFormData.value.maxServers,
128+
skipExisting: syncFormData.value.skipExisting,
129+
rateLimitDelay: syncFormData.value.rateLimitDelay
110130
})
111131
})
112132
@@ -323,6 +343,54 @@ onUnmounted(() => {
323343
</a>
324344
</div>
325345

346+
<!-- Form Fields -->
347+
<div class="space-y-4">
348+
<!-- Max Servers -->
349+
<div class="space-y-2">
350+
<Label for="maxServers">{{ t('mcpCatalog.registrySync.modal.form.maxServers.label') }}</Label>
351+
<Input
352+
id="maxServers"
353+
v-model.number="syncFormData.maxServers"
354+
type="number"
355+
min="1"
356+
:placeholder="t('mcpCatalog.registrySync.modal.form.maxServers.placeholder')"
357+
/>
358+
<p class="text-sm text-muted-foreground">
359+
{{ t('mcpCatalog.registrySync.modal.form.maxServers.description') }}
360+
</p>
361+
</div>
362+
363+
<!-- Rate Limit Delay -->
364+
<div class="space-y-2">
365+
<Label for="rateLimitDelay">{{ t('mcpCatalog.registrySync.modal.form.rateLimitDelay.label') }}</Label>
366+
<Input
367+
id="rateLimitDelay"
368+
v-model.number="syncFormData.rateLimitDelay"
369+
type="number"
370+
min="0"
371+
step="0.5"
372+
:placeholder="t('mcpCatalog.registrySync.modal.form.rateLimitDelay.placeholder')"
373+
/>
374+
<p class="text-sm text-muted-foreground">
375+
{{ t('mcpCatalog.registrySync.modal.form.rateLimitDelay.description') }}
376+
</p>
377+
</div>
378+
379+
<!-- Skip Existing Checkbox -->
380+
<div class="flex items-start space-x-3">
381+
<Checkbox
382+
id="skipExisting"
383+
v-model="syncFormData.skipExisting"
384+
/>
385+
<Label
386+
for="skipExisting"
387+
class="cursor-pointer font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
388+
>
389+
{{ t('mcpCatalog.registrySync.modal.form.skipExisting.label') }}
390+
</Label>
391+
</div>
392+
</div>
393+
326394
<!-- Explanation -->
327395
<div class="space-y-2">
328396
<p class="text-sm font-medium">{{ t('mcpCatalog.registrySync.modal.explanation') }}</p>
@@ -332,13 +400,6 @@ onUnmounted(() => {
332400
</li>
333401
</ul>
334402
</div>
335-
336-
<!-- Note -->
337-
<div class="rounded-md bg-muted p-3">
338-
<p class="text-sm text-muted-foreground">
339-
{{ t('mcpCatalog.registrySync.modal.note') }}
340-
</p>
341-
</div>
342403
</div>
343404

344405
<AlertDialogFooter>

0 commit comments

Comments
 (0)