/** * ComplianceCenter — pending creative/consent/campaign approvals, blocked claims, * review-disclosure checks, and an AI audit + conversion-sync log (spec §8, §10.1). * An unapproved campaign cannot publish and a risky message cannot send. */ import React from 'react'; import { ShieldCheck, AlertTriangle, Ban, CheckCircle2, Clock, RefreshCw, FileWarning, Wand2, ArrowUpRight, } from 'lucide-react'; import { useSocialAds } from '../SocialAdsContext.jsx'; import { PlatformBadge, Card, SectionHeader, StatusChip, Btn } from '../components/ui.jsx'; const TYPE_LABEL = { creative: 'Creative', consent_text: 'Consent text', review_disclosure: 'Review disclosure', campaign: 'Campaign publish', }; const SYNC_ICON = { synced: { icon: CheckCircle2, color: 'text-emerald-500' }, queued: { icon: Clock, color: 'text-amber-500' }, failed: { icon: AlertTriangle, color: 'text-red-500' }, }; export default function ComplianceCenter() { const { approvals, resolveApproval, conversionEvents, retryConversion } = useSocialAds(); const pending = approvals.filter(a => a.status === 'pending'); const blocked = approvals.filter(a => a.status === 'blocked'); const resolved = approvals.filter(a => a.status === 'approved'); return (
{blocked.length > 0 && }
{/* Blocked first — must resolve */} {blocked.length > 0 && (
{blocked.map(a => )}
)} {/* Pending queue */} {pending.length === 0 ?

Nothing pending — all clear.

:
{pending.map(a => )}
}
{/* Conversion sync log */}
{conversionEvents.map(e => { const meta = SYNC_ICON[e.syncStatus]; const Icon = meta.icon; return (

{e.eventName}

dedup: {e.dedupKey}

{e.value > 0 && ${e.value.toLocaleString()}} {e.syncStatus === 'failed' ? retryConversion(e.id)}>Retry : }
); })}
{/* Recently resolved + AI audit */}
{resolved.map(a => (

{a.title}

{TYPE_LABEL[a.type]} · approved by {a.reviewer}

))}

AI guardrail summary: 2 creatives auto-flagged this week (1 scarcity, 1 price claim), 0 auto-published. All AI copy required human approval.

); } function ApprovalRow({ item, onResolve }) { const isBlocked = item.status === 'blocked'; return (
{TYPE_LABEL[item.type]}

{item.title}

{item.flags?.length > 0 && (
    {item.flags.map((f, i) =>
  • {f}
  • )}
)}
onResolve(item.id, 'approved')}>Approve onResolve(item.id, 'blocked')}>{isBlocked ? 'Keep blocked' : 'Block'}
); }