'use client'; import { useState, useEffect } from 'react'; import { useAuth } from '@/contexts/AuthContext'; import { useRouter } from 'next/navigation'; type Tab = 'overview' | 'campaigns' | 'distributions' | 'newsletters' | 'documents' | 'invoices' | 'reports'; interface Campaign { id: string; campaign_name: string; client_name: string; publication_id: string; placement_zone: string; start_date: string; end_date: string; status: string; agreed_rate: number; impressions: number; clicks: number; ctr: number; mooninvoice_invoice_id?: string; blackmagic_excluded: boolean; } interface EmailCampaign { id: string; campaign_name: string; client_name: string; publication_id: string; subject: string; send_date: string; status: string; sent_count: number; agreed_rate?: number; is_newsletter: boolean; email_lists?: { list_name: string; subscriber_count: number }; } interface Stats { activeCampaigns: number; pendingApprovals: number; endingSoon: number; emailsSentThisMonth: number; revenueThisMonth: number; recentActivity: Campaign[]; } const PUBLICATIONS = [ { id: 'avbeat', name: 'AV Beat', short: 'AVB' }, { id: 'av-beat', name: 'AV Beat', short: 'AV' }, ]; const STATUS_COLORS: Record = { pending_info: 'bg-yellow-900/50 text-yellow-300 border-yellow-700', pending_approval: 'bg-blue-900/50 text-blue-300 border-blue-700', active: 'bg-green-900/50 text-green-300 border-green-700', paused: 'bg-gray-700/50 text-gray-300 border-gray-600', ended: 'bg-gray-800/50 text-gray-400 border-gray-700', cancelled: 'bg-red-900/50 text-red-300 border-red-700', draft: 'bg-gray-700/50 text-gray-300 border-gray-600', scheduled: 'bg-purple-900/50 text-purple-300 border-purple-700', sent: 'bg-green-900/50 text-green-300 border-green-700', held: 'bg-red-900/50 text-red-300 border-red-700', }; export default function AdOpsDashboard() { const { user, loading } = useAuth(); const router = useRouter(); const [activeTab, setActiveTab] = useState('overview'); const [isAdmin, setIsAdmin] = useState(false); const [authChecked, setAuthChecked] = useState(false); const [stats, setStats] = useState(null); const [campaigns, setCampaigns] = useState([]); const [emailCampaigns, setEmailCampaigns] = useState([]); const [filterPub, setFilterPub] = useState(''); const [filterStatus, setFilterStatus] = useState(''); const [filterClient, setFilterClient] = useState(''); const [showNewCampaign, setShowNewCampaign] = useState(false); const [newCampaign, setNewCampaign] = useState({ campaign_name: '', client_name: '', publication_id: '', placement_zone: '', destination_url: '', start_date: '', end_date: '', agreed_rate: '', notes: '', }); const [saving, setSaving] = useState(false); const [inboxLogs, setInboxLogs] = useState([]); useEffect(() => { if (loading) return; if (!user) { router.replace('/'); return; } fetch('/api/adops/stats') .then(r => { if (r.status === 404) { router.replace('/'); return null; } return r.json(); }) .then(data => { if (!data) return; setIsAdmin(true); setStats(data); setAuthChecked(true); }) .catch(() => router.replace('/')); }, [user, loading, router]); useEffect(() => { if (!isAdmin) return; loadCampaigns(); loadEmailCampaigns(); }, [isAdmin, filterPub, filterStatus, filterClient]); async function loadCampaigns() { const params = new URLSearchParams(); if (filterPub) params.set('publication', filterPub); if (filterStatus) params.set('status', filterStatus); if (filterClient) params.set('client', filterClient); const res = await fetch(`/api/adops/campaigns?${params}`); if (res.ok) { const data = await res.json(); setCampaigns(data.campaigns || []); } } async function loadEmailCampaigns() { const res = await fetch('/api/adops/email-campaigns'); if (res.ok) { const data = await res.json(); setEmailCampaigns(data.campaigns || []); } } async function loadInboxLogs() { const res = await fetch('/api/adops/poll'); if (res.ok) { const data = await res.json(); setInboxLogs(data.logs || []); } } async function handleCreateCampaign() { setSaving(true); const res = await fetch('/api/adops/campaigns', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ ...newCampaign, agreed_rate: parseFloat(newCampaign.agreed_rate) || null, campaign_type: 'banner', }), }); if (res.ok) { setShowNewCampaign(false); setNewCampaign({ campaign_name: '', client_name: '', publication_id: '', placement_zone: '', destination_url: '', start_date: '', end_date: '', agreed_rate: '', notes: '' }); loadCampaigns(); } setSaving(false); } async function handleStatusChange(campaignId: string, newStatus: string) { await fetch('/api/adops/campaigns', { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id: campaignId, status: newStatus }), }); loadCampaigns(); } async function triggerPoll() { await fetch('/api/adops/poll', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: '{}' }); loadInboxLogs(); } if (loading || !authChecked) { return (
); } if (!isAdmin) return null; const tabs: { id: Tab; label: string }[] = [ { id: 'overview', label: 'Overview' }, { id: 'campaigns', label: 'Banner Campaigns' }, { id: 'distributions', label: 'Email Distributions' }, { id: 'newsletters', label: 'Newsletters' }, { id: 'documents', label: 'Documents' }, { id: 'invoices', label: 'Invoices' }, { id: 'reports', label: 'Reports' }, ]; const newsletters = emailCampaigns.filter(c => c.is_newsletter); const distributions = emailCampaigns.filter(c => !c.is_newsletter); return (
{/* Header */}

Ad Ops & Email Marketing

Ryan Salazar — Admin Only

adops@avbeat.com
{/* Tabs */}
{tabs.map(tab => ( ))}
{/* Tab Content */}
{/* OVERVIEW TAB */} {activeTab === 'overview' && (
{/* Stats Cards */}
{[ { label: 'Active Campaigns', value: stats?.activeCampaigns || 0, color: 'text-green-400' }, { label: 'Pending Approvals', value: stats?.pendingApprovals || 0, color: 'text-yellow-400' }, { label: 'Ending in 7 Days', value: stats?.endingSoon || 0, color: 'text-orange-400' }, { label: 'Emails This Month', value: stats?.emailsSentThisMonth || 0, color: 'text-blue-400' }, { label: 'Revenue This Month', value: `$${(stats?.revenueThisMonth || 0).toLocaleString()}`, color: 'text-emerald-400' }, ].map(card => (

{card.label}

{card.value}

))}
{/* Recent Activity */}

Recent Campaign Activity

{stats?.recentActivity?.length ? (
{stats.recentActivity.map(c => (

{c.campaign_name || c.client_name}

{c.publication_id} • {c.placement_zone}

{c.status.replace('_', ' ')}
))}
) : (

No recent activity

)}
{/* Inbox Log */}

Inbox Log

{inboxLogs.length ? (
{inboxLogs.slice(0, 10).map(log => (

{log.subject || '(no subject)'}

{log.from_email} • {new Date(log.received_at).toLocaleString()}

{log.request_type} {log.processing_status}
))}
) : (

No inbox logs yet. Click "Poll Inbox" to check for new emails.

)}
)} {/* BANNER CAMPAIGNS TAB */} {activeTab === 'campaigns' && (
{/* Filters + New Campaign */}
setFilterClient(e.target.value)} className="bg-gray-800 border border-gray-700 text-gray-200 text-sm rounded-md px-3 py-2 w-48" />
{/* New Campaign Form */} {showNewCampaign && (

New Banner Campaign

{[ { key: 'campaign_name', label: 'Campaign Name', type: 'text' }, { key: 'client_name', label: 'Client Name', type: 'text' }, { key: 'destination_url', label: 'Click-Through URL', type: 'url' }, { key: 'agreed_rate', label: 'Agreed Rate ($)', type: 'number' }, { key: 'start_date', label: 'Start Date', type: 'date' }, { key: 'end_date', label: 'End Date', type: 'date' }, ].map(field => (
setNewCampaign(prev => ({ ...prev, [field.key]: e.target.value }))} className="w-full bg-gray-800 border border-gray-700 text-gray-200 text-sm rounded-md px-3 py-2" />
))}
setNewCampaign(prev => ({ ...prev, placement_zone: e.target.value }))} className="w-full bg-gray-800 border border-gray-700 text-gray-200 text-sm rounded-md px-3 py-2" />
)} {/* Campaigns Table */}
{['Client', 'Publication', 'Zone', 'Dates', 'Status', 'Impressions', 'Clicks', 'CTR', 'Revenue', 'Invoice', 'Actions'].map(h => ( ))} {campaigns.length === 0 ? ( ) : campaigns.map(c => ( ))}
{h}
No campaigns found

