import React, { useState, useEffect } from "react"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Instagram, Loader2 } from "lucide-react"; import { metaAdsService } from "@/services/metaAdsService"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "@/components/ui/tooltip"; const formatCurrency = (value, decimalPlaces = 2) => new Intl.NumberFormat("en-US", { style: "currency", currency: "USD", minimumFractionDigits: decimalPlaces, maximumFractionDigits: decimalPlaces, }).format(value || 0); const formatNumber = (value, decimalPlaces = 0) => { return new Intl.NumberFormat("en-US", { minimumFractionDigits: decimalPlaces, maximumFractionDigits: decimalPlaces, }).format(value || 0); }; const formatPercent = (value, decimalPlaces = 2) => `${(value || 0).toFixed(decimalPlaces)}%`; const summaryCard = (label, value, options = {}) => { const { isMonetary = false, isPercentage = false, decimalPlaces = 0, } = options; let displayValue; if (isMonetary) { displayValue = formatCurrency(value, decimalPlaces); } else if (isPercentage) { displayValue = formatPercent(value, decimalPlaces); } else { displayValue = formatNumber(value, decimalPlaces); } return (
{label}
{displayValue}
); }; const MetricCell = ({ value, label, sublabel, isMonetary = false, isPercentage = false, decimalPlaces = 0, }) => (
{isMonetary ? formatCurrency(value, decimalPlaces) : isPercentage ? formatPercent(value, decimalPlaces) : formatNumber(value, decimalPlaces)}
{label && (
{label}
)} {sublabel && (
{sublabel}
)} ); const getActionValue = (campaign, actionType) => { if (actionType === "impressions" || actionType === "reach") { return campaign.metrics[actionType] || 0; } const actions = campaign.metrics.actions; if (Array.isArray(actions)) { const action = actions.find((a) => a.action_type === actionType); return action ? parseInt(action.value) || 0 : 0; } return 0; }; const CampaignName = ({ name }) => { if (name.startsWith("Instagram post: ")) { return (
{name.replace("Instagram post: ", "")}
); } return {name}; }; const MetaAdsOverview = () => { const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [campaigns, setCampaigns] = useState([]); const [timeframe, setTimeframe] = useState("30"); const [summaryMetrics, setSummaryMetrics] = useState(null); const computeDateRange = (timeframe) => { // Create date in Eastern Time const now = new Date(); const easternTime = new Date( now.toLocaleString("en-US", { timeZone: "America/New_York" }) ); easternTime.setHours(0, 0, 0, 0); // Set to start of day let sinceDate, untilDate; if (timeframe === "today") { // For today, both dates should be the current date in Eastern Time sinceDate = untilDate = new Date(easternTime); } else { // For other periods, calculate the date range untilDate = new Date(easternTime); untilDate.setDate(untilDate.getDate() - 1); // Yesterday sinceDate = new Date(untilDate); sinceDate.setDate(sinceDate.getDate() - parseInt(timeframe) + 1); } return { since: sinceDate.toISOString().split("T")[0], until: untilDate.toISOString().split("T")[0], }; }; useEffect(() => { const fetchMetaAdsData = async () => { try { setLoading(true); setError(null); const { since, until } = computeDateRange(timeframe); const [campaignData, accountInsights] = await Promise.all([ metaAdsService.getCampaigns({ since, until }), metaAdsService.getAccountInsights({ since, until }), ]); const activeCampaigns = campaignData.filter((c) => c.metrics.spend > 0); setCampaigns(activeCampaigns); const totalReach = parseInt(accountInsights.reach || 0); if (activeCampaigns.length > 0) { const totalSpend = activeCampaigns.reduce( (sum, camp) => sum + camp.metrics.spend, 0 ); const totalImpressions = activeCampaigns.reduce( (sum, camp) => sum + camp.metrics.impressions, 0 ); const totalPurchases = activeCampaigns.reduce( (sum, camp) => sum + camp.metrics.totalPurchases, 0 ); const totalPurchaseValue = activeCampaigns.reduce( (sum, camp) => sum + camp.metrics.purchaseValue, 0 ); const totalLinkClicks = activeCampaigns.reduce( (sum, camp) => sum + (camp.metrics.clicks || 0), 0 ); const totalPostEngagements = activeCampaigns.reduce( (sum, camp) => sum + (camp.metrics.totalPostEngagements || 0), 0 ); const totalFrequency = activeCampaigns.reduce( (sum, camp) => sum + camp.metrics.frequency, 0 ); const totalCpm = activeCampaigns.reduce( (sum, camp) => sum + camp.metrics.cpm, 0 ); const totalCtr = activeCampaigns.reduce( (sum, camp) => sum + camp.metrics.ctr, 0 ); const totalCpc = activeCampaigns.reduce( (sum, camp) => sum + camp.metrics.cpc, 0 ); const numCampaigns = activeCampaigns.length; setSummaryMetrics({ totalSpend, totalPurchaseValue, totalLinkClicks, totalImpressions, totalReach, totalPurchases, avgFrequency: totalFrequency / numCampaigns, avgCpm: totalCpm / numCampaigns, avgCtr: totalCtr / numCampaigns, avgCpc: totalCpc / numCampaigns, totalPostEngagements, totalCampaigns: numCampaigns, }); } } catch (err) { console.error("Meta Ads fetch error:", err); setError(`Failed to fetch Meta Ads data: ${err.message}`); } finally { setLoading(false); } }; fetchMetaAdsData(); }, [timeframe]); if (loading) { return ( ); } if (error) { return (
{error}
); } return (
Meta Ads Performance
{[ { label: "Active Campaigns", value: summaryMetrics?.totalCampaigns, }, { label: "Total Spend", value: summaryMetrics?.totalSpend, options: { isMonetary: true, decimalPlaces: 0 }, }, { label: "Total Reach", value: summaryMetrics?.totalReach }, { label: "Total Impressions", value: summaryMetrics?.totalImpressions, }, { label: "Avg Frequency", value: summaryMetrics?.avgFrequency, options: { decimalPlaces: 2 }, }, { label: "Total Engagements", value: summaryMetrics?.totalPostEngagements, }, { label: "Avg CPM", value: summaryMetrics?.avgCpm, options: { isMonetary: true, decimalPlaces: 2 }, }, { label: "Avg CTR", value: summaryMetrics?.avgCtr, options: { isPercentage: true, decimalPlaces: 2 }, }, { label: "Avg CPC", value: summaryMetrics?.avgCpc, options: { isMonetary: true, decimalPlaces: 2 }, }, { label: "Total Link Clicks", value: summaryMetrics?.totalLinkClicks, }, { label: "Total Purchases", value: summaryMetrics?.totalPurchases }, { label: "Purchase Value", value: summaryMetrics?.totalPurchaseValue, options: { isMonetary: true, decimalPlaces: 0 }, }, ].map((card) => (
{summaryCard(card.label, card.value, card.options)}
))}
{campaigns.map((campaign) => ( ))}
Campaign Spend Reach Impressions CPM CTR Results Value Engagements
{campaign.objective}
); }; export default MetaAdsOverview;