// ======================================== // SolutionDrawer Component // ======================================== // Right-side solution detail drawer import { useEffect, useMemo, useState } from 'react'; import { useIntl } from 'react-intl'; import { X, FileText, CheckCircle, Circle, Loader2, XCircle, Clock, AlertTriangle, Terminal } from 'lucide-react'; import { Badge } from '@/components/ui/Badge'; import { Button } from '@/components/ui/Button'; import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/Tabs'; import { QueueExecuteInSession } from '@/components/issue/queue/QueueExecuteInSession'; import { IssueTerminalTab } from '@/components/issue/hub/IssueTerminalTab'; import { useIssueQueue } from '@/hooks'; import { cn } from '@/lib/utils'; import type { QueueItem } from '@/lib/api'; // ========== Types ========== export interface SolutionDrawerProps { item: QueueItem | null; isOpen: boolean; onClose: () => void; } type TabValue = 'overview' | 'tasks' | 'terminal' | 'json'; // ========== Status Configuration ========== const statusConfig: Record }> = { pending: { label: 'issues.queue.status.pending', variant: 'secondary', icon: Circle }, ready: { label: 'issues.queue.status.ready', variant: 'info', icon: Clock }, executing: { label: 'issues.queue.status.executing', variant: 'warning', icon: Loader2 }, completed: { label: 'issues.queue.status.completed', variant: 'success', icon: CheckCircle }, failed: { label: 'issues.queue.status.failed', variant: 'destructive', icon: XCircle }, blocked: { label: 'issues.queue.status.blocked', variant: 'destructive', icon: AlertTriangle }, }; // ========== Component ========== export function SolutionDrawer({ item, isOpen, onClose }: SolutionDrawerProps) { const { formatMessage } = useIntl(); const [activeTab, setActiveTab] = useState('overview'); const { data: queue } = useIssueQueue(); const itemId = item?.item_id; const solutionId = item?.solution_id; // ESC key to close useEffect(() => { if (!isOpen) return; const handleEsc = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose(); }; window.addEventListener('keydown', handleEsc); return () => window.removeEventListener('keydown', handleEsc); }, [isOpen, onClose]); // Reset tab when switching items useEffect(() => { if (!isOpen || !itemId) return; setActiveTab('overview'); }, [itemId, isOpen]); const tasksForSolution = useMemo(() => { if (!solutionId) return []; const allItems = Object.values(queue?.grouped_items || {}).flat(); const isTaskItem = (qi: QueueItem) => Boolean(qi.task_id) || qi.item_id.startsWith('task-'); return allItems .filter((qi) => qi.solution_id === solutionId && isTaskItem(qi)) .sort((a, b) => a.execution_order - b.execution_order); }, [queue?.grouped_items, solutionId]); if (!item || !isOpen) { return null; } const status = statusConfig[item.status] || statusConfig.pending; const StatusIcon = status.icon; // Get solution details (would need to fetch full solution data) const issueId = item.issue_id; return ( <> {/* Overlay */}