"use client"; import { Card } from "@/components/ui/card"; import { cn } from "@/lib/utils"; import { useState, useEffect, useRef } from "react"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; interface Run { start_article: string; destination_article: string; steps: string[]; } interface RunsListProps { runs: Run[]; onSelectRun: (runId: number) => void; selectedRunId: number | null; onTryRun?: (startArticle: string, destinationArticle: string) => void; } export default function RunsList({ runs, onSelectRun, selectedRunId, onTryRun, }: RunsListProps) { const [isPlaying, setIsPlaying] = useState(true); const [filter, setFilter] = useState(""); const timerRef = useRef(null); const listContainerRef = useRef(null); const runItemsRef = useRef>(new Map()); // Filter runs based on start and end filters const filteredRuns = runs.filter((run) => { const matches = filter === "" || run.start_article.toLowerCase().includes(filter.toLowerCase()) || run.destination_article.toLowerCase().includes(filter.toLowerCase()); return matches; }); const _onSelectRun = (runId: number) => { onSelectRun(runId); setIsPlaying(false); }; // Auto-play functionality useEffect(() => { if (isPlaying) { timerRef.current = setInterval(() => { if (filteredRuns.length === 0) return; const nextIndex = selectedRunId === null ? 0 : (selectedRunId + 1) % filteredRuns.length; const originalIndex = runs.findIndex( run => run === filteredRuns[nextIndex] ); onSelectRun(originalIndex); }, 1500); } else if (timerRef.current) { clearInterval(timerRef.current); timerRef.current = null; } return () => { if (timerRef.current) { clearInterval(timerRef.current); } }; }, [isPlaying, selectedRunId, filteredRuns, runs, onSelectRun]); // Scroll selected run into view when it changes useEffect(() => { if (selectedRunId !== null && isPlaying) { const selectedElement = runItemsRef.current.get(selectedRunId); if (selectedElement && listContainerRef.current) { selectedElement.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); } } }, [selectedRunId, isPlaying]); const togglePlayPause = () => { setIsPlaying(prev => !prev); }; return (
setFilter(e.target.value)} className="h-9" />
{filteredRuns.map((run) => { const originalIndex = runs.indexOf(run); return ( { if (el) { runItemsRef.current.set(originalIndex, el); } else { runItemsRef.current.delete(originalIndex); } }} className={cn( "p-0 cursor-pointer transition-all border overflow-hidden", selectedRunId === originalIndex ? "bg-primary/5 border-primary/50 shadow-md" : "hover:bg-muted/50 border-border" )} >
_onSelectRun(originalIndex)} >
{run.start_article} {run.destination_article}
{run.steps.length} {run.steps.length === 1 ? 'hop' : 'hops'} {selectedRunId === originalIndex && (
)}
{onTryRun && selectedRunId === originalIndex && (
)} ); })} {filteredRuns.length === 0 && (
No runs available
)}
); }