From 77f4dba1b246f0f6228290b17ed093270bb070eb Mon Sep 17 00:00:00 2001 From: Marcio Bevervanso Date: Tue, 16 Jun 2026 15:04:27 -0300 Subject: [PATCH] =?UTF-8?q?fix:=20resolver=20instagram=5Factor=5Fid=20v?= =?UTF-8?q?=C3=A1lido=20p/=20Ads=20+=20modal=20largo=20com=20scroll?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit IG: o id que vem da Página (instagram_business_account) é recusado pela API de Ads (1815199) mesmo com a conta autorizada. Agora resolvemos o "ator" correto via /act_X/instagram_accounts, casando pelo @username, nos fluxos de criar campanha e adicionar anúncio. Modal: .modal-content ganha max-height 90vh + overflow para não quebrar a tela ao gerar copy/imagem, e tamanho 'lg' (680px) usado no Adicionar Anúncio, que estava estreito demais. Co-Authored-By: Claude Opus 4.8 --- src/app/api/adsets/[id]/ads/route.ts | 10 ++++++++- src/app/api/campaigns/create/route.ts | 14 +++++++++--- src/app/dashboard/create/page.tsx | 3 +++ src/app/globals.css | 9 ++++++++ src/components/dashboard/AddAdModal.tsx | 3 ++- src/components/ui/Modal.tsx | 9 ++++++-- src/lib/mcp/client.ts | 10 +++++++++ src/lib/mcp/tools.ts | 30 +++++++++++++++++++++++++ 8 files changed, 81 insertions(+), 7 deletions(-) diff --git a/src/app/api/adsets/[id]/ads/route.ts b/src/app/api/adsets/[id]/ads/route.ts index 794777f..368edcc 100644 --- a/src/app/api/adsets/[id]/ads/route.ts +++ b/src/app/api/adsets/[id]/ads/route.ts @@ -1,6 +1,6 @@ import { NextRequest, NextResponse } from 'next/server'; import { createMetaMCPClient } from '@/lib/mcp/client'; -import { addAdToAdSet, getAdSetDefaultCreative } from '@/lib/mcp/tools'; +import { addAdToAdSet, getAdSetDefaultCreative, resolveInstagramActorId } from '@/lib/mcp/tools'; import { getAccessToken } from '@/lib/auth/session'; function isWhatsappAd(body: Record) { @@ -97,6 +97,14 @@ export async function POST( ); } + // Resolve o instagram_actor_id válido para Ads a partir da conta de anúncios + // (o id da Página costuma ser recusado com 1815199). Casa pelo @username. + finalInstagramActorId = await resolveInstagramActorId(mcpClient, { + username: body.instagram_username, + fallbackId: finalInstagramActorId, + adAccountId: ad_account_id, + }); + const finalIsWhatsapp = isWhatsappAd({ ...body, cta: finalCta }); if (!finalLink && !whatsapp_link && !whatsapp_phone_number) { diff --git a/src/app/api/campaigns/create/route.ts b/src/app/api/campaigns/create/route.ts index 2940b89..dc39f5a 100644 --- a/src/app/api/campaigns/create/route.ts +++ b/src/app/api/campaigns/create/route.ts @@ -1,7 +1,7 @@ import { NextResponse } from 'next/server'; import { cookies } from 'next/headers'; import { createMetaMCPClient } from '@/lib/mcp/client'; -import { createFullCampaign, getPages } from '@/lib/mcp/tools'; +import { createFullCampaign, getPages, resolveInstagramActorId } from '@/lib/mcp/tools'; function toNumberOrUndefined(value: unknown): number | undefined { if (value === undefined || value === null || value === '') return undefined; @@ -247,6 +247,14 @@ export async function POST(req: Request) { // análise/entrega na Meta. const finalStatus = body.activate === true ? 'ACTIVE' : 'PAUSED'; + // Resolve o instagram_actor_id correto para Ads (o id da Página costuma ser + // recusado com 1815199). Casa pelo @username vindo do formulário. + const resolvedInstagramActorId = await resolveInstagramActorId(mcpClient, { + username: body.instagram_username, + fallbackId: instagram_actor_id, + adAccountId: ad_account_id, + }); + const baseTargeting = targeting || { geo_locations: { countries: ['BR'] } }; const finalTargeting: Record = { ...baseTargeting, @@ -256,7 +264,7 @@ export async function POST(req: Request) { // Sem identidade de Instagram, o anúncio não pode rodar em posicionamentos // do Instagram (a Meta recusa com subcode 1772103). Nesse caso restringimos // aos posicionamentos que não exigem IG (Facebook/Audience Network/Messenger). - if (!instagram_actor_id && !(baseTargeting as any).publisher_platforms) { + if (!resolvedInstagramActorId && !(baseTargeting as any).publisher_platforms) { finalTargeting.publisher_platforms = ['facebook', 'audience_network', 'messenger']; } @@ -269,7 +277,7 @@ export async function POST(req: Request) { bid_amount: finalBidStrategy === 'LOWEST_COST_WITHOUT_CAP' ? undefined : finalBidAmount, targeting: finalTargeting, page_id: finalPageId, - instagram_actor_id: instagram_actor_id || undefined, + instagram_actor_id: resolvedInstagramActorId || undefined, image_urls, image_bytes, link, diff --git a/src/app/dashboard/create/page.tsx b/src/app/dashboard/create/page.tsx index d3b8d2c..9da21f1 100644 --- a/src/app/dashboard/create/page.tsx +++ b/src/app/dashboard/create/page.tsx @@ -551,6 +551,9 @@ export default function CreateCampaignPage() { instagram_actor_id: selectedInstagramAccount?.id || undefined, + // username pra resolver o "ator" de IG correto na conta de anúncios. + instagram_username: selectedInstagramAccount?.username || undefined, + pixel_id: form.pixel_id || undefined, conversion_event: form.conversion_event, diff --git a/src/app/globals.css b/src/app/globals.css index 756873b..83b9836 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -464,9 +464,18 @@ body { padding: 32px; max-width: 480px; width: 90%; + /* Evita transbordar a tela quando o conteúdo cresce (gerar copy/imagem): + limita a altura e rola por dentro em vez de quebrar o layout. */ + max-height: 90vh; + overflow-y: auto; animation: fadeInUp 0.3s ease; } +/* Modal largo (formulários maiores, ex: adicionar anúncio). */ +.modal-content.modal-lg { + max-width: 680px; +} + /* ---- Autofill Override ---- */ input:-webkit-autofill, input:-webkit-autofill:hover, diff --git a/src/components/dashboard/AddAdModal.tsx b/src/components/dashboard/AddAdModal.tsx index 087a2c3..41b736e 100644 --- a/src/components/dashboard/AddAdModal.tsx +++ b/src/components/dashboard/AddAdModal.tsx @@ -253,6 +253,7 @@ export function AddAdModal({ campaign_name: campaignName, page_id: effectivePageId || defaults?.page_id, instagram_actor_id: selectedInstagramAccount?.id || undefined, + instagram_username: selectedInstagramAccount?.username || undefined, link: link.trim() || undefined, headline: selectedCopyData.headline, primary_text: selectedCopyData.primary_text, @@ -283,7 +284,7 @@ export function AddAdModal({ }; return ( - +

Campanha: {campaignName} diff --git a/src/components/ui/Modal.tsx b/src/components/ui/Modal.tsx index c518e1a..b29c09d 100644 --- a/src/components/ui/Modal.tsx +++ b/src/components/ui/Modal.tsx @@ -7,14 +7,19 @@ interface ModalProps { onClose: () => void; title: string; children: React.ReactNode; + /** 'lg' deixa o modal mais largo, para formulários maiores. */ + size?: 'default' | 'lg'; } -export function Modal({ isOpen, onClose, title, children }: ModalProps) { +export function Modal({ isOpen, onClose, title, children, size = 'default' }: ModalProps) { if (!isOpen) return null; return (

-
e.stopPropagation()}> +
e.stopPropagation()} + >

{title}