import { Plan } from "@vertesia/common"; import { Badge, Button, cn } from "@vertesia/ui/core"; import { AlertCircle, CheckCircle, Circle, Clock } from "lucide-react"; import React from "react"; import { useUITranslation } from '../../../../i18n/index.js'; interface InlinePlanPanelProps { plan: Plan; workstreamStatus: Map; isOpen: boolean; onClose: () => void; plans?: Array<{ plan: Plan; timestamp: number }>; activePlanIndex?: number; onChangePlan?: (index: number) => void; } function InlineSlidingPlanPanelComponent({ plan, workstreamStatus, isOpen, onClose: _onClose, plans = [], activePlanIndex = 0, onChangePlan = () => { }, }: InlinePlanPanelProps) { const { t } = useUITranslation(); // Don't render if panel is closed if (!isOpen) { return null; } // Render the normal panel return (
{/* Plan Summary - count only tasks, excluding main workstream */}
{t('agent.taskProgress')}
{/* Calculate progress based on plan tasks, regardless of workstream */} {(() => { // Get all tasks from the plan itself const planTasks = plan.plan || []; const totalTasks = planTasks.length; // Count completed tasks by checking their status in workstreamStatus let completedTasks = 0; if (totalTasks > 0) { // Count each completed task from the plan planTasks.forEach((task) => { if (task && task.id) { const taskId = task.id.toString(); const taskStatus = workstreamStatus.get(taskId); if (taskStatus === "completed") { completedTasks++; } } }); } // Calculate percentage const progressPercentage = totalTasks > 0 ? Math.round((completedTasks / totalTasks) * 100) : 0; return ( <>
{totalTasks > 0 ? `${completedTasks}/${totalTasks}` : "0/0"} ); })()}
{/* Plan selector - only shown when multiple plans exist */} {plans.length > 1 && (
{plans[activePlanIndex]?.timestamp ? new Date( plans[activePlanIndex].timestamp, ).toLocaleTimeString() : t('agent.unknownTime')}
)} {/* Detailed Plan Steps */}
{t('agent.stepByStepPlan')}
{plan.plan && plan.plan.length > 0 ? ( plan.plan.map((task, index) => { // Extract task info with null checks const taskId = task.id ? task.id.toString() : `task-${index}`; const taskGoal = task.goal || `Task ${index + 1}`; // Determine task status - use task.status if available or lookup from workstream let status: | "pending" | "in_progress" | "completed" | "skipped" = task.status || "pending"; if (workstreamStatus.has(taskId)) { status = workstreamStatus.get(taskId)!; } // Determine status icon and style let StatusIcon = Circle; let statusColor = "text-muted"; if (status === "in_progress") { StatusIcon = Clock; statusColor = "text-info"; } else if (status === "completed") { StatusIcon = CheckCircle; statusColor = "text-success"; } return (
{taskId}
{taskGoal}
{status === "completed" ? t('agent.completed') : status === "in_progress" ? t('agent.inProgress') : t('agent.pending')}
); }) ) : (

{t('agent.noPlanDetected')}

{t('agent.plansWillAppear')}

)}
{/* Workstream Status Summary - excluding main and task IDs from the plan */} {(() => { // Get all task IDs from the plan for filtering const planTaskIds = new Set( (plan.plan || []) .filter((task) => task && task.id) .map((task) => task.id.toString()), ); // Filter out 'main' workstream and any IDs that are tasks in the plan const workstreamEntries = Array.from( workstreamStatus.entries(), ).filter(([id]) => id !== "main" && !planTaskIds.has(id)); // Only show the section if there are actual workstreams (not tasks or main) return workstreamEntries.length > 0 ? (
{t('agent.workstreams')}
{workstreamEntries.map(([id, status]) => { let StatusIcon = Circle; let statusColor = "text-gray-400"; let statusBg = "bg-gray-100 dark:bg-gray-800"; let statusText = t('agent.pending'); if (status === "in_progress") { StatusIcon = Clock; statusColor = "text-info"; statusBg = "bg-info/20"; statusText = t('agent.inProgress'); } else if (status === "completed") { StatusIcon = CheckCircle; statusColor = "text-success"; statusBg = "bg-success/20"; statusText = t('agent.completed'); } return (
{id}
{statusText}
); })}
) : null; })()}
); } const InlineSlidingPlanPanel = React.memo(InlineSlidingPlanPanelComponent); export default InlineSlidingPlanPanel;