import { useState, useEffect, useContext } from 'react' import { useNavigate } from 'react-router-dom' import { Plus, Search, TrendingUp, DollarSign, Eye, MousePointer, Target, BarChart3 } from 'lucide-react' import { format } from 'date-fns' import { AppContext } from '../App' import { useAuth } from '../contexts/AuthContext' import { useLanguage } from '../i18n/LanguageContext' import { api, PLATFORMS } from '../utils/api' import { PlatformIcons } from '../components/PlatformIcon' import StatusBadge from '../components/StatusBadge' import BrandBadge from '../components/BrandBadge' import BudgetBar from '../components/BudgetBar' import InteractiveTimeline from '../components/InteractiveTimeline' import CampaignDetailPanel from '../components/CampaignDetailPanel' function ROIBadge({ revenue, spent }) { if (!spent || spent <= 0) return null const roi = ((revenue - spent) / spent * 100).toFixed(0) const color = roi >= 0 ? 'text-emerald-600 bg-emerald-50' : 'text-red-600 bg-red-50' return ( ROI {roi}% ) } function MetricCard({ icon: Icon, label, value, color = 'text-text-primary' }) { return (
{value || '—'}
{label}
) } export default function Campaigns() { const { brands, getBrandName } = useContext(AppContext) const { lang, currencySymbol } = useLanguage() const { permissions } = useAuth() const navigate = useNavigate() const [campaigns, setCampaigns] = useState([]) const [loading, setLoading] = useState(true) const [panelCampaign, setPanelCampaign] = useState(null) const [filters, setFilters] = useState({ brand: '', status: '' }) useEffect(() => { loadCampaigns() }, []) const loadCampaigns = async () => { try { const res = await api.get('/campaigns') setCampaigns(res.data || res || []) } catch (err) { console.error('Failed to load campaigns:', err) } finally { setLoading(false) } } const handlePanelSave = async (campaignId, data) => { if (campaignId) { await api.patch(`/campaigns/${campaignId}`, data) } else { await api.post('/campaigns', data) } loadCampaigns() } const handlePanelDelete = async (campaignId) => { await api.delete(`/campaigns/${campaignId}`) loadCampaigns() } const openNew = () => { setPanelCampaign({ status: 'planning', platforms: [] }) } const filtered = campaigns.filter(c => { if (filters.brand && String(c.brandId || c.brand_id) !== filters.brand) return false if (filters.status && c.status !== filters.status) return false return true }) // Aggregate stats const totalBudget = filtered.reduce((sum, c) => sum + (c.budget || 0), 0) const totalSpent = filtered.reduce((sum, c) => sum + (c.budgetSpent || c.budget_spent || 0), 0) const totalImpressions = filtered.reduce((sum, c) => sum + (c.impressions || 0), 0) const totalClicks = filtered.reduce((sum, c) => sum + (c.clicks || 0), 0) const totalConversions = filtered.reduce((sum, c) => sum + (c.conversions || 0), 0) const totalRevenue = filtered.reduce((sum, c) => sum + (c.revenue || 0), 0) if (loading) { return (
) } return (
{/* Toolbar */}
{permissions?.canCreateCampaigns && ( )}
{/* Summary Cards */} {(totalBudget > 0 || totalSpent > 0) && (
Budget
{totalBudget.toLocaleString()}
{currencySymbol} total
Spent
{totalSpent.toLocaleString()}
{currencySymbol} spent
Impressions
{totalImpressions.toLocaleString()}
Clicks
{totalClicks.toLocaleString()}
Conversions
{totalConversions.toLocaleString()}
Revenue
{totalRevenue.toLocaleString()}
{currencySymbol}
)} {/* Timeline */} ({ id: campaign._id || campaign.id, label: campaign.name, description: campaign.description, startDate: campaign.startDate || campaign.start_date || campaign.createdAt, endDate: campaign.endDate || campaign.end_date, status: campaign.status, assigneeName: campaign.brandName || campaign.brand_name, tags: campaign.platforms || [], })} onDateChange={async (campaignId, { startDate, endDate }) => { try { await api.patch(`/campaigns/${campaignId}`, { start_date: startDate, end_date: endDate }) } catch (err) { console.error('Timeline date update failed:', err) } finally { loadCampaigns() } }} onItemClick={(campaign) => { navigate(`/campaigns/${campaign._id || campaign.id}`) }} /> {/* Campaign list */}

All Campaigns

{filtered.length === 0 ? (
No campaigns found
) : ( filtered.map(campaign => { const spent = campaign.budgetSpent || campaign.budget_spent || 0 const budget = campaign.budget || 0 return (
navigate(`/campaigns/${campaign.id || campaign._id}`)} className="relative px-5 py-4 hover:bg-surface-secondary cursor-pointer transition-colors" >

{campaign.name}

{(campaign.brand_id || campaign.brandName) && }
{campaign.description && (

{campaign.description}

)}
{budget > 0 && (
)} {(campaign.impressions > 0 || campaign.clicks > 0) && (
{campaign.impressions > 0 && 👁 {campaign.impressions.toLocaleString()}} {campaign.clicks > 0 && 🖱 {campaign.clicks.toLocaleString()}} {campaign.conversions > 0 && 🎯 {campaign.conversions.toLocaleString()}}
)}
{campaign.startDate && campaign.endDate ? ( <> {format(new Date(campaign.startDate), 'MMM d')} – {format(new Date(campaign.endDate), 'MMM d, yyyy')} ) : '—'}
{campaign.platforms && campaign.platforms.length > 0 && (
)}
) }) )}
{/* Campaign Panel */} {panelCampaign && ( setPanelCampaign(null)} onSave={handlePanelSave} onDelete={permissions?.canDeleteCampaigns ? handlePanelDelete : null} brands={brands} permissions={permissions} /> )}
) }