'use client' import { useEffect, useState } from 'react' import { useRouter } from 'next/navigation' import { Header } from '@/components/Header' import { Footer } from '@/components/Footer' import { useStore } from '@/lib/store' import { api } from '@/lib/api' import { Users, Database, Mail, TrendingUp, Shield, RefreshCw, Search, Crown, Zap, Activity, Globe, Bell, AlertCircle, Check, Loader2, Trash2, Eye, Gavel, CheckCircle, XCircle, Download, Send, Clock, History, X, ChevronDown, BookOpen, Plus, Edit2, Trash2 as TrashIcon, ExternalLink, } from 'lucide-react' import clsx from 'clsx' type TabType = 'overview' | 'users' | 'alerts' | 'newsletter' | 'tld' | 'auctions' | 'blog' | 'system' | 'activity' interface AdminStats { users: { total: number active: number verified: number new_this_week: number } subscriptions: Record domains: { watched: number portfolio: number } tld_data: { unique_tlds: number price_records: number } newsletter_subscribers: number auctions: number price_alerts: number } interface AdminUser { id: number email: string name: string | null is_active: boolean is_verified: boolean is_admin: boolean created_at: string last_login: string | null domain_count: number subscription: { tier: string tier_name: string status: string | null domain_limit: number } } interface NewsletterSubscriber { id: number email: string is_active: boolean subscribed_at: string unsubscribed_at: string | null } interface PriceAlert { id: number tld: string target_price: number | null alert_type: string created_at: string user: { id: number; email: string; name: string | null } } interface ActivityLogEntry { id: number action: string details: string created_at: string admin: { id: number; email: string; name: string | null } } interface BlogPostAdmin { id: number title: string slug: string excerpt: string | null cover_image: string | null category: string | null tags: string[] is_published: boolean published_at: string | null created_at: string view_count: number author: { id: number; name: string | null } } interface SchedulerJob { id: string name: string next_run: string | null trigger: string } export default function AdminPage() { const router = useRouter() const { user, isAuthenticated, isLoading, checkAuth } = useStore() const [activeTab, setActiveTab] = useState('overview') const [stats, setStats] = useState(null) const [users, setUsers] = useState([]) const [usersTotal, setUsersTotal] = useState(0) const [selectedUsers, setSelectedUsers] = useState([]) const [newsletter, setNewsletter] = useState([]) const [newsletterTotal, setNewsletterTotal] = useState(0) const [priceAlerts, setPriceAlerts] = useState([]) const [priceAlertsTotal, setPriceAlertsTotal] = useState(0) const [activityLog, setActivityLog] = useState([]) const [activityLogTotal, setActivityLogTotal] = useState(0) const [blogPosts, setBlogPosts] = useState([]) const [blogPostsTotal, setBlogPostsTotal] = useState(0) const [showBlogEditor, setShowBlogEditor] = useState(false) const [editingPost, setEditingPost] = useState(null) const [blogForm, setBlogForm] = useState({ title: '', content: '', excerpt: '', category: '', tags: '', cover_image: '', is_published: false, }) const [schedulerStatus, setSchedulerStatus] = useState<{ scheduler_running: boolean jobs: SchedulerJob[] last_runs: { tld_scrape: string | null; auction_scrape: string | null; domain_check: string | null } } | null>(null) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) const [success, setSuccess] = useState(null) const [searchQuery, setSearchQuery] = useState('') const [scraping, setScraping] = useState(false) const [auctionScraping, setAuctionScraping] = useState(false) const [domainChecking, setDomainChecking] = useState(false) const [sendingEmail, setSendingEmail] = useState(false) const [auctionStatus, setAuctionStatus] = useState(null) const [systemHealth, setSystemHealth] = useState(null) const [selectedUser, setSelectedUser] = useState(null) const [bulkTier, setBulkTier] = useState('trader') useEffect(() => { checkAuth() }, [checkAuth]) useEffect(() => { if (!isLoading && !isAuthenticated) { router.push('/login') } }, [isLoading, isAuthenticated, router]) useEffect(() => { if (isAuthenticated) { loadAdminData() } }, [isAuthenticated, activeTab]) const loadAdminData = async () => { setLoading(true) setError(null) try { if (activeTab === 'overview') { const statsData = await api.getAdminStats() setStats(statsData) } else if (activeTab === 'users') { const usersData = await api.getAdminUsers(50, 0, searchQuery || undefined) setUsers(usersData.users) setUsersTotal(usersData.total) } else if (activeTab === 'alerts') { const alertsData = await api.getAdminPriceAlerts(100, 0) setPriceAlerts(alertsData.alerts) setPriceAlertsTotal(alertsData.total) } else if (activeTab === 'newsletter') { const nlData = await api.getAdminNewsletter(100, 0) setNewsletter(nlData.subscribers) setNewsletterTotal(nlData.total) } else if (activeTab === 'auctions') { try { const statusData = await api.getAuctionScrapeStatus() setAuctionStatus(statusData) } catch { setAuctionStatus(null) } } else if (activeTab === 'system') { try { const [healthData, schedulerData] = await Promise.all([ api.getSystemHealth(), api.getSchedulerStatus(), ]) setSystemHealth(healthData) setSchedulerStatus(schedulerData) } catch { setSystemHealth(null) setSchedulerStatus(null) } } else if (activeTab === 'activity') { try { const logData = await api.getActivityLog(50, 0) setActivityLog(logData.logs) setActivityLogTotal(logData.total) } catch { setActivityLog([]) setActivityLogTotal(0) } } else if (activeTab === 'blog') { try { const blogData = await api.getAdminBlogPosts(50, 0) setBlogPosts(blogData.posts) setBlogPostsTotal(blogData.total) } catch { setBlogPosts([]) setBlogPostsTotal(0) } } } catch (err) { if (err instanceof Error && err.message.includes('403')) { setError('Admin privileges required. You are not authorized to access this page.') } else { setError(err instanceof Error ? err.message : 'Failed to load admin data') } } finally { setLoading(false) } } const handleTriggerScrape = async () => { setScraping(true) setError(null) try { const result = await api.triggerTldScrape() setSuccess(`Scrape completed: ${result.tlds_scraped} TLDs, ${result.prices_saved} prices saved`) } catch (err) { setError(err instanceof Error ? err.message : 'Scrape failed') } finally { setScraping(false) } } const handleTriggerAuctionScrape = async () => { setAuctionScraping(true) setError(null) try { const result = await api.triggerAuctionScrape() setSuccess(`Auction scrape completed: ${result.total_auctions || 0} auctions found`) loadAdminData() } catch (err) { setError(err instanceof Error ? err.message : 'Auction scrape failed') } finally { setAuctionScraping(false) } } const handleTriggerDomainChecks = async () => { setDomainChecking(true) setError(null) try { const result = await api.triggerDomainChecks() setSuccess(`Domain checks started: ${result.domains_queued} domains queued`) } catch (err) { setError(err instanceof Error ? err.message : 'Domain check failed') } finally { setDomainChecking(false) } } const handleSendTestEmail = async () => { setSendingEmail(true) setError(null) try { const result = await api.sendTestEmail() setSuccess(`Test email sent to ${result.sent_to}`) } catch (err) { setError(err instanceof Error ? err.message : 'Email test failed') } finally { setSendingEmail(false) } } const handleUpgradeUser = async (userId: number, tier: string) => { try { await api.upgradeUser(userId, tier) setSuccess(`User upgraded to ${tier}`) loadAdminData() } catch (err) { setError(err instanceof Error ? err.message : 'Upgrade failed') } } const handleToggleAdmin = async (userId: number, isAdmin: boolean) => { try { await api.updateAdminUser(userId, { is_admin: !isAdmin }) setSuccess(isAdmin ? 'Admin privileges removed' : 'Admin privileges granted') loadAdminData() } catch (err) { setError(err instanceof Error ? err.message : 'Update failed') } } const handleDeleteUser = async (userId: number, userEmail: string) => { if (!confirm(`Are you sure you want to delete user "${userEmail}" and ALL their data?\n\nThis action cannot be undone.`)) { return } try { await api.deleteAdminUser(userId) setSuccess(`User ${userEmail} and all their data have been deleted`) setSelectedUser(null) loadAdminData() } catch (err) { setError(err instanceof Error ? err.message : 'Delete failed') } } const handleBulkUpgrade = async () => { if (selectedUsers.length === 0) { setError('Please select users to upgrade') return } try { const result = await api.bulkUpgradeUsers(selectedUsers, bulkTier) setSuccess(`Upgraded ${result.total_upgraded} users to ${bulkTier}`) setSelectedUsers([]) loadAdminData() } catch (err) { setError(err instanceof Error ? err.message : 'Bulk upgrade failed') } } const handleExportUsers = async () => { try { const result = await api.exportUsersCSV() const blob = new Blob([result.csv], { type: 'text/csv' }) const url = URL.createObjectURL(blob) const a = document.createElement('a') a.href = url a.download = `users-export-${new Date().toISOString().split('T')[0]}.csv` a.click() setSuccess(`Exported ${result.count} users`) } catch (err) { setError(err instanceof Error ? err.message : 'Export failed') } } const toggleUserSelection = (userId: number) => { setSelectedUsers(prev => prev.includes(userId) ? prev.filter(id => id !== userId) : [...prev, userId] ) } const toggleSelectAll = () => { if (selectedUsers.length === users.length) { setSelectedUsers([]) } else { setSelectedUsers(users.map(u => u.id)) } } if (isLoading) { return (
) } if (error && error.includes('403')) { return (

Access Denied

You don't have admin privileges to access this page.

) } const tabs = [ { id: 'overview' as const, label: 'Overview', icon: Activity }, { id: 'users' as const, label: 'Users', icon: Users }, { id: 'alerts' as const, label: 'Price Alerts', icon: Bell }, { id: 'newsletter' as const, label: 'Newsletter', icon: Mail }, { id: 'tld' as const, label: 'TLD Data', icon: Globe }, { id: 'auctions' as const, label: 'Auctions', icon: Gavel }, { id: 'blog' as const, label: 'Blog', icon: BookOpen }, { id: 'system' as const, label: 'System', icon: Database }, { id: 'activity' as const, label: 'Activity', icon: History }, ] return (
{/* Background Effects */}
{/* Header */}
Admin Panel

Control Center.

Manage users, monitor system health, and control platform settings.

{/* Messages */} {error && !error.includes('403') && (

{error}

)} {success && (

{success}

)} {/* Tabs */}
{tabs.map((tab) => ( ))}
{loading ? (
) : ( <> {/* Overview Tab */} {activeTab === 'overview' && stats && (

Subscriptions by Tier

Scout

{stats.subscriptions.scout || 0}

Trader

{stats.subscriptions.trader || 0}

Tycoon

{stats.subscriptions.tycoon || 0}

Active Auctions

{stats.auctions.toLocaleString()}

from all platforms

Price Alerts

{stats.price_alerts.toLocaleString()}

active alerts

)} {/* Users Tab */} {activeTab === 'users' && (
setSearchQuery(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && loadAdminData()} placeholder="Search users..." className="w-full pl-11 pr-4 py-2.5 bg-background-secondary border border-border rounded-xl text-body-sm text-foreground placeholder:text-foreground-subtle focus:outline-none focus:border-accent/50" />
{selectedUsers.length > 0 && (
{selectedUsers.length} users selected
)}
{users.map((u) => ( ))}
0} onChange={toggleSelectAll} className="w-4 h-4 rounded border-border" /> User Status Tier Domains Actions
toggleUserSelection(u.id)} className="w-4 h-4 rounded border-border" />
{u.is_admin && Admin} {u.is_verified && Verified} {!u.is_active && Inactive}
{u.subscription.tier_name} {u.domain_count}

Showing {users.length} of {usersTotal} users

)} {/* Price Alerts Tab */} {activeTab === 'alerts' && (

{priceAlertsTotal} active price alerts

{priceAlerts.length === 0 ? (

No active price alerts

) : (
{priceAlerts.map((alert) => ( ))}
TLD Target Price Type User Created
.{alert.tld} {alert.target_price ? `$${alert.target_price.toFixed(2)}` : '—'} {alert.alert_type} {alert.user.email} {new Date(alert.created_at).toLocaleDateString()}
)}
)} {/* Newsletter Tab */} {activeTab === 'newsletter' && (

{newsletterTotal} total subscribers

{newsletter.map((s) => ( ))}
Email Status Subscribed
{s.email} {s.is_active ? 'Active' : 'Unsubscribed'} {new Date(s.subscribed_at).toLocaleDateString()}
)} {/* TLD Tab */} {activeTab === 'tld' && (

TLD Price Data

Unique TLDs {stats?.tld_data.unique_tlds || 0}
Price Records {stats?.tld_data.price_records?.toLocaleString() || 0}

Scrape TLD Prices

Manually trigger a TLD price scrape.

)} {/* Auctions Tab */} {activeTab === 'auctions' && (

Auction Data

Total Auctions {stats?.auctions || 0}

Scrape Auctions

Scrape from GoDaddy, Sedo, NameJet, DropCatch.

Platforms

{['GoDaddy', 'Sedo', 'NameJet', 'DropCatch'].map((platform) => (
{platform}

Active

))}
)} {/* System Tab */} {activeTab === 'system' && (

System Status

Database {systemHealth?.database === 'healthy' ? ( <>Healthy ) : ( <>{systemHealth?.database || 'Unknown'} )}
Email (SMTP) {systemHealth?.email_configured ? ( <>Configured ) : ( <>Not configured )}
Stripe Payments {systemHealth?.stripe_configured ? ( <>Configured ) : ( <>Not configured )}
Scheduler {schedulerStatus?.scheduler_running ? ( <>Running ) : ( <>Stopped )}
{/* Scheduler Jobs */} {schedulerStatus && (

Scheduled Jobs

{schedulerStatus.jobs.map((job) => (

{job.name}

{job.trigger}

Next run:

{job.next_run ? new Date(job.next_run).toLocaleString() : 'Not scheduled'}

))}

Last Runs

TLD Scrape

{schedulerStatus.last_runs.tld_scrape ? new Date(schedulerStatus.last_runs.tld_scrape).toLocaleString() : 'Never'}

Auction Scrape

{schedulerStatus.last_runs.auction_scrape ? new Date(schedulerStatus.last_runs.auction_scrape).toLocaleString() : 'Never'}

Domain Check

{schedulerStatus.last_runs.domain_check ? new Date(schedulerStatus.last_runs.domain_check).toLocaleString() : 'Never'}

)} {/* Quick Actions */}

Manual Triggers

Environment

SMTP_HOST smtp.zoho.eu
SMTP_PORT 465
DATABASE SQLite
{systemHealth?.timestamp && (
Last check: {new Date(systemHealth.timestamp).toLocaleString()}
)}
)} {/* Blog Tab */} {activeTab === 'blog' && (

{blogPostsTotal} blog posts

{blogPosts.length === 0 ? (

No blog posts yet

Create your first post to get started

) : (
{blogPosts.map((post) => ( ))}
Title Category Status Views Actions

{post.title}

{post.slug}

{post.category ? ( {post.category} ) : ( )} {post.is_published ? 'Published' : 'Draft'} {post.view_count}
{!post.is_published ? ( ) : ( )}
)}
)} {/* Activity Log Tab */} {activeTab === 'activity' && (

{activityLogTotal} log entries

{activityLog.length === 0 ? (

No activity logged yet

) : (
{activityLog.map((log) => ( ))}
Action Details Admin Time
{log.action} {log.details} {log.admin.email} {new Date(log.created_at).toLocaleString()}
)}
)} )}
{/* User Detail Modal */} {selectedUser && (

User Details

Email

{selectedUser.email}

Name

{selectedUser.name || 'Not set'}

Status

{selectedUser.is_admin && Admin} {selectedUser.is_verified && Verified} {selectedUser.is_active ? ( Active ) : ( Inactive )}

Subscription

{selectedUser.subscription.tier_name}

Domains

{selectedUser.domain_count} / {selectedUser.subscription.domain_limit}

Created

{new Date(selectedUser.created_at).toLocaleDateString()}

{selectedUser.last_login && (

Last Login

{new Date(selectedUser.last_login).toLocaleString()}

)}
)} {/* Blog Editor Modal */} {showBlogEditor && (

{editingPost ? 'Edit Post' : 'New Blog Post'}

setBlogForm({ ...blogForm, title: e.target.value })} placeholder="Enter post title..." className="w-full px-4 py-3 bg-background-secondary border border-border rounded-xl text-foreground placeholder:text-foreground-subtle focus:outline-none focus:border-accent/50" />