44import { ref , computed , onMounted } from ' vue'
55import { useI18n } from ' vue-i18n'
66import { toast } from ' vue-sonner'
7- import { Settings , Eye , EyeOff , Plus , Trash2 } from ' lucide-vue-next'
7+ import { Settings , Eye , EyeOff , Plus , Trash2 , Monitor } from ' lucide-vue-next'
88import { McpCatalogService } from ' @/services/mcpCatalogService'
99import { McpInstallationService } from ' @/services/mcpInstallationService'
10+ import { useDevices } from ' @/composables/useDevices'
1011import { Button } from ' @/components/ui/button'
1112import { Badge } from ' @/components/ui/badge'
1213import { Input } from ' @/components/ui/input'
1314import { Label } from ' @/components/ui/label'
1415import { Textarea } from ' @/components/ui/textarea'
16+ import {
17+ Select ,
18+ SelectContent ,
19+ SelectItem ,
20+ SelectTrigger ,
21+ SelectValue ,
22+ } from ' @/components/ui/select'
1523import {
1624 AlertDialog ,
1725 AlertDialogContent ,
@@ -60,20 +68,21 @@ const showPassword = ref(false)
6068const isSubmitting = ref (false )
6169const formErrors = ref <Record <string , string >>({})
6270
63- // Device name for new configurations
64- const deviceName = ref (' ' )
71+ // Device management
72+ const { devices, fetchDevices, isLoading : isLoadingDevices } = useDevices ()
73+ const selectedDeviceId = ref (' ' )
6574
6675// Load server data and user configurations
6776onMounted (async () => {
6877 try {
6978 isLoadingServer .value = true
7079 isLoadingUserConfig .value = true
71-
80+
7281 // Load server schema data
7382 if (props .installation .server_id ) {
7483 serverData .value = await McpCatalogService .getServerById (props .installation .server_id )
7584 }
76-
85+
7786 // Load user configurations
7887 await loadUserConfigurations ()
7988 } catch (error ) {
@@ -92,7 +101,7 @@ const loadUserConfigurations = async () => {
92101 props .installation .id
93102 )
94103 userConfigurations .value = configs
95-
104+
96105 // Set current config (first one for now, could be device-specific later)
97106 if (configs .length > 0 && configs [0 ]) {
98107 currentUserConfig .value = configs [0 ]
@@ -171,7 +180,7 @@ const isLoading = computed(() => {
171180// Modal functions
172181const openEditModal = (item : any , type : ' arg' | ' env' ) => {
173182 if (! props .canEdit ) return
174-
183+
175184 editingItem .value = item
176185 editingType .value = type
177186 editingValue .value = item .currentValue
@@ -180,10 +189,12 @@ const openEditModal = (item: any, type: 'arg' | 'env') => {
180189 isEditModalOpen .value = true
181190}
182191
183- const openCreateModal = () => {
192+ const openCreateModal = async () => {
184193 if (! props .canEdit ) return
185-
186- deviceName .value = ' '
194+
195+ // Load devices when opening create modal
196+ await fetchDevices ()
197+ selectedDeviceId .value = ' '
187198 formErrors .value = {}
188199 isCreateModalOpen .value = true
189200}
@@ -199,7 +210,7 @@ const closeEditModal = () => {
199210
200211const closeCreateModal = () => {
201212 isCreateModalOpen .value = false
202- deviceName .value = ' '
213+ selectedDeviceId .value = ' '
203214 formErrors .value = {}
204215}
205216
@@ -222,63 +233,63 @@ const isTextarea = (item: any) => {
222233
223234const validateForm = () => {
224235 const errors: Record <string , string > = {}
225-
236+
226237 if (editingItem .value ?.required && ! editingValue .value .trim ()) {
227238 errors .value = t (' mcpInstallations.userConfiguration.editModal.validation.required' )
228239 }
229-
240+
230241 formErrors .value = errors
231242 return Object .keys (errors ).length === 0
232243}
233244
234245const validateCreateForm = () => {
235246 const errors: Record <string , string > = {}
236-
237- if (! deviceName .value . trim () ) {
238- errors .deviceName = t (' mcpInstallations.userConfiguration.createModal.validation.deviceNameRequired ' )
247+
248+ if (! selectedDeviceId .value ) {
249+ errors .deviceId = t (' mcpInstallations.userConfiguration.createModal.validation.deviceRequired ' )
239250 }
240-
251+
241252 formErrors .value = errors
242253 return Object .keys (errors ).length === 0
243254}
244255
245256const handleEdit = async () => {
246257 if (! validateForm () || ! currentUserConfig .value ) return
247-
258+
248259 isSubmitting .value = true
249-
260+
250261 try {
251262 let updatedArgs = [... currentUserArgs .value ]
252263 let updatedEnv = { ... currentUserEnv .value }
253-
264+
254265 if (editingType .value === ' arg' ) {
255266 updatedArgs [editingItem .value .index ] = editingValue .value
256267 } else {
257268 updatedEnv [editingItem .value .name ] = editingValue .value
258269 }
259-
270+
260271 const updatedConfig: UserConfiguration = await McpInstallationService .updateUserConfiguration (
261272 props .teamId ,
262273 props .installation .id ,
263274 currentUserConfig .value .id ,
264275 {
265- device_name : currentUserConfig .value .device_name ,
276+ device_id : currentUserConfig .value .device_id ,
266277 user_args: updatedArgs ,
267278 user_env: updatedEnv
268279 }
269280 )
270-
281+
271282 currentUserConfig .value = updatedConfig
272283 emit (' configuration-updated' , updatedConfig )
273-
274- const itemName = editingType .value === ' arg'
284+
285+ const itemName = editingType .value === ' arg'
275286 ? t (' mcpInstallations.userConfiguration.table.values.argumentNumber' , { number: editingItem .value .index + 1 })
276287 : editingItem .value .name
277-
288+
278289 toast .success (t (' mcpInstallations.userConfiguration.editModal.success.updated' , { item: itemName }), {
279290 description: t (' mcpInstallations.userConfiguration.editModal.success.description' )
280291 })
281-
292+
282293 closeEditModal ()
283294 } catch (error ) {
284295 console .error (' Error updating user configuration:' , error )
@@ -290,36 +301,50 @@ const handleEdit = async () => {
290301
291302const handleCreate = async () => {
292303 if (! validateCreateForm ()) return
293-
304+
294305 isSubmitting .value = true
295-
306+
296307 try {
297308 // Initialize with empty arrays/objects based on schema
298309 const initialArgs = new Array (userArgsSchema .value .length ).fill (' ' )
299310 const initialEnv: Record <string , string > = {}
300-
311+
312+ // Only include non-empty environment variables
301313 userEnvSchema .value .forEach ((envSchema : any ) => {
302- initialEnv [envSchema .name ] = ' '
314+ // Don't add empty values to avoid validation errors
315+ if (envSchema .required ) {
316+ // For required fields, we'll let the backend validation handle it
317+ initialEnv [envSchema .name ] = ' '
318+ }
319+ // For optional fields, we simply don't include them if empty
320+ })
321+
322+ // Filter out empty environment variables to avoid backend validation errors
323+ const filteredEnv: Record <string , string > = {}
324+ Object .entries (initialEnv ).forEach (([key , value ]) => {
325+ if (value && value .trim () !== ' ' ) {
326+ filteredEnv [key ] = value
327+ }
303328 })
304-
329+
305330 const newConfig: UserConfiguration = await McpInstallationService .createUserConfiguration (
306331 props .teamId ,
307332 props .installation .id ,
308333 {
309- device_name: deviceName .value . trim () ,
310- user_args: initialArgs ,
311- user_env: initialEnv
334+ device_id: selectedDeviceId .value ,
335+ user_args: initialArgs . filter ( arg => arg . trim () !== ' ' ) ,
336+ user_env: filteredEnv
312337 }
313338 )
314-
339+
315340 currentUserConfig .value = newConfig
316341 userConfigurations .value .push (newConfig )
317342 emit (' configuration-updated' , newConfig )
318-
343+
319344 toast .success (t (' mcpInstallations.userConfiguration.createModal.success.created' ), {
320345 description: t (' mcpInstallations.userConfiguration.createModal.success.description' )
321346 })
322-
347+
323348 closeCreateModal ()
324349 } catch (error ) {
325350 console .error (' Error creating user configuration:' , error )
@@ -331,7 +356,7 @@ const handleCreate = async () => {
331356
332357const modalTitle = computed (() => {
333358 if (! editingItem .value ) return ' '
334-
359+
335360 if (editingType .value === ' arg' ) {
336361 return t (' mcpInstallations.userConfiguration.editModal.titleArg' , { number: editingItem .value .index + 1 })
337362 } else {
@@ -649,21 +674,35 @@ const modalTitle = computed(() => {
649674 {{ formErrors.general }}
650675 </div >
651676
652- <!-- Device Name Input -->
677+ <!-- Device Selection -->
653678 <div class =" space-y-2" >
654- <Label for =" device-name" >{{ t('mcpInstallations.userConfiguration.createModal.form.labels.deviceName') }}</Label >
655- <Input
656- id =" device-name"
657- v-model =" deviceName"
658- :placeholder =" t('mcpInstallations.userConfiguration.createModal.form.placeholders.deviceName')"
659- :class =" { 'border-destructive': formErrors.deviceName }"
660- required
661- />
662- <div v-if =" formErrors.deviceName" class =" text-sm text-destructive" >
663- {{ formErrors.deviceName }}
679+ <Label for =" device-select" >{{ t('mcpInstallations.userConfiguration.createModal.form.labels.device') }}</Label >
680+ <Select v-model =" selectedDeviceId" >
681+ <SelectTrigger
682+ id =" device-select"
683+ :class =" { 'border-destructive': formErrors.deviceId }"
684+ >
685+ <SelectValue :placeholder =" isLoadingDevices ? t('mcpInstallations.userConfiguration.createModal.form.placeholders.loadingDevices') : t('mcpInstallations.userConfiguration.createModal.form.placeholders.selectDevice')" />
686+ </SelectTrigger >
687+ <SelectContent >
688+ <SelectItem
689+ v-for =" device in devices"
690+ :key =" device.id"
691+ :value =" device.id"
692+ >
693+ <div class =" flex items-center gap-2" >
694+ <Monitor class =" h-4 w-4 text-muted-foreground" />
695+ <span >{{ device.device_name }}</span >
696+ <span class =" text-muted-foreground text-sm" >({{ device.os_type }})</span >
697+ </div >
698+ </SelectItem >
699+ </SelectContent >
700+ </Select >
701+ <div v-if =" formErrors.deviceId" class =" text-sm text-destructive" >
702+ {{ formErrors.deviceId }}
664703 </div >
665704 <p class =" text-xs text-gray-500" >
666- {{ t('mcpInstallations.userConfiguration.createModal.form.help.deviceName ') }}
705+ {{ t('mcpInstallations.userConfiguration.createModal.form.help.device ') }}
667706 </p >
668707 </div >
669708
0 commit comments