import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from "@/components/ui/alertDialog"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { CodeEditor } from "@/components/ui/codeEditor"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger } from "@/components/ui/dropdownMenu"; import { DottedSeparator } from "@/components/ui/separator"; import { Sheet, SheetContent, SheetHeader, SheetTitle } from "@/components/ui/sheet"; import { downloadAsJson } from "@/lib/utils/browser-download"; import { Status, StatusColors, Statuses } from "@/lib/constants/logs"; import type { MCPToolLogEntry } from "@/lib/types/logs"; import { ChevronDown, ChevronUp, Download, MoreVertical, Trash2 } from "lucide-react"; import { addMilliseconds, format, isValid } from "date-fns"; import { useState, type ReactNode } from "react"; import { useHotkeys } from "react-hotkeys-hook"; import { toast } from "sonner"; interface MCPLogDetailSheetProps { log: MCPToolLogEntry | null; open: boolean; onOpenChange: (open: boolean) => void; handleDelete: (log: MCPToolLogEntry) => Promise; onNavigate?: (direction: "prev" | "next") => void; hasPrev?: boolean; hasNext?: boolean; } const LogEntryDetailsView = ({ label, value, className }: { label: string; value: React.ReactNode; className?: string }) => (
{label}
{value}
); const BlockHeader = ({ title, icon }: { title: string; icon?: ReactNode }) => { return (
{icon}
{title}
); }; // Helper function to validate status and return a safe Status value const getValidatedStatus = (status: string): Status => { // Check if status is a valid Status by checking against Statuses array if (Statuses.includes(status as Status)) { return status as Status; } // Fallback to "processing" for unknown statuses return "processing"; }; export function MCPLogDetailSheet({ log, open, onOpenChange, handleDelete, onNavigate, hasPrev = false, hasNext = false, }: MCPLogDetailSheetProps) { const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); // Keyboard navigation: arrow up/down to navigate between logs useHotkeys("up", () => onNavigate?.("prev"), { enabled: open && hasPrev, preventDefault: true }); useHotkeys("down", () => onNavigate?.("next"), { enabled: open && hasNext, preventDefault: true }); if (!log) return null; return (
{log.id &&

Request ID: {log.id}

} {log.status}
downloadAsJson(log, `mcp-log-${log.id ?? "export"}.json`)} > Export as JSON Delete log Are you sure you want to delete this log? This action cannot be undone. This will permanently delete the log entry. Cancel { e.preventDefault(); try { await handleDelete(log); setDeleteDialogOpen(false); onOpenChange(false); } catch (err) { const errorMessage = err instanceof Error ? err.message : "Failed to delete log"; toast.error(errorMessage); // Keep dialog open on error so user can see the error and retry } }} > Delete
{log.tool_name}} /> {log.server_label} ) : ( "-" ) } /> {log.virtual_key && } {log.llm_request_id && ( {log.llm_request_id}} /> )}
{/* Arguments */} {log.arguments && (
Arguments
, null, 2)} lang="json" readonly={true} options={{ scrollBeyondLastLine: false, collapsibleBlocks: true, lineNumbers: "off", alwaysConsumeMouseWheel: false }} />
)} {/* Result */} {log.result && log.status !== "processing" && (
Result
)} {/* Metadata */} {log.metadata && Object.keys(log.metadata).length > 0 && (
{Object.entries(log.metadata).map(([key, value]) => ( ))}
)} {/* Error Details */} {log.error_details && (
Error Details
)}
); }