From ab76c952589483f2c8fb2a058223d0e7f8fda02b Mon Sep 17 00:00:00 2001 From: Krishav Raj Singh Date: Thu, 31 Jul 2025 03:44:32 +0530 Subject: [PATCH] ui: include sections --- .../tool-views/task-list/TaskListToolView.tsx | 299 ++++++++++-------- .../thread/tool-views/task-list/_utils.ts | 37 ++- .../tool-views/wrapper/ToolViewRegistry.tsx | 2 +- 3 files changed, 189 insertions(+), 149 deletions(-) diff --git a/frontend/src/components/thread/tool-views/task-list/TaskListToolView.tsx b/frontend/src/components/thread/tool-views/task-list/TaskListToolView.tsx index 63268a86..e027fe1d 100644 --- a/frontend/src/components/thread/tool-views/task-list/TaskListToolView.tsx +++ b/frontend/src/components/thread/tool-views/task-list/TaskListToolView.tsx @@ -1,40 +1,35 @@ import type React from "react" -import { Check, Circle, X, Clock, AlertTriangle, CircleCheck, CircleX } from "lucide-react" -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" -import { Badge } from "@/components/ui/badge" +import { Check, Clock, Pause, ChevronDown, CircleX, CheckCircle, AlertTriangle, ListTodo, X } from "lucide-react" import { cn } from "@/lib/utils" -import { extractTaskListData, type Task, type TaskListData } from "./_utils" +import { extractTaskListData, type Task, type Section } from "./_utils" +import { getToolTitle } from "../utils" import type { ToolViewProps } from "../types" +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' +import { Badge } from '@/components/ui/badge' import { ScrollArea } from "@/components/ui/scroll-area" const TaskItem: React.FC<{ task: Task; index: number }> = ({ task, index }) => { const isCompleted = task.status === "completed" - const isCancelled = task.status === "cancelled" + const isCancelled = task.status === "cancelled" + const isPending = !isCompleted && !isCancelled return ( -
+
{/* Status Icon */} -
- {isCompleted && } - {isCancelled && } - {!isCompleted && !isCancelled && } +
+ {isCompleted && } + {isCancelled && } + {isPending && }
- {/* Content */} + {/* Task Content */}

