import { useMemo } from 'react'; import { Link } from 'react-router'; import { formatCurrency } from '@/lib/format'; import { cx } from '@/utils/cx'; import type { Campaign } from '@/types/entities'; interface CampaignRoiCardsProps { campaigns: Campaign[]; } type CampaignWithCac = Campaign & { cac: number; conversionRate: number; budgetProgress: number; }; export const CampaignRoiCards = ({ campaigns }: CampaignRoiCardsProps) => { const sorted = useMemo((): CampaignWithCac[] => { return campaigns .map((campaign) => { const spent = campaign.amountSpent?.amountMicros ?? 0; const converted = campaign.convertedCount ?? 0; const leadCount = campaign.leadCount ?? 0; const budgetMicros = campaign.budget?.amountMicros ?? 1; const cac = converted > 0 ? spent / converted : Infinity; const conversionRate = leadCount > 0 ? converted / leadCount : 0; const budgetProgress = budgetMicros > 0 ? spent / budgetMicros : 0; return { ...campaign, cac, conversionRate, budgetProgress }; }) .sort((a, b) => a.cac - b.cac); }, [campaigns]); const getHealthColor = (rate: number): string => { if (rate >= 0.1) return 'bg-success-500'; if (rate >= 0.05) return 'bg-warning-500'; return 'bg-error-500'; }; return (

Campaign ROI

{sorted.map((campaign) => (
{campaign.campaignName}
{campaign.leadCount ?? 0} leads | {campaign.convertedCount ?? 0} converted
{campaign.cac === Infinity ? '—' : formatCurrency(campaign.cac)} CAC
{Math.round(campaign.budgetProgress * 100)}% budget used
))}
View All Campaigns →
); };