'use client' import { useEffect, useState } from 'react' import { useStore } from '@/lib/store' import { api } from '@/lib/api' import { CommandCenterLayout } from '@/components/CommandCenterLayout' import { PageContainer, StatCard, Badge } from '@/components/PremiumTable' import { Plus, Bell, Target, Zap, Loader2, Trash2, Edit2, CheckCircle, AlertCircle, X, Play, Pause, Mail, Smartphone, Settings, TestTube, ChevronDown, ChevronUp, } from 'lucide-react' import clsx from 'clsx' interface SniperAlert { id: number name: string description: string | null tlds: string | null keywords: string | null exclude_keywords: string | null max_length: number | null min_length: number | null max_price: number | null min_price: number | null max_bids: number | null ending_within_hours: number | null platforms: string | null no_numbers: boolean no_hyphens: boolean exclude_chars: string | null notify_email: boolean notify_sms: boolean is_active: boolean matches_count: number notifications_sent: number last_matched_at: string | null created_at: string } interface TestResult { alert_name: string auctions_checked: number matches_found: number matches: Array<{ domain: string platform: string current_bid: number num_bids: number end_time: string }> message: string } export default function SniperAlertsPage() { const { subscription } = useStore() const [alerts, setAlerts] = useState([]) const [loading, setLoading] = useState(true) const [showCreateModal, setShowCreateModal] = useState(false) const [creating, setCreating] = useState(false) const [testing, setTesting] = useState(null) const [testResult, setTestResult] = useState(null) const [expandedAlert, setExpandedAlert] = useState(null) const [error, setError] = useState(null) const [success, setSuccess] = useState(null) // Create form const [newAlert, setNewAlert] = useState({ name: '', description: '', tlds: '', keywords: '', exclude_keywords: '', max_length: '', min_length: '', max_price: '', min_price: '', max_bids: '', no_numbers: false, no_hyphens: false, exclude_chars: '', notify_email: true, }) useEffect(() => { loadAlerts() }, []) const loadAlerts = async () => { setLoading(true) try { const data = await api.request('/sniper-alerts') setAlerts(data) } catch (err: any) { setError(err.message) } finally { setLoading(false) } } const handleCreate = async (e: React.FormEvent) => { e.preventDefault() setCreating(true) setError(null) try { await api.request('/sniper-alerts', { method: 'POST', body: JSON.stringify({ name: newAlert.name, description: newAlert.description || null, tlds: newAlert.tlds || null, keywords: newAlert.keywords || null, exclude_keywords: newAlert.exclude_keywords || null, max_length: newAlert.max_length ? parseInt(newAlert.max_length) : null, min_length: newAlert.min_length ? parseInt(newAlert.min_length) : null, max_price: newAlert.max_price ? parseFloat(newAlert.max_price) : null, min_price: newAlert.min_price ? parseFloat(newAlert.min_price) : null, max_bids: newAlert.max_bids ? parseInt(newAlert.max_bids) : null, no_numbers: newAlert.no_numbers, no_hyphens: newAlert.no_hyphens, exclude_chars: newAlert.exclude_chars || null, notify_email: newAlert.notify_email, }), }) setSuccess('Sniper Alert created!') setShowCreateModal(false) setNewAlert({ name: '', description: '', tlds: '', keywords: '', exclude_keywords: '', max_length: '', min_length: '', max_price: '', min_price: '', max_bids: '', no_numbers: false, no_hyphens: false, exclude_chars: '', notify_email: true, }) loadAlerts() } catch (err: any) { setError(err.message) } finally { setCreating(false) } } const handleToggle = async (alert: SniperAlert) => { try { await api.request(`/sniper-alerts/${alert.id}`, { method: 'PUT', body: JSON.stringify({ is_active: !alert.is_active }), }) loadAlerts() } catch (err: any) { setError(err.message) } } const handleDelete = async (alert: SniperAlert) => { if (!confirm(`Delete alert "${alert.name}"?`)) return try { await api.request(`/sniper-alerts/${alert.id}`, { method: 'DELETE' }) setSuccess('Alert deleted') loadAlerts() } catch (err: any) { setError(err.message) } } const handleTest = async (alert: SniperAlert) => { setTesting(alert.id) setTestResult(null) try { const result = await api.request(`/sniper-alerts/${alert.id}/test`, { method: 'POST', }) setTestResult(result) setExpandedAlert(alert.id) } catch (err: any) { setError(err.message) } finally { setTesting(null) } } const tier = subscription?.tier || 'scout' const limits = { scout: 2, trader: 10, tycoon: 50 } const maxAlerts = limits[tier as keyof typeof limits] || 2 return ( setShowCreateModal(true)} disabled={alerts.length >= maxAlerts} className="flex items-center gap-2 px-4 py-2 bg-accent text-background text-sm font-medium rounded-lg hover:bg-accent-hover transition-all disabled:opacity-50" > New Alert } > {/* Messages */} {error && (

{error}

)} {success && (

{success}

)} {/* Stats */}
a.is_active).length} icon={Bell} /> sum + a.matches_count, 0)} icon={Target} /> sum + a.notifications_sent, 0)} icon={Zap} />
{/* Alerts List */} {loading ? (
) : alerts.length === 0 ? (

No Sniper Alerts

Create alerts to get notified when domains matching your criteria appear in auctions.

) : (
{alerts.map((alert) => (
{/* Header */}

{alert.name}

{alert.is_active ? 'Active' : 'Paused'}
{alert.description && (

{alert.description}

)}
{/* Stats */}

{alert.matches_count}

Matches

{alert.notifications_sent}

Notified

{/* Actions */}
{/* Filter Summary */}
{alert.tlds && ( TLDs: {alert.tlds} )} {alert.max_length && ( Max {alert.max_length} chars )} {alert.max_price && ( Max ${alert.max_price} )} {alert.no_numbers && ( No numbers )} {alert.no_hyphens && ( No hyphens )} {alert.notify_email && ( Email )}
{/* Test Results */} {expandedAlert === alert.id && testResult && testResult.alert_name === alert.name && (

Test Results

Checked {testResult.auctions_checked} auctions

{testResult.matches_found === 0 ? (

{testResult.message}

) : (

Found {testResult.matches_found} matching domains!

{testResult.matches.map((match, idx) => (
{match.domain} ${match.current_bid}
))}
)}
)}
))}
)}
{/* Create Modal */} {showCreateModal && (

Create Sniper Alert

Get notified when domains matching your criteria appear in auctions.

setNewAlert({ ...newAlert, name: e.target.value })} placeholder="4-letter .com without numbers" className="w-full px-4 py-3 bg-background border border-border rounded-xl text-foreground placeholder:text-foreground-subtle focus:outline-none focus:border-accent" />
setNewAlert({ ...newAlert, description: e.target.value })} placeholder="Optional description" className="w-full px-4 py-3 bg-background border border-border rounded-xl text-foreground placeholder:text-foreground-subtle focus:outline-none focus:border-accent" />
setNewAlert({ ...newAlert, tlds: e.target.value })} placeholder="com,io,ai" className="w-full px-4 py-3 bg-background border border-border rounded-xl text-foreground placeholder:text-foreground-subtle focus:outline-none focus:border-accent" />
setNewAlert({ ...newAlert, keywords: e.target.value })} placeholder="ai,tech,crypto" className="w-full px-4 py-3 bg-background border border-border rounded-xl text-foreground placeholder:text-foreground-subtle focus:outline-none focus:border-accent" />
setNewAlert({ ...newAlert, min_length: e.target.value })} placeholder="3" className="w-full px-4 py-3 bg-background border border-border rounded-xl text-foreground placeholder:text-foreground-subtle focus:outline-none focus:border-accent" />
setNewAlert({ ...newAlert, max_length: e.target.value })} placeholder="6" className="w-full px-4 py-3 bg-background border border-border rounded-xl text-foreground placeholder:text-foreground-subtle focus:outline-none focus:border-accent" />
setNewAlert({ ...newAlert, max_price: e.target.value })} placeholder="500" className="w-full px-4 py-3 bg-background border border-border rounded-xl text-foreground placeholder:text-foreground-subtle focus:outline-none focus:border-accent" />
setNewAlert({ ...newAlert, max_bids: e.target.value })} placeholder="5" className="w-full px-4 py-3 bg-background border border-border rounded-xl text-foreground placeholder:text-foreground-subtle focus:outline-none focus:border-accent" />
setNewAlert({ ...newAlert, exclude_chars: e.target.value })} placeholder="q,x,z" className="w-full px-4 py-3 bg-background border border-border rounded-xl text-foreground placeholder:text-foreground-subtle focus:outline-none focus:border-accent" />
)}
) }