Replaces stub ToolsTab with a grouped-by-module grid of clickable tool cards. Adds ToolDetailDrawer (Sheet) showing name, description, module, source_location, and params_schema for the selected tool. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
84 lines
2.9 KiB
TypeScript
84 lines
2.9 KiB
TypeScript
"use client";
|
||
|
||
import { useState, useMemo } from "react";
|
||
import { Wrench, AlertCircle } from "lucide-react";
|
||
import { Card, CardContent } from "@/components/ui/card";
|
||
import { Skeleton } from "@/components/ui/skeleton";
|
||
import { Badge } from "@/components/ui/badge";
|
||
import { useMcpTools, type McpTool } from "@/lib/api/settings";
|
||
import { ToolDetailDrawer } from "./tool-detail-drawer";
|
||
|
||
export function ToolsTab() {
|
||
const { data, isPending, error } = useMcpTools();
|
||
const [selected, setSelected] = useState<McpTool | null>(null);
|
||
const [open, setOpen] = useState(false);
|
||
|
||
const grouped = useMemo(() => {
|
||
if (!data?.tools) return new Map<string, McpTool[]>();
|
||
const m = new Map<string, McpTool[]>();
|
||
for (const t of data.tools) {
|
||
const mod = t.module.split(".").pop() || "other";
|
||
const arr = m.get(mod) ?? [];
|
||
arr.push(t);
|
||
m.set(mod, arr);
|
||
}
|
||
return m;
|
||
}, [data]);
|
||
|
||
if (isPending) return <Skeleton className="h-96 w-full" />;
|
||
if (error) {
|
||
return (
|
||
<Card className="bg-surface border-danger/40">
|
||
<CardContent className="p-6 flex items-center gap-3 text-danger">
|
||
<AlertCircle className="w-5 h-5" />
|
||
<span>שגיאה בטעינת tools: {error.message}</span>
|
||
</CardContent>
|
||
</Card>
|
||
);
|
||
}
|
||
if (!data) return null;
|
||
|
||
return (
|
||
<div className="space-y-4">
|
||
<div className="flex items-center gap-2 text-sm text-ink-muted">
|
||
<Wrench className="w-4 h-4" />
|
||
סה"כ {data.count} tools
|
||
</div>
|
||
{[...grouped.entries()].sort().map(([mod, tools]) => (
|
||
<Card key={mod} className="bg-surface border-rule">
|
||
<CardContent className="px-6 py-5">
|
||
<h2 className="text-navy text-lg mb-3 flex items-center gap-2">
|
||
<code dir="ltr">{mod}</code>
|
||
<Badge variant="outline" className="text-[0.7rem]">
|
||
{tools.length}
|
||
</Badge>
|
||
</h2>
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
|
||
{tools.map((t) => (
|
||
<button
|
||
key={t.name}
|
||
onClick={() => {
|
||
setSelected(t);
|
||
setOpen(true);
|
||
}}
|
||
className="text-start rounded-md border border-rule px-3 py-2 hover:bg-rule-soft/40 transition-colors"
|
||
>
|
||
<code dir="ltr" className="font-mono text-sm text-navy">
|
||
{t.name}
|
||
</code>
|
||
{t.description && (
|
||
<p className="text-[0.78rem] text-ink-muted mt-0.5 line-clamp-2">
|
||
{t.description}
|
||
</p>
|
||
)}
|
||
</button>
|
||
))}
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
))}
|
||
<ToolDetailDrawer tool={selected} open={open} onOpenChange={setOpen} />
|
||
</div>
|
||
);
|
||
}
|