'use client'; import { useState, useCallback, useEffect } from 'react'; export type Sport = 'NBA' | 'MLB'; export interface OddsLine { player: string; stat_type: string; line: number; direction: string; book: string; } interface SimplifiedSelectorProps { onScan: (leg: { player: string; stat_type: string; line: number; direction: string; sport: Sport }) => void; scanning?: boolean; oddsApiUrl?: string; nbaServiceUrl?: string; } const NBA_STATS = ['points', 'rebounds', 'assists', 'threes', 'blocks', 'steals', 'pra', 'turnovers']; const MLB_STATS = [ 'strikeouts', 'hits_allowed', 'earned_runs', 'innings_pitched', 'walks_allowed', 'hits', 'total_bases', 'rbi', 'runs', 'stolen_bases', 'home_runs', 'walks', 'singles', 'doubles', ]; export default function SimplifiedSelector({ onScan, scanning = false, oddsApiUrl, nbaServiceUrl, }: SimplifiedSelectorProps) { const [sport, setSport] = useState('NBA'); const [playerQuery, setPlayerQuery] = useState(''); const [selectedPlayer, setSelectedPlayer] = useState(''); const [statType, setStatType] = useState(''); const [line, setLine] = useState(''); const [direction, setDirection] = useState<'over' | 'under'>('over'); const [suggestions, setSuggestions] = useState([]); const [playerOdds, setPlayerOdds] = useState([]); const [showSuggestions, setShowSuggestions] = useState(false); const stats = sport === 'NBA' ? NBA_STATS : MLB_STATS; const apiBase = oddsApiUrl || process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000'; const nbaBase = nbaServiceUrl || process.env.NEXT_PUBLIC_NBA_SERVICE_URL || 'http://localhost:8000'; // Reset when sport changes useEffect(() => { setPlayerQuery(''); setSelectedPlayer(''); setStatType(''); setLine(''); setPlayerOdds([]); setSuggestions([]); }, [sport]); // Fetch player suggestions const searchPlayer = useCallback( async (name: string) => { if (name.length < 2) { setSuggestions([]); return; } try { const res = await fetch(`${nbaBase}/players/search?name=${encodeURIComponent(name)}`); const data = await res.json(); setSuggestions((data.results || []).map((r: any) => r.full_name).slice(0, 5)); setShowSuggestions(true); } catch { setSuggestions([]); } }, [nbaBase], ); // Fetch odds for selected player to pre-fill lines const fetchPlayerOdds = useCallback( async (playerName: string, selectedSport: Sport) => { try { const sportKey = selectedSport.toLowerCase(); const res = await fetch(`${apiBase}/api/odds/${sportKey}`); if (!res.ok) return; const data = await res.json(); const props: OddsLine[] = (data.props || []).filter( (p: OddsLine) => p.player?.toLowerCase() === playerName.toLowerCase(), ); setPlayerOdds(props); } catch { setPlayerOdds([]); } }, [apiBase], ); // When player is selected, fetch their odds const selectPlayer = (name: string) => { setSelectedPlayer(name); setPlayerQuery(name); setShowSuggestions(false); setSuggestions([]); setStatType(''); setLine(''); fetchPlayerOdds(name, sport); }; // When stat changes, pre-fill line from odds useEffect(() => { if (!selectedPlayer || !statType) return; const match = playerOdds.find((o) => o.stat_type === statType); if (match) { setLine(match.line); setDirection((match.direction as 'over' | 'under') || 'over'); } else { setLine(''); } }, [statType, selectedPlayer, playerOdds]); // Available stats for this player based on odds data const availableStats = playerOdds.length > 0 ? stats.filter((s) => playerOdds.some((o) => o.stat_type === s)) : stats; const canScan = selectedPlayer && statType && line !== ''; const handleScan = () => { if (!canScan || scanning) return; onScan({ player: selectedPlayer, stat_type: statType, line: Number(line), direction, sport, }); }; return (
{/* Sport Toggle */}
{(['NBA', 'MLB'] as Sport[]).map((s) => ( ))}
{/* Player Search */}
{ setPlayerQuery(e.target.value); setSelectedPlayer(''); searchPlayer(e.target.value); }} onBlur={() => setTimeout(() => setShowSuggestions(false), 200)} onFocus={() => suggestions.length > 0 && setShowSuggestions(true)} className="w-full px-4 py-3 rounded-xl bg-[var(--bg)] border border-[var(--border)] text-white placeholder:text-[var(--text-muted)] focus:outline-none focus:border-[var(--cyan)] text-sm" data-testid="player-search" /> {showSuggestions && suggestions.length > 0 && (
{suggestions.map((name) => ( ))}
)}
{/* Stat Dropdown */} {selectedPlayer && ( )} {/* Line + Direction */} {selectedPlayer && statType && (
setLine(e.target.value ? Number(e.target.value) : '')} className="flex-1 px-4 py-3 rounded-xl bg-[var(--bg)] border border-[var(--border)] text-white placeholder:text-[var(--text-muted)] text-sm" data-testid="line-input" />
)} {/* Scan Button */}
); }