Skip to content

Commit b55c2a4

Browse files
author
Lasim
committed
feat(frontend): implement expandable rows in ToolsTab for detailed view
1 parent 39d0b65 commit b55c2a4

File tree

2 files changed

+96
-42
lines changed

2 files changed

+96
-42
lines changed

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

Lines changed: 92 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@ import { Button } from '@/components/ui/button'
1010
import { ButtonGroup } from '@/components/ui/button-group'
1111
import { Spinner } from '@/components/ui/spinner'
1212
import { Alert, AlertDescription } from '@/components/ui/alert'
13-
import { AlertCircle, Package, CircleCheck, CircleMinus } from 'lucide-vue-next'
13+
import { AlertCircle, Package, CircleCheck, CircleMinus, ChevronRight, ChevronDown } from 'lucide-vue-next'
1414
import type { McpInstallation } from '@/types/mcp-installations'
1515
import ToolsMetricsPanel from './ToolsMetricsPanel.vue'
16+
import { CodeHighlight } from '@/components/ui/code-highlight'
1617
1718
interface Props {
1819
installation: McpInstallation
@@ -35,6 +36,18 @@ const error = ref<string | null>(null)
3536
const selectedToolIds = ref<string[]>([])
3637
const isBulkToggling = ref(false)
3738
39+
// Track expanded rows
40+
const expandedRows = ref<Set<string>>(new Set())
41+
42+
// Toggle row expansion
43+
const toggleRow = (toolId: string) => {
44+
if (expandedRows.value.has(toolId)) {
45+
expandedRows.value.delete(toolId)
46+
} else {
47+
expandedRows.value.add(toolId)
48+
}
49+
}
50+
3851
// Format token count with commas
3952
const formatTokenCount = (count: number) => {
4053
return count.toLocaleString()
@@ -262,55 +275,92 @@ async function handleBulkToggle(isDisabled: boolean) {
262275
@update:checked="toggleAllTools"
263276
/>
264277
</TableHead>
278+
<TableHead class="w-12"></TableHead>
265279
<TableHead>{{ t('mcpInstallations.details.tools.table.columns.status') }}</TableHead>
266280
<TableHead>{{ t('mcpInstallations.details.tools.table.columns.toolName') }}</TableHead>
267-
<TableHead>{{ t('mcpInstallations.details.tools.table.columns.description') }}</TableHead>
281+
<TableHead class="w-96">{{ t('mcpInstallations.details.tools.table.columns.description') }}</TableHead>
268282
<TableHead class="text-right">{{ t('mcpInstallations.details.tools.table.columns.tokenCount') }}</TableHead>
269283
<TableHead class="text-right">{{ t('mcpInstallations.details.tools.table.columns.distribution') }}</TableHead>
270284
</TableRow>
271285
</TableHeader>
272286
<TableBody>
273-
<TableRow v-for="tool in tools.tools" :key="tool.id">
274-
<TableCell>
275-
<Checkbox
276-
:checked="isToolSelected(tool.id)"
277-
:disabled="!props.canEdit"
278-
@update:checked="() => toggleToolSelection(tool.id)"
279-
/>
280-
</TableCell>
281-
<TableCell>
282-
<div
283-
class="inline-flex items-center justify-center rounded-full border px-1.5 py-0.5 text-xs font-medium text-muted-foreground gap-1"
284-
>
285-
<CircleCheck
286-
v-if="!tool.is_disabled"
287-
class="size-3 fill-green-500 text-green-500 dark:fill-green-400 dark:text-green-400"
287+
<template v-for="tool in tools.tools" :key="tool.id">
288+
<!-- Main Tool Row (clickable) -->
289+
<TableRow
290+
class="cursor-pointer hover:bg-muted/50"
291+
@click.stop="toggleRow(tool.id)"
292+
>
293+
<TableCell @click.stop>
294+
<Checkbox
295+
:checked="isToolSelected(tool.id)"
296+
:disabled="!props.canEdit"
297+
@update:checked="() => toggleToolSelection(tool.id)"
288298
/>
289-
<CircleMinus
290-
v-else
291-
class="size-3 text-muted-foreground"
292-
/>
293-
<span>
294-
{{ tool.is_disabled
295-
? t('mcpInstallations.details.tools.table.values.disabled')
296-
: t('mcpInstallations.details.tools.table.values.enabled')
297-
}}
298-
</span>
299-
</div>
300-
</TableCell>
301-
<TableCell class="text-sm font-medium">{{ tool.tool_name }}</TableCell>
302-
<TableCell class="text-sm text-muted-foreground max-w-2xl">
303-
<div class="whitespace-normal wrap-break-word">
304-
{{ tool.description || t('mcpInstallations.details.tools.table.values.noDescription') }}
305-
</div>
306-
</TableCell>
307-
<TableCell class="text-right whitespace-nowrap text-sm font-medium">
308-
{{ formatTokenCount(tool.token_count) }}
309-
</TableCell>
310-
<TableCell class="text-right whitespace-nowrap text-sm text-muted-foreground">
311-
{{ calculateTokenPercentage(tool.token_count) }}
312-
</TableCell>
313-
</TableRow>
299+
</TableCell>
300+
<TableCell>
301+
<Button variant="ghost" size="icon" class="h-6 w-6">
302+
<ChevronRight v-if="!expandedRows.has(tool.id)" class="h-4 w-4" />
303+
<ChevronDown v-else class="h-4 w-4" />
304+
</Button>
305+
</TableCell>
306+
<TableCell>
307+
<div
308+
class="inline-flex items-center justify-center rounded-full border px-1.5 py-0.5 text-xs font-medium text-muted-foreground gap-1"
309+
>
310+
<CircleCheck
311+
v-if="!tool.is_disabled"
312+
class="size-3 fill-green-500 text-green-500 dark:fill-green-400 dark:text-green-400"
313+
/>
314+
<CircleMinus
315+
v-else
316+
class="size-3 text-muted-foreground"
317+
/>
318+
<span>
319+
{{ tool.is_disabled
320+
? t('mcpInstallations.details.tools.table.values.disabled')
321+
: t('mcpInstallations.details.tools.table.values.enabled')
322+
}}
323+
</span>
324+
</div>
325+
</TableCell>
326+
<TableCell class="text-sm font-medium">{{ tool.tool_name }}</TableCell>
327+
<TableCell class="text-sm text-muted-foreground w-96">
328+
<div class="truncate">
329+
{{ tool.description || t('mcpInstallations.details.tools.table.values.noDescription') }}
330+
</div>
331+
</TableCell>
332+
<TableCell class="text-right whitespace-nowrap text-sm font-medium">
333+
{{ formatTokenCount(tool.token_count) }}
334+
</TableCell>
335+
<TableCell class="text-right whitespace-nowrap text-sm text-muted-foreground">
336+
{{ calculateTokenPercentage(tool.token_count) }}
337+
</TableCell>
338+
</TableRow>
339+
340+
<!-- Expanded Detail Row -->
341+
<TableRow v-if="expandedRows.has(tool.id)" class="bg-muted/30">
342+
<TableCell colspan="7" class="p-6">
343+
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
344+
<!-- Left: Full Description -->
345+
<div>
346+
<h4 class="text-sm font-semibold mb-2">{{ t('mcpInstallations.details.tools.detail.description') }}</h4>
347+
<p class="text-sm text-muted-foreground">
348+
{{ tool.description || t('mcpInstallations.details.tools.table.values.noDescription') }}
349+
</p>
350+
</div>
351+
352+
<!-- Right: Input Schema -->
353+
<div>
354+
<h4 class="text-sm font-semibold mb-2">{{ t('mcpInstallations.details.tools.detail.inputSchema') }}</h4>
355+
<CodeHighlight
356+
:code="JSON.stringify(tool.input_schema, null, 2)"
357+
language="json"
358+
/>
359+
</div>
360+
</div>
361+
</TableCell>
362+
</TableRow>
363+
</template>
314364
</TableBody>
315365
</Table>
316366
</div>

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -641,6 +641,10 @@ export default {
641641
disabled: 'Disabled'
642642
}
643643
},
644+
detail: {
645+
description: 'Description',
646+
inputSchema: 'Input Schema'
647+
},
644648
bulkActions: {
645649
enable: 'Enable',
646650
disable: 'Disable'

0 commit comments

Comments
 (0)