Skip to content

Commit 4d2e332

Browse files
author
Lasim
committed
feat(frontend): Implement device management features including listing, viewing, editing, and removing devices
1 parent ae4cdd0 commit 4d2e332

File tree

19 files changed

+1250
-103
lines changed

19 files changed

+1250
-103
lines changed

services/frontend/src/components/AppSidebar.vue

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ import {
4141
Users,
4242
UserRoundPen,
4343
FileSliders,
44-
FolderTree
44+
FolderTree,
45+
Monitor
4546
} from 'lucide-vue-next'
4647
4748
// Define props, including variant
@@ -101,6 +102,11 @@ const navigationItems = [
101102
icon: Key,
102103
url: '/credentials',
103104
},
105+
{
106+
title: t('sidebar.navigation.devices'),
107+
icon: Monitor,
108+
url: '/devices',
109+
},
104110
]
105111
106112
// Fetch user data logic using UserService

services/frontend/src/components/admin/mcp-catalog/ReviewStep.vue

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,11 @@ const loadFreshData = () => {
5353
}
5454
5555
// Storage change handler
56+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
5657
const handleStorageChange = (data: { key: string; oldValue: any; newValue: any }) => {
5758
// Reload data when any of our storage keys change
58-
if (data.key === 'edit_basic_data' ||
59-
data.key === 'edit_repository_data' ||
59+
if (data.key === 'edit_basic_data' ||
60+
data.key === 'edit_repository_data' ||
6061
data.key === 'edit_technical_data' ||
6162
data.key === 'edit_claude_config') {
6263
loadFreshData()
@@ -66,7 +67,7 @@ const handleStorageChange = (data: { key: string; oldValue: any; newValue: any }
6667
onMounted(() => {
6768
// Load initial data
6869
loadFreshData()
69-
70+
7071
// Listen for storage changes
7172
eventBus.on('storage-changed', handleStorageChange)
7273
})

services/frontend/src/components/admin/mcp-catalog/steps/ConfigurationSchemaStepAdd.vue

Lines changed: 37 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<!--
22
* ADD MODE CONFIGURATION SCHEMA STEP
3-
*
3+
*
44
* This component follows the ADD wizard architecture:
55
* - Uses v-model and props exclusively
66
* - No storage or event bus for data management
@@ -339,47 +339,47 @@ const assembleSchemaAndEmit = () => {
339339
localData.value.forEach(item => {
340340
if (item.type === 'arg') {
341341
if (item.category === 'template') {
342-
schema.template_args!.push({
343-
value: item.value || '',
344-
locked: item.locked,
345-
description: item.description
342+
schema.template_args!.push({
343+
value: item.value || '',
344+
locked: item.locked,
345+
description: item.description
346346
})
347347
} else if (item.category === 'team') {
348-
schema.team_args_schema!.push({
349-
name: item.name,
350-
type: item.dataType,
351-
description: item.description,
352-
required: item.required,
353-
locked: item.locked,
354-
default_team_locked: item.default_team_locked
348+
schema.team_args_schema!.push({
349+
name: item.name,
350+
type: item.dataType,
351+
description: item.description,
352+
required: item.required,
353+
locked: item.locked,
354+
default_team_locked: item.default_team_locked
355355
})
356356
} else if (item.category === 'user') {
357-
schema.user_args_schema!.push({
358-
name: item.name,
359-
type: item.dataType,
360-
description: item.description,
361-
required: item.required,
362-
locked: item.locked
357+
schema.user_args_schema!.push({
358+
name: item.name,
359+
type: item.dataType,
360+
description: item.description,
361+
required: item.required,
362+
locked: item.locked
363363
})
364364
}
365365
} else if (item.type === 'env') {
366366
if (item.category === 'team') {
367-
schema.team_env_schema!.push({
368-
name: item.name,
369-
type: item.dataType,
370-
description: item.description,
371-
required: item.required,
372-
locked: item.locked,
373-
default_team_locked: item.default_team_locked,
374-
visible_to_users: item.visible_to_users
367+
schema.team_env_schema!.push({
368+
name: item.name,
369+
type: item.dataType,
370+
description: item.description,
371+
required: item.required,
372+
locked: item.locked,
373+
default_team_locked: item.default_team_locked,
374+
visible_to_users: item.visible_to_users
375375
})
376376
} else if (item.category === 'user') {
377-
schema.user_env_schema!.push({
378-
name: item.name,
379-
type: item.dataType,
380-
description: item.description,
381-
required: item.required,
382-
locked: item.locked
377+
schema.user_env_schema!.push({
378+
name: item.name,
379+
type: item.dataType,
380+
description: item.description,
381+
required: item.required,
382+
locked: item.locked
383383
})
384384
}
385385
}
@@ -459,7 +459,7 @@ const validateForm = () => {
459459
} else {
460460
// Check for duplicates
461461
const isDuplicate = localData.value.some((item, index) =>
462-
item.name === formDataLocal.value.name &&
462+
item.name === formDataLocal.value.name &&
463463
item.type === formDataLocal.value.type &&
464464
index !== editingIndex.value
465465
)
@@ -481,7 +481,7 @@ const handleSubmit = () => {
481481
if (!validateForm()) return
482482
483483
const updatedData = [...localData.value]
484-
const newItem = {
484+
const newItem = {
485485
...formDataLocal.value,
486486
id: formDataLocal.value.id || `${formDataLocal.value.type}_${Date.now()}`
487487
}
@@ -508,7 +508,7 @@ const handleDelete = (index: number) => {
508508
509509
// Get category info for display with safe fallback
510510
const getCategoryInfo = (category: string) => {
511-
const allOptions = [...argCategoryOptions,
511+
const allOptions = [...argCategoryOptions,
512512
{ value: 'team', label: computed(() => t('mcpCatalog.form.configurationSchema.categories.team')), icon: Users, color: 'green' },
513513
{ value: 'user', label: computed(() => t('mcpCatalog.form.configurationSchema.categories.user')), icon: User, color: 'purple' },
514514
]
@@ -637,7 +637,7 @@ watch(localData, () => {
637637
</tr>
638638
</thead>
639639
<tbody>
640-
<tr v-for="(item, index) in argumentItems" :key="item.id">
640+
<tr v-for="(item) in argumentItems" :key="item.id">
641641
<td class="relative py-5 pr-6">
642642
<div class="flex gap-x-6">
643643
<div class="flex-auto">
@@ -719,7 +719,7 @@ watch(localData, () => {
719719
</div>
720720

721721
<!-- Environment Variables Section - Now using shared component -->
722-
<ConfigurationSchemaEnvironmentSection
722+
<ConfigurationSchemaEnvironmentSection
723723
:items="environmentItems"
724724
:get-category-info="getCategoryInfo"
725725
@add="handleEnvAdd"

services/frontend/src/components/admin/mcp-catalog/steps/ConfigurationSchemaStepEdit.vue

Lines changed: 46 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
<!--
22
* EDIT MODE CONFIGURATION SCHEMA STEP
3-
*
3+
*
44
* This component follows the EDIT wizard storage-first architecture:
5-
* - Uses event bus and localStorage exclusively
5+
* - Uses event bus and localStorage exclusively
66
* - No v-model or props for data management
77
* - Storage-first patterns throughout
88
* - Reads/writes data directly to/from storage
@@ -264,47 +264,47 @@ const assembleSchemaAndSave = () => {
264264
localData.value.forEach(item => {
265265
if (item.type === 'arg') {
266266
if (item.category === 'template') {
267-
schema.template_args!.push({
268-
value: item.value || '',
269-
locked: item.locked,
270-
description: item.description
267+
schema.template_args!.push({
268+
value: item.value || '',
269+
locked: item.locked,
270+
description: item.description
271271
})
272272
} else if (item.category === 'team') {
273-
schema.team_args_schema!.push({
274-
name: item.name,
275-
type: item.dataType,
276-
description: item.description,
277-
required: item.required,
278-
locked: item.locked,
279-
default_team_locked: item.default_team_locked
273+
schema.team_args_schema!.push({
274+
name: item.name,
275+
type: item.dataType,
276+
description: item.description,
277+
required: item.required,
278+
locked: item.locked,
279+
default_team_locked: item.default_team_locked
280280
})
281281
} else if (item.category === 'user') {
282-
schema.user_args_schema!.push({
283-
name: item.name,
284-
type: item.dataType,
285-
description: item.description,
286-
required: item.required,
287-
locked: item.locked
282+
schema.user_args_schema!.push({
283+
name: item.name,
284+
type: item.dataType,
285+
description: item.description,
286+
required: item.required,
287+
locked: item.locked
288288
})
289289
}
290290
} else if (item.type === 'env') {
291291
if (item.category === 'team') {
292-
schema.team_env_schema!.push({
293-
name: item.name,
294-
type: item.dataType,
295-
description: item.description,
296-
required: item.required,
297-
locked: item.locked,
298-
default_team_locked: item.default_team_locked,
299-
visible_to_users: item.visible_to_users
292+
schema.team_env_schema!.push({
293+
name: item.name,
294+
type: item.dataType,
295+
description: item.description,
296+
required: item.required,
297+
locked: item.locked,
298+
default_team_locked: item.default_team_locked,
299+
visible_to_users: item.visible_to_users
300300
})
301301
} else if (item.category === 'user') {
302-
schema.user_env_schema!.push({
303-
name: item.name,
304-
type: item.dataType,
305-
description: item.description,
306-
required: item.required,
307-
locked: item.locked
302+
schema.user_env_schema!.push({
303+
name: item.name,
304+
type: item.dataType,
305+
description: item.description,
306+
required: item.required,
307+
locked: item.locked
308308
})
309309
}
310310
}
@@ -315,13 +315,6 @@ const assembleSchemaAndSave = () => {
315315
}
316316
}
317317
318-
// Save data to storage
319-
const saveToStorage = () => {
320-
if (!isUpdatingFromStorage.value) {
321-
assembleSchemaAndSave()
322-
}
323-
}
324-
325318
// Computed properties
326319
const argumentItems = computed(() => {
327320
return localData.value.filter(item => item.type === 'arg')
@@ -387,7 +380,7 @@ const validateForm = () => {
387380
} else {
388381
// Check for duplicates
389382
const isDuplicate = localData.value.some((item, index) =>
390-
item.name === formDataLocal.value.name &&
383+
item.name === formDataLocal.value.name &&
391384
item.type === formDataLocal.value.type &&
392385
index !== editingIndex.value
393386
)
@@ -409,7 +402,7 @@ const handleSubmit = () => {
409402
if (!validateForm()) return
410403
411404
const updatedData = [...localData.value]
412-
const newItem = {
405+
const newItem = {
413406
...formDataLocal.value,
414407
id: formDataLocal.value.id || `${formDataLocal.value.type}_${Date.now()}`
415408
}
@@ -441,7 +434,7 @@ const updateData = (newData: ConfigItem[]) => {
441434
442435
// Get category info for display with safe fallback
443436
const getCategoryInfo = (category: string) => {
444-
const allOptions = [...argCategoryOptions,
437+
const allOptions = [...argCategoryOptions,
445438
{ value: 'team', label: computed(() => t('mcpCatalog.form.configurationSchema.categories.team')), icon: Users, color: 'green' },
446439
{ value: 'user', label: computed(() => t('mcpCatalog.form.configurationSchema.categories.user')), icon: User, color: 'purple' },
447440
]
@@ -499,10 +492,10 @@ const availableCategoryOptions = computed(() => {
499492
// Fresh data loading on step entry
500493
const refreshDataOnStepEntry = () => {
501494
loadFromStorageSchema()
502-
495+
503496
// Load persisted environment variables from TechnicalStep
504497
const extractedEnvVars = eventBus.getState<string[]>('technical_extracted_env_vars_edit')
505-
498+
506499
if (extractedEnvVars && extractedEnvVars.length > 0) {
507500
handleTechnicalEnvVarsUpdate({ envVars: extractedEnvVars })
508501
}
@@ -522,10 +515,10 @@ const handleStepChange = (data: { to: number; stepKey: string }) => {
522515
const handleTechnicalEnvVarsUpdate = (data: { envVars: string[] }) => {
523516
// Get current environment variables to avoid duplicates
524517
const currentEnvNames = environmentItems.value.map(item => item.name)
525-
518+
526519
// Add new environment variables that don't already exist
527520
const newItems: ConfigItem[] = []
528-
521+
529522
data.envVars.forEach(envVarName => {
530523
if (!currentEnvNames.includes(envVarName)) {
531524
newItems.push({
@@ -542,7 +535,7 @@ const handleTechnicalEnvVarsUpdate = (data: { envVars: string[] }) => {
542535
})
543536
}
544537
})
545-
538+
546539
// Add new items to existing data
547540
if (newItems.length > 0) {
548541
const updatedData = [...localData.value, ...newItems]
@@ -553,13 +546,13 @@ const handleTechnicalEnvVarsUpdate = (data: { envVars: string[] }) => {
553546
onMounted(() => {
554547
// Initialize with current storage data
555548
loadFromStorageSchema()
556-
549+
557550
// Listen for step changes
558551
eventBus.on('mcp-form-step-changed', handleStepChange)
559-
552+
560553
// Listen for environment variables updates from TechnicalStep
561554
eventBus.on('technical-env-vars-updated', handleTechnicalEnvVarsUpdate)
562-
555+
563556
// Load persisted env vars immediately on mount
564557
const extractedEnvVars = eventBus.getState<string[]>('technical_extracted_env_vars_edit')
565558
if (extractedEnvVars && extractedEnvVars.length > 0) {
@@ -610,7 +603,7 @@ onUnmounted(() => {
610603
</tr>
611604
</thead>
612605
<tbody>
613-
<tr v-for="(item, index) in argumentItems" :key="item.id">
606+
<tr v-for="(item) in argumentItems" :key="item.id">
614607
<td class="relative py-5 pr-6">
615608
<div class="flex gap-x-6">
616609
<div class="flex-auto">
@@ -692,7 +685,7 @@ onUnmounted(() => {
692685
</div>
693686

694687
<!-- Environment Variables Section - Now using shared component -->
695-
<ConfigurationSchemaEnvironmentSection
688+
<ConfigurationSchemaEnvironmentSection
696689
:items="environmentItems"
697690
:get-category-info="getCategoryInfo"
698691
@add="handleEnvAdd"

services/frontend/src/components/mcp-server/installation/TeamConfiguration.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
<script setup lang="ts">
2+
23
import { ref, computed, onMounted } from 'vue'
34
import { useI18n } from 'vue-i18n'
45
import { toast } from 'vue-sonner'
5-
import { Settings, Edit, Eye, EyeOff } from 'lucide-vue-next'
6+
import { Settings, Eye, EyeOff } from 'lucide-vue-next'
67
import { McpInstallationService } from '@/services/mcpInstallationService'
78
import { McpCatalogService } from '@/services/mcpCatalogService'
8-
import { TeamService } from '@/services/teamService'
99
import { Button } from '@/components/ui/button'
1010
import { Badge } from '@/components/ui/badge'
1111
import { Input } from '@/components/ui/input'

0 commit comments

Comments
 (0)