{task.content} @@ -44,128 +39,166 @@ const TaskItem: React.FC<{ task: Task; index: number }> = ({ task, index }) => { ) } -const EmptyState: React.FC = () => ( -

-

No tasks yet

-

Tasks will appear here as they are created

-
-) +const SectionHeader: React.FC<{ section: Section }> = ({ section }) => { + const totalTasks = section.tasks.length + const completedTasks = section.tasks.filter((t) => t.status === "completed").length -export const TaskListToolView: React.FC = ({ - assistantContent, - toolContent, - isStreaming = false, + return ( +
+

{section.title}

+
+ + {completedTasks}/{totalTasks} + + {completedTasks === totalTasks && totalTasks > 0 && ( + + + + )} +
+
+ ) +} + +const SectionView: React.FC<{ section: Section }> = ({ section }) => { + return ( +
+ +
+ {section.tasks.map((task, index) => ( + + ))} + {section.tasks.length === 0 && ( +
+

No tasks in this section

+
+ )} +
+
+ ) +} + + + +export const TaskListToolView: React.FC = ({ + name = 'task-list', + assistantContent, + toolContent, + assistantTimestamp, + toolTimestamp, + isSuccess = true, + isStreaming = false }) => { const taskData = extractTaskListData(assistantContent, toolContent) + const toolTitle = getToolTitle(name) - // Show loading state while streaming and no data - if (isStreaming && !taskData) { - return ( - - -
- - Task List -
-
- -

Processing tasks...

-
-
- ) - } - console.log('taskData', taskData) - // Show no data state if no task data - if (!taskData) { - return ( - - -
- - Task List -
-
- -

No task data available

-
-
- ) - } - - // Show task data - const tasks = taskData.tasks || [] - const totalTasks = tasks.length - const completedTasks = tasks.filter((t) => t.status === "completed").length - const cancelledTasks = tasks.filter((t) => t.status === "cancelled").length - const completionPercentage = totalTasks > 0 ? ((completedTasks + cancelledTasks) / totalTasks) * 100 : 0 + // Process task data + const sections = taskData?.sections || [] + const allTasks = sections.flatMap((section) => section.tasks) + const totalTasks = allTasks.length + const completedTasks = allTasks.filter((t) => t.status === "completed").length + const hasData = sections.length > 0 return ( - -
-
-
- -
-
- - Task List - -
-
+ +
+
+
+ +
+
+ + {toolTitle} + +
+
- - {completedTasks}/{totalTasks} completed - -
-
- - - -
-
- {tasks.length > 0 ? ( - tasks.map((task, index) => ( - - ) - ) - ) : ( - + {!isStreaming && ( +
+ + {completedTasks} / {totalTasks} tasks + + + {isSuccess ? ( + + ) : ( + + )} + {isSuccess ? 'Tasks loaded' : 'Failed to load'} + +
)} - {/* Progress Bar */} - {tasks.length > 0 &&
-
0 && completionPercentage <= 25 && "bg-yellow-400", - completionPercentage > 25 && completionPercentage <= 50 && "bg-yellow-500", - completionPercentage > 50 && completionPercentage <= 75 && "bg-green-300", - completionPercentage > 75 && completionPercentage < 100 && "bg-green-400", - completionPercentage === 100 && "bg-green-600" +
+ + + + {isStreaming && !hasData ? ( +
+
+ +
+

+ Loading Tasks +

+

+ Preparing your task list... +

+
+ ) : hasData ? ( + +
+ {sections.map((section) => )} +
+
+ ) : ( +
+
+ +
+

+ No Tasks Yet +

+

+ Your task list will appear here once created +

+
+ )} +
+ +
+
+ {!isStreaming && hasData && ( +
+ + + {sections.length} sections + + {completedTasks === totalTasks && totalTasks > 0 && ( + + + All complete + )} - style={{ width: `${completionPercentage}%` }} - /> -
} - +
+ )} +
+ +
+ {toolTimestamp && !isStreaming + ? new Date(toolTimestamp).toLocaleTimeString() + : assistantTimestamp + ? new Date(assistantTimestamp).toLocaleTimeString() + : ''}
- - - -
-
- {tasks.length > 0 && ( - - - {tasks.length} tasks - - )} -
-
- + ) -} \ No newline at end of file +} diff --git a/frontend/src/components/thread/tool-views/task-list/_utils.ts b/frontend/src/components/thread/tool-views/task-list/_utils.ts index b72fe7c6..ea391b66 100644 --- a/frontend/src/components/thread/tool-views/task-list/_utils.ts +++ b/frontend/src/components/thread/tool-views/task-list/_utils.ts @@ -1,17 +1,21 @@ -export interface TaskListData { - tasks: Task[] - filter?: string - total?: number - message?: string -} - export interface Task { id: string content: string status: "pending" | "completed" | "cancelled" - created_at: string - updated_at: string - completed_at?: string + section: string // Fixed: should be section, not section_id +} + +export interface Section { + id: string + title: string + tasks: Task[] +} + +export interface TaskListData { + sections: Section[] + total?: number + message?: string + filter?: string } export function extractTaskListData( @@ -41,14 +45,15 @@ export function extractTaskListData( const output = parsedContent.tool_execution.result.output; const outputData = parseContent(output); - if (outputData?.tasks && Array.isArray(outputData.tasks)) { - return outputData; + // Nested sections format + if (outputData?.sections && Array.isArray(outputData.sections)) { + return { sections: outputData.sections }; } } - // Check for direct tasks array - if (parsedContent.tasks && Array.isArray(parsedContent.tasks)) { - return parsedContent; + // Check for direct sections array + if (parsedContent.sections && Array.isArray(parsedContent.sections)) { + return { sections: parsedContent.sections }; } // Check for nested content @@ -58,6 +63,8 @@ export function extractTaskListData( return null; }; + + // Try tool content first, then assistant content return extractFromNewFormat(toolContent) || extractFromNewFormat(assistantContent); diff --git a/frontend/src/components/thread/tool-views/wrapper/ToolViewRegistry.tsx b/frontend/src/components/thread/tool-views/wrapper/ToolViewRegistry.tsx index 1b05064c..c76a620b 100644 --- a/frontend/src/components/thread/tool-views/wrapper/ToolViewRegistry.tsx +++ b/frontend/src/components/thread/tool-views/wrapper/ToolViewRegistry.tsx @@ -81,7 +81,7 @@ const defaultRegistry: ToolViewRegistryType = { 'view-tasks': TaskListToolView, 'update-tasks': TaskListToolView, 'delete-tasks': TaskListToolView, - 'clear-all-tasks': TaskListToolView, + 'clear-tasks': TaskListToolView, 'expose-port': ExposePortToolView,