'use client'
import { useEffect, useState } from 'react'
import { useStore } from '@/lib/store'
import { api } from '@/lib/api'
import { CommandCenterLayout } from '@/components/CommandCenterLayout'
import { Toast, useToast } from '@/components/Toast'
import {
Plus,
Trash2,
RefreshCw,
Loader2,
Bell,
BellOff,
History,
ExternalLink,
MoreVertical,
Search,
Filter,
ArrowUpRight,
} from 'lucide-react'
import clsx from 'clsx'
import Link from 'next/link'
interface DomainHistory {
id: number
status: string
is_available: boolean
checked_at: string
}
// Status indicator component with traffic light system
function StatusIndicator({ domain }: { domain: any }) {
// Determine status based on domain data
let status: 'available' | 'watching' | 'stable' = 'stable'
let label = 'Stable'
let description = 'Domain is registered and active'
if (domain.is_available) {
status = 'available'
label = 'Available'
description = 'Domain is available for registration!'
} else if (domain.status === 'checking' || domain.status === 'pending') {
status = 'watching'
label = 'Watching'
description = 'Monitoring for changes'
}
const colors = {
available: 'bg-accent text-accent',
watching: 'bg-amber-400 text-amber-400',
stable: 'bg-foreground-muted text-foreground-muted',
}
return (
{status === 'available' && (
)}
)
}
export default function WatchlistPage() {
const { domains, addDomain, deleteDomain, refreshDomain, subscription } = useStore()
const { toast, showToast, hideToast } = useToast()
const [newDomain, setNewDomain] = useState('')
const [adding, setAdding] = useState(false)
const [refreshingId, setRefreshingId] = useState(null)
const [deletingId, setDeletingId] = useState(null)
const [selectedDomainId, setSelectedDomainId] = useState(null)
const [domainHistory, setDomainHistory] = useState(null)
const [loadingHistory, setLoadingHistory] = useState(false)
const [togglingNotifyId, setTogglingNotifyId] = useState(null)
const [filterStatus, setFilterStatus] = useState<'all' | 'available' | 'watching'>('all')
const [searchQuery, setSearchQuery] = useState('')
// Filter domains
const filteredDomains = domains?.filter(domain => {
// Search filter
if (searchQuery && !domain.name.toLowerCase().includes(searchQuery.toLowerCase())) {
return false
}
// Status filter
if (filterStatus === 'available' && !domain.is_available) return false
if (filterStatus === 'watching' && domain.is_available) return false
return true
}) || []
// Stats
const availableCount = domains?.filter(d => d.is_available).length || 0
const watchingCount = domains?.filter(d => !d.is_available).length || 0
const handleAddDomain = async (e: React.FormEvent) => {
e.preventDefault()
if (!newDomain.trim()) return
setAdding(true)
try {
await addDomain(newDomain.trim())
setNewDomain('')
showToast(`Added ${newDomain.trim()} to watchlist`, 'success')
} catch (err: any) {
showToast(err.message || 'Failed to add domain', 'error')
} finally {
setAdding(false)
}
}
const handleRefresh = async (id: number) => {
setRefreshingId(id)
try {
await refreshDomain(id)
showToast('Domain status refreshed', 'success')
} catch (err: any) {
showToast(err.message || 'Failed to refresh', 'error')
} finally {
setRefreshingId(null)
}
}
const handleDelete = async (id: number, name: string) => {
if (!confirm(`Remove ${name} from your watchlist?`)) return
setDeletingId(id)
try {
await deleteDomain(id)
showToast(`Removed ${name} from watchlist`, 'success')
} catch (err: any) {
showToast(err.message || 'Failed to remove', 'error')
} finally {
setDeletingId(null)
}
}
const handleToggleNotify = async (id: number, currentState: boolean) => {
setTogglingNotifyId(id)
try {
await api.updateDomainNotify(id, !currentState)
showToast(
!currentState ? 'Notifications enabled' : 'Notifications disabled',
'success'
)
} catch (err: any) {
showToast(err.message || 'Failed to update', 'error')
} finally {
setTogglingNotifyId(null)
}
}
const loadHistory = async (domainId: number) => {
if (selectedDomainId === domainId) {
setSelectedDomainId(null)
setDomainHistory(null)
return
}
setSelectedDomainId(domainId)
setLoadingHistory(true)
try {
const history = await api.getDomainHistory(domainId)
setDomainHistory(history)
} catch (err) {
setDomainHistory([])
} finally {
setLoadingHistory(false)
}
}
const domainLimit = subscription?.domain_limit || 5
const domainsUsed = domains?.length || 0
const canAddMore = domainsUsed < domainLimit
return (
{toast && }
{/* Stats Cards */}
Total Watched
{domainsUsed}
Available
{availableCount}
Limit
{domainLimit === -1 ? '∞' : domainLimit}
{/* Add Domain Form */}
{!canAddMore && (
You've reached your domain limit. Upgrade to track more.
Upgrade
)}
{/* Filters */}
{/* Domain List */}
{filteredDomains.length === 0 ? (
{domainsUsed === 0 ? (
<>
Your watchlist is empty
Add a domain above to start tracking
>
) : (
<>
No domains match your filters
>
)}
) : (
filteredDomains.map((domain) => (
{/* Domain Name + Status */}
{domain.name}
{domain.is_available && (
GRAB IT!
)}
{/* Actions */}
{/* Notify Toggle */}
{/* History */}
{/* Refresh */}
{/* Delete */}
{/* External Link (if available) */}
{domain.is_available && (
Register
)}
{/* History Panel */}
{selectedDomainId === domain.id && (
Status History
{loadingHistory ? (
Acquiring targets...
) : domainHistory && domainHistory.length > 0 ? (
{domainHistory.slice(0, 5).map((entry) => (
{new Date(entry.checked_at).toLocaleDateString()} at{' '}
{new Date(entry.checked_at).toLocaleTimeString()}
{entry.is_available ? 'Available' : 'Registered'}
))}
) : (
No history available yet
)}
)}
))
)}
)
}