'use client' import { useCallback, useEffect, useMemo, useState } from 'react' import clsx from 'clsx' import { ExternalLink, Loader2, RefreshCw, Shield, Eye } from 'lucide-react' import { api } from '@/lib/api' import { useAnalyzePanelStore } from '@/lib/analyze-store' import { useStore } from '@/lib/store' function calcTimeRemaining(endTimeIso: string): string { const end = new Date(endTimeIso).getTime() const now = Date.now() const diff = end - now if (diff <= 0) return 'Ended' const seconds = Math.floor(diff / 1000) const days = Math.floor(seconds / 86400) const hours = Math.floor((seconds % 86400) / 3600) const mins = Math.floor((seconds % 3600) / 60) if (days > 0) return `${days}d ${hours}h` if (hours > 0) return `${hours}h ${mins}m` if (mins > 0) return `${mins}m` return '< 1m' } export function SniperTab({ showToast }: { showToast: (message: string, type?: any) => void }) { const openAnalyze = useAnalyzePanelStore((s) => s.open) const addDomain = useStore((s) => s.addDomain) const [loading, setLoading] = useState(true) const [refreshing, setRefreshing] = useState(false) const [error, setError] = useState(null) const [items, setItems] = useState< Array<{ domain: string platform: string auction_url: string current_bid: number currency: string end_time: string age_years: number | null backlinks: number | null pounce_score: number | null }> >([]) const load = useCallback(async () => { setError(null) const res = await api.getHuntBargainBin(200) setItems(res.items || []) }, []) useEffect(() => { let cancelled = false const run = async () => { setLoading(true) try { await load() } catch (e) { if (!cancelled) setError(e instanceof Error ? e.message : String(e)) } finally { if (!cancelled) setLoading(false) } } run() return () => { cancelled = true } }, [load]) const handleRefresh = useCallback(async () => { setRefreshing(true) try { await load() } catch (e) { setError(e instanceof Error ? e.message : String(e)) } finally { setRefreshing(false) } }, [load]) const rows = useMemo(() => items.slice(0, 150), [items]) const [tracking, setTracking] = useState(null) const track = useCallback( async (domain: string) => { if (tracking) return setTracking(domain) try { await addDomain(domain) showToast(`Tracked ${domain}`, 'success') } catch (e) { showToast(e instanceof Error ? e.message : 'Failed to track domain', 'error') } finally { setTracking(null) } }, [addDomain, showToast, tracking] ) if (loading) { return (
) } return (
Closeout Sniper
Bargain Bin
Only real scraped data: price < $10, age ≥ 5y, backlinks > 0.
{error ? (
{error}
) : rows.length === 0 ? (
No sniper items right now.
) : (
Domain
Age
Backlinks
Price
Time
Action
{rows.map((r) => (
{r.age_years !== null ? `${r.age_years}y` : '—'}
{r.backlinks !== null ? String(r.backlinks) : '—'}
${Math.round(r.current_bid)}
{calcTimeRemaining(r.end_time)}
Buy
))}
)}
) }