'use client'; import React, { useState, useEffect, useCallback } from 'react'; import { MetricsCard } from '@/components/dashboard/MetricsCard'; import { CampaignTable } from '@/components/dashboard/CampaignTable'; import { DateFilter } from '@/components/dashboard/DateFilter'; import { Spinner } from '@/components/ui/Spinner'; import { RefreshCw } from 'lucide-react'; import type { CampaignWithInsights, AccountOverview, DatePreset } from '@/lib/meta/types'; export default function DashboardPage() { const [datePreset, setDatePreset] = useState('last_30d'); const [campaigns, setCampaigns] = useState([]); const [overview, setOverview] = useState(null); const [loading, setLoading] = useState(true); const [refreshing, setRefreshing] = useState(false); const [error, setError] = useState(''); const fetchData = useCallback(async (showLoading = true) => { if (showLoading) setLoading(true); else setRefreshing(true); setError(''); try { const [campaignsRes, overviewRes] = await Promise.all([ fetch(`/api/campaigns?date_preset=${datePreset}`), fetch(`/api/campaigns/insights?date_preset=${datePreset}`), ]); if (!campaignsRes.ok || !overviewRes.ok) { throw new Error('Erro ao carregar dados'); } const campaignsData = await campaignsRes.json(); const overviewData = await overviewRes.json(); if (campaignsData.success) setCampaigns(campaignsData.data); if (overviewData.success) setOverview(overviewData.data); } catch (err) { setError(err instanceof Error ? err.message : 'Erro ao carregar dados'); } finally { setLoading(false); setRefreshing(false); } }, [datePreset]); useEffect(() => { fetchData(); }, [fetchData]); // Auto-refresh every 60s useEffect(() => { const interval = setInterval(() => fetchData(false), 60000); return () => clearInterval(interval); }, [fetchData]); const handleStatusChange = async (campaignId: string, newStatus: 'ACTIVE' | 'PAUSED') => { const res = await fetch(`/api/campaigns/${campaignId}/status`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ status: newStatus }), }); if (!res.ok) throw new Error('Erro ao atualizar status'); // Update local state setCampaigns(prev => prev.map(c => c.campaign.id === campaignId ? { ...c, campaign: { ...c.campaign, status: newStatus } } : c ) ); }; const handleBudgetChange = async (campaignId: string, newBudget: number) => { const res = await fetch(`/api/campaigns/${campaignId}/budget`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ daily_budget: newBudget }), }); if (!res.ok) throw new Error('Erro ao atualizar orçamento'); // Update local state setCampaigns(prev => prev.map(c => c.campaign.id === campaignId ? { ...c, campaign: { ...c.campaign, daily_budget: String(newBudget) } } : c ) ); }; const formatCurrency = (val: any) => `R$ ${Number(val || 0).toFixed(2)}`; const formatNumber = (val: any) => Number(val || 0).toLocaleString('pt-BR'); return (
{/* Header */}

Dashboard

Visão geral das suas campanhas de Facebook Ads

{/* Error */} {error && (

{error}

Verifique se as credenciais do Meta estão configuradas no .env.local

)} {/* Metrics Cards */} {loading ? (
{Array.from({ length: 6 }).map((_, i) => (
))}
) : overview ? (
0 ? `CPL: ${formatCurrency(overview.total_spend / overview.total_leads)}` : undefined} icon="users" color="green" delay={2} /> 0 ? `${overview.total_roas.toFixed(2)}x` : '—'} subtitle="Retorno sobre investimento" icon="trending" color="blue" delay={3} />
) : null} {/* Campaigns Table */} {/* Footer */} {!loading && (

{campaigns.length} campanha{campaigns.length !== 1 ? 's' : ''} encontrada{campaigns.length !== 1 ? 's' : ''}

Auto-refresh: 60s • Dados via Meta Marketing API v20.0

)} {/* Loading overlay on refresh */} {refreshing && (
Atualizando...
)}
); }