"use client"; import type React from "react"; import { useState } from "react"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Input } from "@/components/ui/input"; import { ArrowLeft, Search, Filter, CheckCircle, XCircle, Loader2, GitBranch, GitPullRequest, Bug, Calendar, Clock, } from "lucide-react"; import { useRouter } from "next/navigation"; import { ThreadDisplayInfo, threadToDisplayInfo } from "@/components/v2/types"; import { useThreads } from "@/hooks/useThreads"; import { GraphState } from "@open-swe/shared/open-swe/types"; type FilterStatus = "all" | "running" | "completed" | "failed" | "pending"; export default function AllThreadsPage() { const router = useRouter(); const { threads } = useThreads(); const [searchQuery, setSearchQuery] = useState(""); const [statusFilter, setStatusFilter] = useState("all"); // Convert Thread objects to ThreadDisplayInfo for UI const displayThreads: ThreadDisplayInfo[] = threads?.map(threadToDisplayInfo) ?? []; const getStatusColor = (status: ThreadDisplayInfo["status"]) => { switch (status) { case "running": return "bg-blue-950 text-blue-400"; case "completed": return "bg-green-950 text-green-400"; case "failed": return "bg-red-950 text-red-400"; case "pending": return "bg-yellow-950 text-yellow-400"; default: return "bg-gray-800 text-gray-400"; } }; const getStatusIcon = (status: ThreadDisplayInfo["status"]) => { switch (status) { case "running": return ; case "completed": return ; case "failed": return ; case "pending": return ; default: return null; } }; const getPRStatusColor = (status: string) => { switch (status) { case "merged": return "text-purple-400"; case "open": return "text-green-400"; case "draft": return "text-gray-400"; case "closed": return "text-red-400"; default: return "text-gray-400"; } }; // Filter and search threads const filteredThreads = displayThreads.filter((thread) => { const matchesSearch = thread.title.toLowerCase().includes(searchQuery.toLowerCase()) || thread.repository.toLowerCase().includes(searchQuery.toLowerCase()); const matchesStatus = statusFilter === "all" || thread.status === statusFilter; return matchesSearch && matchesStatus; }); // Group threads by status const groupedThreads = { running: filteredThreads.filter((t) => t.status === "running"), completed: filteredThreads.filter((t) => t.status === "completed"), failed: filteredThreads.filter((t) => t.status === "failed"), pending: filteredThreads.filter((t) => t.status === "pending"), }; const statusCounts = { all: displayThreads.length, running: displayThreads.filter((t) => t.status === "running").length, completed: displayThreads.filter((t) => t.status === "completed").length, failed: displayThreads.filter((t) => t.status === "failed").length, pending: displayThreads.filter((t) => t.status === "pending").length, }; const handleThreadClick = (thread: ThreadDisplayInfo) => { router.push(`/chat/${thread.id}`); }; return (
{/* Header */}
All Threads
{filteredThreads.length} threads
{/* Search and Filters */}
setSearchQuery(e.target.value)} className="border-gray-700 bg-gray-900 pl-10 text-gray-300 placeholder:text-gray-600" />
Filter: {( [ "all", "running", "completed", "failed", "pending", ] as FilterStatus[] ).map((status) => ( ))}
{/* Content */}
{statusFilter === "all" ? ( // Show grouped view when "all" is selected
{Object.entries(groupedThreads).map(([status, threads]) => { if (threads.length === 0) return null; return (

{status} Threads

{threads.length}
{threads.map((thread) => ( handleThreadClick(thread)} getStatusColor={getStatusColor} getStatusIcon={getStatusIcon} getPRStatusColor={getPRStatusColor} /> ))}
); })}
) : ( // Show flat list when specific status is selected
{filteredThreads.map((thread) => ( handleThreadClick(thread)} getStatusColor={getStatusColor} getStatusIcon={getStatusIcon} getPRStatusColor={getPRStatusColor} /> ))}
)} {filteredThreads.length === 0 && (
No threads found
{searchQuery ? "Try adjusting your search query" : "No threads match the selected filter"}
)}
); } interface ThreadCardProps { thread: ThreadDisplayInfo; onClick: () => void; getStatusColor: (status: ThreadDisplayInfo["status"]) => string; getStatusIcon: (status: ThreadDisplayInfo["status"]) => React.ReactNode; getPRStatusColor: (status: string) => string; } function ThreadCard({ thread, onClick, getStatusColor, getStatusIcon, getPRStatusColor, }: ThreadCardProps) { return (
{thread.title}
{thread.repository}
{getStatusIcon(thread.status)} {thread.status}
{thread.taskCount} tasks •
{thread.lastActivity}
{thread.githubIssue && ( )} {thread.pullRequest && ( )}
); }