import { useState, useEffect, useRef } from 'react' import { useNavigate, Link } from 'react-router-dom' import { Search, Clock, ArrowRight, Play, Loader2 } from 'lucide-react' import { treesApi } from '@/api/trees' import { sessionsApi } from '@/api/sessions' import type { TreeListItem } from '@/types' import type { Session } from '@/types/session' import { cn } from '@/lib/utils' function timeAgo(dateStr: string): string { const now = Date.now() const then = new Date(dateStr).getTime() const diffMs = now - then const minutes = Math.floor(diffMs / 60000) if (minutes < 1) return 'just now' if (minutes < 60) return `${minutes}m ago` const hours = Math.floor(minutes / 60) if (hours < 24) return `${hours}h ago` const days = Math.floor(hours / 24) return `${days}d ago` } export function QuickStartPage() { const navigate = useNavigate() const [query, setQuery] = useState('') const [searchResults, setSearchResults] = useState([]) const [isSearching, setIsSearching] = useState(false) const [showResults, setShowResults] = useState(false) const [activeSessions, setActiveSessions] = useState([]) const [recentTrees, setRecentTrees] = useState<{ tree_id: string; name: string; lastUsed: string }[]>([]) const [isLoading, setIsLoading] = useState(true) const searchRef = useRef(null) const debounceRef = useRef | null>(null) // Load sessions on mount useEffect(() => { async function loadData() { try { const [active, recent] = await Promise.all([ sessionsApi.list({ completed: false, size: 5 }), sessionsApi.list({ size: 10 }), ]) setActiveSessions(active.slice(0, 3)) // Deduplicate recent sessions by tree_id, max 5 const seen = new Set() const deduped: { tree_id: string; name: string; lastUsed: string }[] = [] for (const s of recent) { if (!seen.has(s.tree_id) && deduped.length < 5) { seen.add(s.tree_id) deduped.push({ tree_id: s.tree_id, name: s.tree_snapshot?.name || 'Unnamed Tree', lastUsed: s.started_at, }) } } setRecentTrees(deduped) } catch (err) { console.error('Failed to load sessions:', err) } finally { setIsLoading(false) } } loadData() }, []) // Debounced search useEffect(() => { if (debounceRef.current) clearTimeout(debounceRef.current) if (query.length < 2) { setSearchResults([]) setShowResults(false) setIsSearching(false) return } setIsSearching(true) setShowResults(true) debounceRef.current = setTimeout(async () => { try { const results = await treesApi.search(query, 8) setSearchResults(results) } catch (err) { console.error('Search failed:', err) setSearchResults([]) } finally { setIsSearching(false) } }, 300) return () => { if (debounceRef.current) clearTimeout(debounceRef.current) } }, [query]) // Close dropdown on outside click useEffect(() => { function handleClick(e: MouseEvent) { if (searchRef.current && !searchRef.current.contains(e.target as Node)) { setShowResults(false) } } document.addEventListener('mousedown', handleClick) return () => document.removeEventListener('mousedown', handleClick) }, []) return (
{/* Hero Section */}

What are you troubleshooting?

setQuery(e.target.value)} onFocus={() => query.length >= 2 && setShowResults(true)} placeholder="Paste ticket subject or search for a tree..." className={cn( 'w-full rounded-lg border border-border bg-card py-3 pl-12 pr-4 text-lg', 'text-foreground placeholder:text-muted-foreground', 'focus:outline-none focus:ring-2 focus:ring-primary/50' )} />
{/* Search Results Dropdown */} {showResults && (
{isSearching ? (
) : searchResults.length === 0 ? (
No results found
) : (
    {searchResults.map((tree) => (
  • ))}
)}
)}
{/* Continue Session Section */} {activeSessions.length > 0 && (

Continue Session

{activeSessions.map((session) => ( ))}
)} {/* Recent Trees Section */} {!isLoading && recentTrees.length > 0 && (

Recent Trees

{recentTrees.map((tree) => ( ))}
)} {/* Footer */}
Browse All Trees
) } export default QuickStartPage