{c.client_name}

{c.blackmagic_excluded && ⚠ BMD excluded from email}
{PUBLICATIONS.find(p => p.id === c.publication_id)?.short || c.publication_id} {c.placement_zone}
{c.start_date}
{c.end_date}
{c.status.replace(/_/g, ' ')} {c.impressions?.toLocaleString() || 0} {c.clicks?.toLocaleString() || 0} {((c.ctr || 0) * 100).toFixed(2)}% ${(c.agreed_rate || 0).toLocaleString()} {c.mooninvoice_invoice_id || '—'}
{c.status === 'pending_approval' && ( )} {c.status === 'active' && ( )} {c.status === 'paused' && ( )}
)} {/* EMAIL DISTRIBUTIONS TAB */} {activeTab === 'distributions' && (

Email Distributions

{['Campaign', 'Publication', 'List', 'Send Date', 'Sent', 'Status', 'Revenue'].map(h => ( ))} {distributions.length === 0 ? ( ) : distributions.map(c => ( ))}
{h}
No distributions yet

{c.campaign_name}

{c.client_name}

{PUBLICATIONS.find(p => p.id === c.publication_id)?.short || c.publication_id} {c.email_lists?.list_name || '—'} {c.send_date || '—'} {c.sent_count?.toLocaleString() || 0} {c.status} ${(c.agreed_rate || 0).toLocaleString()}
)} {/* NEWSLETTERS TAB */} {activeTab === 'newsletters' && (

Bi-Weekly Newsletters

{PUBLICATIONS.map(pub => { const pubNewsletters = newsletters.filter(n => n.publication_id === pub.id); const latest = pubNewsletters[0]; return (

{pub.name}

Every other Thursday
Next send: Thursday 11:00 AM ET
Total issues: {pubNewsletters.length}
{latest && (
Last status: {latest.status}
)}
{latest?.status === 'pending_approval' && ( )}
); })}
{/* Newsletter History */}

Newsletter History

{['Publication', 'Issue', 'Send Date', 'Sent', 'Status'].map(h => ( ))} {newsletters.length === 0 ? ( ) : newsletters.map(n => ( ))}
{h}
No newsletters yet
{PUBLICATIONS.find(p => p.id === n.publication_id)?.name || n.publication_id} #{n.newsletter_issue_number || '—'} {n.send_date || '—'} {n.sent_count?.toLocaleString() || 0} {n.status}
)} {/* DOCUMENTS TAB */} {activeTab === 'documents' && (

Insertion Orders & Documents

Documents are attached via email and stored automatically.

Jeff attaches IOs to campaign emails — they appear here once processed.

)} {/* INVOICES TAB */} {activeTab === 'invoices' && (

Campaign Invoices

{['Campaign', 'Client', 'Publication', 'Amount', 'Invoice #', 'Status'].map(h => ( ))} {campaigns.filter(c => c.mooninvoice_invoice_id || c.agreed_rate).length === 0 ? ( ) : campaigns.filter(c => c.agreed_rate).map(c => ( ))}
{h}
No invoiced campaigns yet
{c.campaign_name || c.client_name} {c.client_name} {PUBLICATIONS.find(p => p.id === c.publication_id)?.short || c.publication_id} ${(c.agreed_rate || 0).toLocaleString()} {c.mooninvoice_invoice_id || 'Pending'} {c.status.replace(/_/g, ' ')}
)} {/* REPORTS TAB */} {activeTab === 'reports' && (

Performance Reports

{PUBLICATIONS.map(pub => { const pubCampaigns = campaigns.filter(c => c.publication_id === pub.id); const totalRevenue = pubCampaigns.reduce((sum, c) => sum + (c.agreed_rate || 0), 0); const activeCnt = pubCampaigns.filter(c => c.status === 'active').length; const totalImpressions = pubCampaigns.reduce((sum, c) => sum + (c.impressions || 0), 0); return (

{pub.name}

Active campaigns{activeCnt}
Total campaigns{pubCampaigns.length}
Total impressions{totalImpressions.toLocaleString()}
Total revenue${totalRevenue.toLocaleString()}
); })}
{/* Export */}

Export Reports

)}
); }