"use client"; import { useState, Suspense, useMemo } from "react"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Input } from "@/components/ui/input"; import { ArrowLeft, Search, Filter } from "lucide-react"; import { useRouter } from "next/navigation"; import { ThreadMetadata } from "@/components/v2/types"; import { useThreadsSWR } from "@/hooks/useThreadsSWR"; import { ThreadCard, ThreadCardLoading } from "@/components/v2/thread-card"; import { ThemeToggle } from "@/components/theme-toggle"; import { InstallationSelector } from "@/components/github/installation-selector"; import { GitHubAppProvider, useGitHubAppProvider } from "@/providers/GitHubApp"; import { MANAGER_GRAPH_ID } from "@open-swe/shared/constants"; import { useThreadsStatus } from "@/hooks/useThreadsStatus"; import { cn } from "@/lib/utils"; import { threadsToMetadata } from "@/lib/thread-utils"; import { UserPopover } from "@/components/user-popover"; import { OpenSWELogoSVG } from "@/components/icons/openswe"; type FilterStatus = | "all" | "running" | "completed" | "failed" | "pending" | "idle" | "paused" | "error"; function AllThreadsPageContent() { const router = useRouter(); const { currentInstallation, installationsLoading } = useGitHubAppProvider(); const { threads, isLoading: threadsLoading } = useThreadsSWR({ assistantId: MANAGER_GRAPH_ID, currentInstallation, }); const [searchQuery, setSearchQuery] = useState(""); const [statusFilter, setStatusFilter] = useState("all"); const threadsMetadata = useMemo(() => threadsToMetadata(threads), [threads]); const threadIds = threadsMetadata.map((thread) => thread.id); const { statusMap, taskPlanMap, statusCounts, isLoading: statusLoading, } = useThreadsStatus(threadIds, threads); const filteredThreads = useMemo(() => { return threadsMetadata.filter((thread: ThreadMetadata) => { const matchesSearch = thread.title.toLowerCase().includes(searchQuery.toLowerCase()) || thread.repository.toLowerCase().includes(searchQuery.toLowerCase()); const matchesStatus = statusFilter === "all" || statusMap[thread.id] === statusFilter; return matchesSearch && matchesStatus; }); }, [threadsMetadata, searchQuery, statusFilter, statusMap]); const groupedThreads = useMemo(() => { return { running: filteredThreads.filter( (thread: ThreadMetadata) => statusMap[thread.id] === "running", ), completed: filteredThreads.filter( (thread: ThreadMetadata) => statusMap[thread.id] === "completed", ), failed: filteredThreads.filter( (thread: ThreadMetadata) => statusMap[thread.id] === "failed", ), pending: filteredThreads.filter( (thread: ThreadMetadata) => statusMap[thread.id] === "pending", ), idle: filteredThreads.filter( (thread: ThreadMetadata) => statusMap[thread.id] === "idle", ), paused: filteredThreads.filter( (thread: ThreadMetadata) => statusMap[thread.id] === "paused", ), error: filteredThreads.filter( (thread: ThreadMetadata) => statusMap[thread.id] === "error", ), }; }, [filteredThreads, statusMap]); // Show loading state if threads/status/installation requests are loading, and there are no // threads to display (conditional of the status filter) const showThreadsLoading = (threadsLoading || statusLoading || installationsLoading) && (statusFilter === "all" ? Object.values(groupedThreads).flat().length === 0 : filteredThreads.length === 0); return (
{/* Header */}
{filteredThreads.length} threads
{/* Search and Filters */}
setSearchQuery(e.target.value)} className="border-border bg-background text-foreground placeholder:text-muted-foreground pl-10" />
Filter: {( [ "all", "running", "completed", "failed", "pending", "idle", "paused", "error", ] as FilterStatus[] ).map((status) => ( ))}
{/* Content */}
{statusFilter === "all" ? (
{Object.entries(groupedThreads).map(([status, threads]) => { if (threads.length === 0) return null; return (

{status} Threads

{threads.length}
{threads.map((thread) => ( ))}
); })}
) : (
{filteredThreads.map((thread) => ( ))}
)} {filteredThreads.length === 0 && !threadsLoading && !statusLoading && (
No threads found
{!threads || threads.length === 0 ? "No threads have been created yet" : searchQuery ? "Try adjusting your search query" : "No threads match the selected filter"}
)} {showThreadsLoading && (

Loading threads...

{Array.from({ length: 9 }).map((_, index) => ( ))}
)}
); } export default function AllThreadsPage() { return ( Loading...}> ); }