@fayz-ai/plugin-conversations 0.12.2 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/data/campaigns.ts","../src/data/providers.ts","../src/views/CampaignsView.tsx","../src/views/TemplatesView.tsx","../src/context.tsx","../src/types.ts","../src/lib/channel.ts","../src/views/shared.tsx","../src/views/ConversationList.tsx","../src/views/SendWindow.tsx","../src/views/MessageInspector.tsx","../src/views/MessageThread.tsx","../src/views/LeadCard.tsx","../src/views/ContactPanel.tsx","../src/views/NewConversationPanel.tsx","../src/views/InboxView.tsx","../src/views/ConversationsPage.tsx","../src/data/accents.ts","../src/data/mock.ts","../src/data/tables.ts","../src/data/supabase.ts","../src/store.ts","../src/locales/en.ts","../src/locales/pt-BR.ts","../src/locales/index.ts","../src/views/dashboardWidgets.tsx","../src/settings/WhatsAppProviders.tsx","../src/data/channels.ts","../src/settings/ConversationsSettings.tsx","../src/lib/onboarding.ts","../src/integrations/tyxter/data.ts","../src/data/payment-preflight.ts","../src/integrations/tyxter/connectorDef.tsx","../src/migrations/index.ts","../src/index.ts"],"names":["client","getSupabaseClientOptional","getActiveTenantId","React","AlertTriangle","toast","jsxs","jsx","Fragment","Button","Loader2","Send","store","MessageSquare","cn","Input","InboxIcon","Clock","useTranslation","X","CHANNELS","Check","RefreshCw","React14","SettingsGroup","invoke","tenant","ExternalLink","useState","useEffect"],"mappings":"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AAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA;AAogB/B,IAAM,gDAAA,GAAmD,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA;AAyGzD,IAAM,oDAAA,GAAuD,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA,CAAA;AAyO7D,IAAM,0DAAA,GAA6D,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA,CAAA;AA0WnE,IAAM,gEAAA,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","file":"index.js","sourcesContent":["// ---------------------------------------------------------------------------\n// Campanhas, do lado do navegador.\n//\n// A leitura é direta (RLS por inquilino); o DISPARO passa pela função de borda,\n// porque é lá que a credencial mora e é lá que o ritmo é respeitado. O navegador\n// nunca fala com o Tyxter nem com o Evolution.\n// ---------------------------------------------------------------------------\nimport { getSupabaseClientOptional, getActiveTenantId } from '@fayz-ai/core'\nimport type { ProviderId } from './providers'\n\nexport interface CampaignTemplate {\n id: string\n name: string\n language: string\n category: string\n body: string\n /** Os nomes das variáveis, na ordem de {{1}}, {{2}}. A Meta entende posição;\n * gente entende nome — e a tela pede \"cargo\", não \"variável 3\". */\n variables: string[]\n provider: string | null\n status: string\n statusDetail: string | null\n}\n\nexport interface CampaignProgress {\n campaignId: string\n name: string\n status: 'draft' | 'scheduled' | 'running' | 'paused' | 'done' | 'cancelled'\n pausedReason: string | null\n provider: string | null\n total: number\n queued: number\n sending: number\n sent: number\n delivered: number\n read: number\n replied: number\n failed: number\n skipped: number\n /** Abaixo de 30% a prática diz que o risco de banimento sobe. Fica na tela. */\n replyRate: number | null\n firstSentAt: string | null\n lastSentAt: string | null\n}\n\nexport interface DispatchResult {\n campaignId: string\n provider?: ProviderId\n sent: number\n remaining: number | null\n failures?: Array<{ to: string; code: string; message: string }>\n paused?: string\n skipped?: boolean\n reason?: string\n}\n\nfunction client() {\n const c = getSupabaseClientOptional() as any\n if (!c) throw new Error('Supabase não inicializado')\n return c\n}\n\nfunction tenant(): string {\n const t = getActiveTenantId()\n if (!t) throw new Error('Nenhuma casa ativa')\n return t\n}\n\nexport const campaignsApi = {\n async templates(): Promise<CampaignTemplate[]> {\n const { data, error } = await client()\n .from('plg_conversations_templates')\n .select('id, name, language, category, body, variables, provider, status, status_detail')\n .eq('tenant_id', tenant()).order('name')\n if (error) throw new Error(error.message)\n return ((data ?? []) as any[]).map((r) => ({\n id: r.id, name: r.name, language: r.language, category: r.category,\n body: r.body, variables: r.variables ?? [], provider: r.provider,\n status: r.status, statusDetail: r.status_detail ?? null,\n }))\n },\n\n async saveTemplate(t: { id?: string; name: string; body: string; variables?: string[]; language?: string; category?: string }) {\n const row = {\n tenant_id: tenant(), name: t.name, body: t.body,\n variables: t.variables ?? [], language: t.language ?? 'pt_BR',\n category: t.category ?? 'MARKETING', updated_at: new Date().toISOString(),\n }\n const q = t.id\n ? client().from('plg_conversations_templates').update(row).eq('id', t.id)\n : client().from('plg_conversations_templates').insert(row)\n const { error } = await q\n if (error) throw new Error(error.message)\n },\n\n async list(): Promise<CampaignProgress[]> {\n const { data, error } = await client()\n .from('v_conversations_campaign_progress')\n .select('*').eq('tenant_id', tenant())\n if (error) throw new Error(error.message)\n return ((data ?? []) as any[]).map((r) => ({\n campaignId: r.campaign_id, name: r.name, status: r.status,\n pausedReason: r.paused_reason ?? null, provider: r.provider,\n total: Number(r.total ?? 0), queued: Number(r.queued ?? 0),\n sending: Number(r.sending ?? 0), sent: Number(r.sent ?? 0),\n delivered: Number(r.delivered ?? 0), read: Number(r.read ?? 0),\n replied: Number(r.replied ?? 0), failed: Number(r.failed ?? 0),\n skipped: Number(r.skipped ?? 0),\n replyRate: r.reply_rate == null ? null : Number(r.reply_rate),\n firstSentAt: r.first_sent_at ?? null, lastSentAt: r.last_sent_at ?? null,\n }))\n },\n\n /** Cria a campanha e enfileira os destinatários.\n *\n * `ON CONFLICT DO NOTHING` via upsert: o índice único (campanha, telefone,\n * rodada) é quem impede o disparo duplo, e não uma checagem aqui. Dois\n * cliques no botão enfileiram uma vez. */\n async create(input: {\n name: string\n templateId?: string\n provider?: ProviderId\n channelId?: string\n targets: Array<{ phone: string; personId?: string; variables?: Record<string, string> | string[] }>\n }): Promise<string> {\n const tenantId = tenant()\n const { data: camp, error } = await client()\n .from('plg_conversations_campaigns')\n .insert({\n tenant_id: tenantId, name: input.name,\n template_id: input.templateId ?? null,\n provider: input.provider ?? null,\n channel_id: input.channelId ?? null,\n status: 'draft',\n })\n .select('id').single()\n if (error) throw new Error(error.message)\n\n const rows = input.targets\n .map((t) => ({\n tenant_id: tenantId, campaign_id: camp.id,\n phone_e164: String(t.phone).replace(/\\D/g, ''),\n person_id: t.personId ?? null,\n variables: t.variables ?? {},\n }))\n .filter((r) => r.phone_e164.length >= 10)\n\n // Em blocos: o PostgREST tem teto por requisição, e uma lista de 300 leads\n // numa inserção só volta truncada sem dizer que truncou.\n for (let i = 0; i < rows.length; i += 200) {\n const { error: e } = await client()\n .from('plg_conversations_campaign_targets')\n .upsert(rows.slice(i, i + 200), { onConflict: 'campaign_id,phone_e164,rotation', ignoreDuplicates: true })\n if (e) throw new Error(e.message)\n }\n return camp.id as string\n },\n\n async setStatus(campaignId: string, status: CampaignProgress['status']) {\n const patch: Record<string, unknown> = { status, updated_at: new Date().toISOString() }\n if (status === 'running') { patch.started_at = new Date().toISOString(); patch.paused_reason = null }\n const { error } = await client()\n .from('plg_conversations_campaigns').update(patch).eq('id', campaignId)\n if (error) throw new Error(error.message)\n },\n\n /** Um lote. Quem chama repete até `remaining` chegar a zero — o arrendamento\n * de cinco minutos é o que impede duas chamadas de mandarem o mesmo. */\n async dispatch(campaignId: string, limit?: number): Promise<DispatchResult> {\n const { data, error } = await client().functions.invoke('conversations-provider', {\n body: { tenantId: tenant(), action: 'dispatch', campaignId, limit },\n })\n if (error) {\n const detail = (await error.context?.json?.().catch(() => null)) as { message?: string } | null\n throw new Error(detail?.message ?? error.message)\n }\n if ((data as any)?.error) throw new Error((data as any).message ?? (data as any).error)\n return data as DispatchResult\n },\n}\n","// ---------------------------------------------------------------------------\n// Os dois provedores de WhatsApp, do lado do navegador.\n//\n// Este arquivo NUNCA vê uma chave. A leitura vem de `v_conversations_providers`,\n// que expõe tudo menos o segredo; a escrita vai por uma função `SECURITY\n// DEFINER`; e qualquer coisa que precise realmente falar com o Tyxter ou com o\n// Evolution passa pela função de borda, onde a credencial mora.\n//\n// É por isso que `hasCredential` é um booleano e não uma string mascarada:\n// `tx_live_••••` ainda é a chave inteira viajando até aqui para ser escondida\n// na renderização. O que não sai do servidor não vaza no devtools.\n// ---------------------------------------------------------------------------\nimport { getSupabaseClientOptional, getActiveTenantId } from '@fayz-ai/core'\n\nexport type ProviderId = 'tyxter' | 'evolution'\n\n/** O que cada provedor sabe fazer. Vem do banco para a tela DESABILITAR o que\n * não existe, em vez de oferecer um botão que erra. */\nexport interface ProviderCapabilities {\n templates: boolean\n /** Verdadeiro no Tyxter: iniciar conversa exige template aprovado pela Meta. */\n requires_template: boolean\n /** 24 no Tyxter, nulo no Evolution — que não tem janela nenhuma. */\n session_window_hours: number | null\n delivery_receipts: boolean\n /** Verdadeiro no Evolution: parear é ler um QR com o celular. */\n qr_pairing: boolean\n ban_risk: 'low' | 'high' | string\n}\n\nexport interface ProviderRow {\n provider: ProviderId\n config: Record<string, unknown>\n capabilities: Partial<ProviderCapabilities>\n isActive: boolean\n status: 'unconfigured' | 'connecting' | 'connected' | 'error' | 'disconnected'\n statusDetail: string | null\n checkedAt: string | null\n /** Se a chave já foi posta. O valor não chega aqui, nem mascarado. */\n hasCredential: boolean\n}\n\n/** Um número por onde a mensagem pode sair. */\nexport interface ProviderNumber {\n id: string\n phone: string | null\n displayName: string | null\n status: string\n /** Só o Tyxter tem: é a Meta quem avalia. Nulo no Evolution, e a tela mostra\n * \"—\" em vez de inventar um \"bom\" que ninguém mediu. */\n quality: string | null\n tier: string | null\n used24h: number | null\n allowance: number | null\n /** Se pode carregar um envio AGORA. */\n ready: boolean\n}\n\nexport interface ProviderTemplate {\n id: string\n name: string\n language: string\n category: string\n status: string\n body: string\n variables: number\n rejectionReason: string | null\n approvedAt: string | null\n}\n\nconst FN = 'conversations-provider'\n\nfunction client() {\n const c = getSupabaseClientOptional() as any\n if (!c) throw new Error('Supabase não inicializado')\n return c\n}\n\n/** A porta de borda. Tudo que fala com um provedor de verdade passa por aqui. */\nasync function callFn<T>(action: string, body: Record<string, unknown> = {}): Promise<T> {\n const tenantId = getActiveTenantId()\n if (!tenantId) throw new Error('Nenhuma casa ativa')\n const { data, error } = await client().functions.invoke(FN, {\n body: { tenantId, action, ...body },\n })\n if (error) {\n // O corpo do erro carrega a mensagem útil (\"o Tyxter só inicia conversa por\n // template\"); o `error.message` do SDK carrega \"Edge Function returned a\n // non-2xx status code\", que não ajuda ninguém.\n const detail = (await error.context?.json?.().catch(() => null)) as { message?: string } | null\n throw new Error(detail?.message ?? error.message)\n }\n if ((data as any)?.error) throw new Error((data as any).message ?? (data as any).error)\n return data as T\n}\n\nexport const providersApi = {\n /** O que está configurado nesta casa. Sem segredo. */\n async list(): Promise<ProviderRow[]> {\n const tenantId = getActiveTenantId()\n if (!tenantId) return []\n const { data, error } = await client()\n .from('v_conversations_providers')\n .select('provider, config, capabilities, is_active, status, status_detail, checked_at, has_credential')\n .eq('tenant_id', tenantId)\n if (error) throw new Error(error.message)\n return ((data ?? []) as any[]).map((r) => ({\n provider: r.provider,\n config: r.config ?? {},\n capabilities: r.capabilities ?? {},\n isActive: Boolean(r.is_active),\n status: r.status,\n statusDetail: r.status_detail ?? null,\n checkedAt: r.checked_at ?? null,\n hasCredential: Boolean(r.has_credential),\n }))\n },\n\n /** Guarda config e, quando vier, a chave. Chave ausente é \"não mexe\": salvar\n * a URL do Evolution sem redigitar a chave é o gesto comum. */\n async save(provider: ProviderId, config: Record<string, unknown>, apiKey?: string): Promise<void> {\n const tenantId = getActiveTenantId()\n if (!tenantId) throw new Error('Nenhuma casa ativa')\n const { error } = await client().rpc('conversations_set_provider', {\n p_tenant: tenantId,\n p_provider: provider,\n p_config: config,\n p_api_key: apiKey && apiKey.trim() ? apiKey.trim() : null,\n })\n if (error) throw new Error(error.message)\n },\n\n /** A troca. Um ativo por casa, garantido por índice único no banco. */\n async activate(provider: ProviderId): Promise<void> {\n const tenantId = getActiveTenantId()\n if (!tenantId) throw new Error('Nenhuma casa ativa')\n const { error } = await client().rpc('conversations_activate_provider', {\n p_tenant: tenantId,\n p_provider: provider,\n })\n if (error) throw new Error(error.message)\n },\n\n health: (provider?: ProviderId) =>\n callFn<{ provider: ProviderId; ok: boolean; detail: string | null; capabilities: ProviderCapabilities }>('health', { provider }),\n\n numbers: (provider?: ProviderId) =>\n callFn<{ provider: ProviderId; numbers: ProviderNumber[] }>('numbers', { provider }),\n\n /** O QR do Evolution. O Tyxter recusa com `unsupported`. */\n pair: (provider?: ProviderId) =>\n callFn<{ provider: ProviderId; qr: string | null; code: string | null }>('pair', { provider }),\n\n templates: (provider?: ProviderId) =>\n callFn<{ provider: ProviderId; templates: ProviderTemplate[]; capabilities: ProviderCapabilities }>('templates', { provider }),\n\n createTemplate: (template: { name: string; body: string; language?: string; category?: string; example?: string[] }, provider?: ProviderId) =>\n callFn<{ provider: ProviderId; result: unknown }>('create_template', { template, provider }),\n\n send: (input: {\n to: string\n text?: string\n template?: { name: string; language?: string; body?: string; variables?: string[] }\n senderId?: string\n provider?: ProviderId\n }) =>\n callFn<{ provider: ProviderId; to: string; providerMessageId: string | null; status: string; skipped?: boolean; reason?: string }>('send', input),\n}\n","import React from 'react'\nimport { Loader2, Pause, Play, Send, AlertTriangle, MessageSquare } from 'lucide-react'\nimport { Button, toast } from '@fayz-ai/ui'\nimport { campaignsApi, type CampaignProgress, type CampaignTemplate } from '../data/campaigns'\nimport { providersApi, type ProviderRow } from '../data/providers'\n\n/**\n * As campanhas, e o botão que as faz andar.\n *\n * ── por que o disparo é um laço na tela e não um botão que \"manda tudo\" ──\n *\n * Uma função de borda tem teto de tempo, e trezentos contatos no ritmo seguro\n * do Evolution — 12 por minuto, com 15 a 45 s aleatórios entre um e outro —\n * levam horas. Nenhuma requisição sobrevive a isso.\n *\n * Então cada clique manda UM LOTE e devolve quantos faltam. A tela repete\n * enquanto a aba estiver aberta, e quem fechar a aba pausa o disparo em vez de\n * perdê-lo: o arrendamento de cinco minutos devolve as linhas para a fila\n * sozinho, e o próximo clique continua de onde parou.\n *\n * É honesto sobre o que é: um disparador que depende de alguém olhando. Um\n * agendador de verdade mora no servidor e é outro trabalho — mas isto aqui\n * manda mensagem hoje, e não mente sobre o que faz quando a aba fecha.\n */\n\nconst STATE_COPY: Record<CampaignProgress['status'], { label: string; cls: string }> = {\n draft: { label: 'rascunho', cls: 'bg-muted text-muted-foreground' },\n scheduled: { label: 'agendada', cls: 'bg-info-soft text-info-soft-foreground' },\n running: { label: 'disparando', cls: 'bg-success-soft text-success-soft-foreground' },\n paused: { label: 'pausada', cls: 'bg-warning-soft text-warning-soft-foreground' },\n done: { label: 'concluída', cls: 'bg-muted text-muted-foreground' },\n cancelled: { label: 'cancelada', cls: 'bg-muted text-muted-foreground' },\n}\n\nfunction Bar({ c }: { c: CampaignProgress }) {\n const total = Math.max(1, c.total)\n const seg = (n: number, cls: string, title: string) =>\n n > 0 ? <div className={cls} style={{ width: `${(n / total) * 100}%` }} title={`${title}: ${n}`} /> : null\n return (\n <div className=\"flex h-1.5 w-full overflow-hidden rounded-full bg-muted\">\n {seg(c.replied, 'bg-success', 'responderam')}\n {seg(c.sent - c.replied, 'bg-primary', 'enviadas')}\n {seg(c.failed, 'bg-destructive', 'falharam')}\n {seg(c.skipped, 'bg-muted-foreground/40', 'puladas')}\n </div>\n )\n}\n\nexport function CampaignsView() {\n const [rows, setRows] = React.useState<CampaignProgress[] | null>(null)\n const [templates, setTemplates] = React.useState<CampaignTemplate[]>([])\n const [providers, setProviders] = React.useState<ProviderRow[]>([])\n const [running, setRunning] = React.useState<string | null>(null)\n // Um sinal de parada que o laço lê a cada volta. Sem ele, \"Pausar\" só\n // mudaria o banco e o lote seguinte sairia assim mesmo.\n const stop = React.useRef(false)\n\n const load = React.useCallback(async () => {\n try {\n const [c, t, p] = await Promise.all([\n campaignsApi.list(), campaignsApi.templates(), providersApi.list(),\n ])\n setRows(c); setTemplates(t); setProviders(p)\n } catch (e: any) {\n toast.error(e?.message ?? 'Não consegui carregar')\n setRows([])\n }\n }, [])\n React.useEffect(() => { void load() }, [load])\n\n const active = providers.find((p) => p.isActive)\n\n async function run(c: CampaignProgress) {\n stop.current = false\n setRunning(c.campaignId)\n try {\n if (c.status !== 'running') await campaignsApi.setStatus(c.campaignId, 'running')\n // O laço. Cada volta é um lote; entre lotes o servidor já esperou o\n // atraso aleatório, então aqui não há sleep — só a próxima chamada.\n for (;;) {\n if (stop.current) { await campaignsApi.setStatus(c.campaignId, 'paused'); break }\n const r = await campaignsApi.dispatch(c.campaignId)\n await load()\n if (r.paused) { toast.error(`Pausado: ${r.paused}`); break }\n if (r.skipped) { toast.error(r.reason ?? 'Campanha não está disparando'); break }\n if (r.failures?.length) {\n for (const f of r.failures.slice(0, 3)) toast.error(`${f.to}: ${f.message}`)\n }\n if (!r.remaining) { toast.success('Campanha concluída'); break }\n }\n } catch (e: any) {\n toast.error(e?.message ?? 'Falhou')\n } finally {\n setRunning(null); void load()\n }\n }\n\n return (\n <div className=\"space-y-4\">\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <div>\n <h2 className=\"text-lg font-semibold\">Campanhas</h2>\n <p className=\"text-xs text-muted-foreground\">\n {active\n ? <>saindo por <span className=\"font-medium text-foreground\">{active.provider}</span>\n {active.capabilities?.requires_template && ' · exige template aprovado'}</>\n : 'nenhum provedor de WhatsApp ativo — configure em Ajustes'}\n </p>\n </div>\n </div>\n\n {rows === null && (\n <div className=\"flex items-center gap-2 py-8 text-sm text-muted-foreground\">\n <Loader2 className=\"h-4 w-4 animate-spin\" /> carregando…\n </div>\n )}\n\n {rows?.length === 0 && (\n <div className=\"rounded-card border-2 border-dashed py-10 text-center\">\n <MessageSquare className=\"mx-auto h-8 w-8 text-muted-foreground/30\" />\n <p className=\"mt-2 text-sm text-muted-foreground\">Nenhuma campanha ainda.</p>\n <p className=\"text-xs text-muted-foreground\">\n Crie uma a partir da lista de leads: filtre, selecione e escolha \"Enviar WhatsApp\".\n </p>\n </div>\n )}\n\n {rows?.map((c) => {\n const st = STATE_COPY[c.status] ?? STATE_COPY.draft\n const busy = running === c.campaignId\n return (\n <div key={c.campaignId} className=\"rounded-card border bg-card p-4\" data-testid={`campaign-${c.campaignId}`}>\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"text-sm font-semibold\">{c.name}</span>\n <span className={`rounded-full px-2 py-0.5 text-[10px] font-medium ${st.cls}`}>{st.label}</span>\n {c.provider && <span className=\"text-[10px] text-muted-foreground\">{c.provider}</span>}\n {/* Por que parou sozinha. É o alarme de qualidade da Meta\n chegando em quem disparou, e não num log que ninguém abre. */}\n {c.pausedReason && (\n <span className=\"inline-flex items-center gap-1 text-[10px] text-warning\">\n <AlertTriangle className=\"h-3 w-3\" /> {c.pausedReason}\n </span>\n )}\n </div>\n\n <div className=\"mt-2\"><Bar c={c} /></div>\n\n <div className=\"mt-2 flex flex-wrap gap-x-4 gap-y-1 text-xs text-muted-foreground\">\n <span>{c.total} destinatários</span>\n <span>enviadas: <span className=\"text-foreground tabular-nums\">{c.sent}</span></span>\n <span>responderam: <span className=\"text-foreground tabular-nums\">{c.replied}</span></span>\n {c.failed > 0 && <span className=\"text-destructive\">falharam: {c.failed}</span>}\n {c.skipped > 0 && <span>puladas: {c.skipped}</span>}\n {c.queued > 0 && <span>na fila: <span className=\"text-foreground tabular-nums\">{c.queued}</span></span>}\n {/* Abaixo de 30% a prática diz que o risco de banimento sobe.\n Por isso a taxa fica ao lado dos números e não num relatório. */}\n {c.replyRate != null && (\n <span className={c.replyRate < 30 ? 'text-warning' : 'text-success'}>\n resposta: {c.replyRate}%\n </span>\n )}\n </div>\n\n <div className=\"mt-3 flex gap-2\">\n {busy ? (\n <Button size=\"sm\" variant=\"outline\" onClick={() => { stop.current = true }}>\n <Pause className=\"mr-1 h-3 w-3\" /> Pausar\n </Button>\n ) : (\n <Button\n size=\"sm\"\n disabled={!active || c.queued === 0 || c.status === 'done' || c.status === 'cancelled'}\n onClick={() => void run(c)}\n >\n {c.status === 'paused' ? <><Play className=\"mr-1 h-3 w-3\" /> Continuar</> : <><Send className=\"mr-1 h-3 w-3\" /> Disparar</>}\n </Button>\n )}\n {busy && <span className=\"flex items-center gap-1 text-xs text-muted-foreground\">\n <Loader2 className=\"h-3 w-3 animate-spin\" /> disparando — mantenha esta aba aberta\n </span>}\n </div>\n </div>\n )\n })}\n\n {templates.length > 0 && (\n <div className=\"rounded-card border bg-card p-4\">\n <p className=\"text-xs font-medium text-muted-foreground mb-2\">Templates ({templates.length})</p>\n {templates.map((t) => (\n <div key={t.id} className=\"flex flex-wrap items-baseline gap-2 py-1 text-xs\">\n <span className=\"font-medium\">{t.name}</span>\n <span className={t.status === 'approved' ? 'text-success' : 'text-muted-foreground'}>{t.status}</span>\n <span className=\"truncate text-muted-foreground\">{t.body.slice(0, 70)}</span>\n </div>\n ))}\n </div>\n )}\n </div>\n )\n}\n","import React from 'react'\nimport { Loader2, Plus, RefreshCw, Send, AlertTriangle, CheckCircle2, Clock } from 'lucide-react'\nimport { Button, Input, toast } from '@fayz-ai/ui'\nimport { providersApi, type ProviderTemplate, type ProviderRow } from '../data/providers'\nimport { campaignsApi, type CampaignTemplate } from '../data/campaigns'\n\n/**\n * Os templates: escrever, submeter à Meta, e saber o que ela respondeu.\n *\n * ── por que esta tela precisa existir ───────────────────────────────────\n *\n * Na API oficial, um template aprovado é a ÚNICA porta para iniciar conversa\n * com quem nunca respondeu — que é o caso de toda lista de feira. Sem esta\n * tela, a única forma de criar um era um comando no terminal, e o estado da\n * aprovação só existia na conta do provedor.\n *\n * ── as duas listas, e por que não são uma ───────────────────────────────\n *\n * `plg_conversations_templates` é o texto DA CASA: existe mesmo sem provedor,\n * serve ao Evolution (que não tem o conceito e manda o texto direto), e\n * sobrevive a uma troca de provedor.\n *\n * A lista do provedor é o que a Meta aprovou. Um texto nosso sem par lá é um\n * texto que não pode iniciar conversa; um par lá sem texto aqui é um template\n * que alguém criou por fora. A tela mostra os dois lados e a diferença, em vez\n * de fingir que são a mesma coisa.\n *\n * ── o limite de caracteres não é enfeite ────────────────────────────────\n *\n * A Meta recusa corpo acima de 1024 caracteres, e a recusa chega um dia depois\n * — porque a fila de aprovação é dela. Contar aqui custa nada e economiza 24h.\n */\n\nconst STATUS_UI: Record<string, { icon: typeof Clock; cls: string; label: string }> = {\n approved: { icon: CheckCircle2, cls: 'text-success', label: 'aprovado' },\n submitted: { icon: Clock, cls: 'text-warning', label: 'em análise' },\n pending: { icon: Clock, cls: 'text-warning', label: 'em análise' },\n rejected: { icon: AlertTriangle, cls: 'text-destructive', label: 'recusado' },\n paused: { icon: AlertTriangle, cls: 'text-warning', label: 'pausado' },\n disabled: { icon: AlertTriangle, cls: 'text-destructive', label: 'desativado' },\n draft: { icon: Clock, cls: 'text-muted-foreground', label: 'rascunho' },\n}\n\n/** `{{1}}`, `{{2}}`… na ordem em que aparecem. A Meta só entende posição. */\nfunction slots(body: string): number {\n const found = new Set((body.match(/\\{\\{(\\d+)\\}\\}/g) ?? []))\n return found.size\n}\n\nexport function TemplatesView() {\n const [mine, setMine] = React.useState<CampaignTemplate[] | null>(null)\n const [theirs, setTheirs] = React.useState<ProviderTemplate[]>([])\n const [providers, setProviders] = React.useState<ProviderRow[]>([])\n const [busy, setBusy] = React.useState<string | null>(null)\n const [draft, setDraft] = React.useState({ name: '', body: '', variables: '' })\n\n const active = providers.find((p) => p.isActive)\n const supportsTemplates = active?.capabilities?.templates !== false\n\n const load = React.useCallback(async () => {\n try {\n const [m, p] = await Promise.all([campaignsApi.templates(), providersApi.list()])\n setMine(m); setProviders(p)\n } catch (e: any) { toast.error(e?.message ?? 'Não consegui carregar'); setMine([]) }\n }, [])\n React.useEffect(() => { void load() }, [load])\n\n const sync = async () => {\n setBusy('sync')\n try {\n const d = await providersApi.templates()\n setTheirs(d.templates)\n toast.success(`${d.templates.length} template(s) no provedor`)\n } catch (e: any) { toast.error(e?.message ?? 'Falhou') } finally { setBusy(null) }\n }\n\n const create = async () => {\n const name = draft.name.trim().toLowerCase().replace(/[^a-z0-9_]/g, '_')\n if (!name || !draft.body.trim()) { toast.error('Nome e corpo são obrigatórios'); return }\n // A Meta recusa acima de 1024 e responde no dia seguinte. Contar aqui\n // custa nada e economiza 24 horas de espera por um erro de digitação.\n if (draft.body.length > 1024) { toast.error(`Corpo com ${draft.body.length} caracteres — a Meta aceita até 1024`); return }\n setBusy('create')\n try {\n const vars = draft.variables.split(',').map((v) => v.trim()).filter(Boolean)\n await campaignsApi.saveTemplate({ name, body: draft.body, variables: vars })\n if (supportsTemplates) {\n // O exemplo é OBRIGATÓRIO quando há variáveis: sem ele a Meta recusa\n // sem dizer por quê, e a recusa volta um dia depois.\n await providersApi.createTemplate({\n name, body: draft.body,\n example: vars.length ? vars.map((v) => `exemplo ${v}`) : undefined,\n })\n toast.success('Submetido — a Meta costuma responder em ~24h')\n } else {\n toast.success('Guardado. Este provedor não usa templates: o texto sai direto.')\n }\n setDraft({ name: '', body: '', variables: '' })\n await load(); await sync()\n } catch (e: any) { toast.error(e?.message ?? 'Falhou') } finally { setBusy(null) }\n }\n\n const byName = new Map(theirs.map((t) => [t.name, t]))\n\n return (\n <div className=\"space-y-4\">\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <div>\n <h2 className=\"text-lg font-semibold\">Templates</h2>\n <p className=\"text-xs text-muted-foreground\">\n {supportsTemplates\n ? <>No <span className=\"font-medium text-foreground\">{active?.provider ?? 'provedor'}</span>, um template aprovado é a única porta para falar com quem nunca respondeu.</>\n : <>O <span className=\"font-medium text-foreground\">{active?.provider}</span> não usa templates — o texto sai direto. O que você escrever aqui vira o corpo da campanha.</>}\n </p>\n </div>\n <Button size=\"sm\" variant=\"outline\" disabled={busy === 'sync' || !active} onClick={() => void sync()}>\n {busy === 'sync' ? <Loader2 className=\"h-3 w-3 animate-spin\" /> : <><RefreshCw className=\"mr-1 h-3 w-3\" /> Buscar no provedor</>}\n </Button>\n </div>\n\n {/* ── escrever ── */}\n <div className=\"rounded-card border bg-card p-4\">\n <p className=\"mb-2 flex items-center gap-1.5 text-sm font-medium\"><Plus className=\"h-3.5 w-3.5\" /> Novo template</p>\n <div className=\"grid gap-2 sm:grid-cols-[1fr_1fr]\">\n <label className=\"block\">\n <span className=\"text-[11px] text-muted-foreground\">Nome (sem espaços)</span>\n <Input className=\"mt-0.5 h-8 text-sm\" placeholder=\"primeiro_contato_feira\"\n value={draft.name} onChange={(e) => setDraft({ ...draft, name: e.target.value })} />\n </label>\n <label className=\"block\">\n <span className=\"text-[11px] text-muted-foreground\">Variáveis, em ordem, separadas por vírgula</span>\n <Input className=\"mt-0.5 h-8 text-sm\" placeholder=\"nome, vendedor, dor\"\n value={draft.variables} onChange={(e) => setDraft({ ...draft, variables: e.target.value })} />\n </label>\n </div>\n <label className=\"mt-2 block\">\n <span className=\"text-[11px] text-muted-foreground\">\n Corpo — use {'{{1}}'}, {'{{2}}'} na ordem das variáveis\n </span>\n <textarea\n rows={5}\n className=\"mt-0.5 w-full resize-y rounded-md border border-border bg-background px-3 py-2 text-sm outline-none focus:border-primary\"\n placeholder={'Oi {{1}}! Aqui é {{2}}, do ChefControl 👋\\n\\nA gente se falou no Salão Abrasel...'}\n value={draft.body}\n onChange={(e) => setDraft({ ...draft, body: e.target.value })}\n />\n </label>\n <div className=\"mt-1 flex flex-wrap items-center gap-3 text-[11px]\">\n <span className={draft.body.length > 1024 ? 'font-medium text-destructive' : 'text-muted-foreground'}>\n {draft.body.length}/1024 caracteres\n </span>\n <span className=\"text-muted-foreground\">{slots(draft.body)} variável(is) no corpo</span>\n {/* O descasamento é o erro mais comum e o mais caro: a Meta recusa e\n você só descobre no dia seguinte. */}\n {slots(draft.body) !== draft.variables.split(',').filter((v) => v.trim()).length && draft.body && (\n <span className=\"text-warning\">\n ⚠ o corpo tem {slots(draft.body)} e você nomeou {draft.variables.split(',').filter((v) => v.trim()).length}\n </span>\n )}\n </div>\n <Button size=\"sm\" className=\"mt-2\" disabled={busy === 'create'} onClick={() => void create()}>\n {busy === 'create' ? <Loader2 className=\"h-3 w-3 animate-spin\" /> : <><Send className=\"mr-1 h-3 w-3\" /> {supportsTemplates ? 'Guardar e submeter' : 'Guardar'}</>}\n </Button>\n </div>\n\n {/* ── nossos ── */}\n <div className=\"rounded-card border bg-card\">\n <p className=\"border-b px-4 py-2 text-xs font-medium text-muted-foreground\">\n Desta casa {mine ? `(${mine.length})` : ''}\n </p>\n {mine === null && <p className=\"px-4 py-3 text-xs text-muted-foreground\">carregando…</p>}\n {mine?.length === 0 && <p className=\"px-4 py-3 text-xs text-muted-foreground\">Nenhum template ainda.</p>}\n {mine?.map((t) => {\n const remote = byName.get(t.name)\n const st = STATUS_UI[remote?.status ?? t.status] ?? STATUS_UI.draft\n const Icon = st.icon\n return (\n <div key={t.id} className=\"border-b px-4 py-2.5 last:border-b-0\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"text-sm font-medium\">{t.name}</span>\n <span className={`inline-flex items-center gap-1 text-[10px] ${st.cls}`}>\n <Icon className=\"h-3 w-3\" /> {st.label}\n </span>\n {t.variables.length > 0 && (\n <span className=\"text-[10px] text-muted-foreground\">{t.variables.join(' · ')}</span>\n )}\n {/* Existe aqui e não lá: não pode iniciar conversa. É a\n diferença que decide se dá para usar numa campanha. */}\n {supportsTemplates && !remote && (\n <span className=\"text-[10px] text-warning\">não está no provedor</span>\n )}\n </div>\n <p className=\"mt-0.5 whitespace-pre-wrap text-xs text-muted-foreground\">{t.body}</p>\n {remote?.rejectionReason && (\n <p className=\"mt-1 text-[11px] text-destructive\">Recusado: {remote.rejectionReason}</p>\n )}\n </div>\n )\n })}\n </div>\n\n {/* ── os do provedor que não temos ── */}\n {theirs.filter((t) => !(mine ?? []).some((m) => m.name === t.name)).length > 0 && (\n <div className=\"rounded-card border bg-card\">\n <p className=\"border-b px-4 py-2 text-xs font-medium text-muted-foreground\">\n No provedor, sem par nesta casa — criados por fora\n </p>\n {theirs.filter((t) => !(mine ?? []).some((m) => m.name === t.name)).map((t) => {\n const st = STATUS_UI[t.status] ?? STATUS_UI.draft\n const Icon = st.icon\n return (\n <div key={t.id} className=\"flex flex-wrap items-baseline gap-2 border-b px-4 py-2 text-xs last:border-b-0\">\n <span className=\"font-medium\">{t.name}</span>\n <span className={`inline-flex items-center gap-1 text-[10px] ${st.cls}`}><Icon className=\"h-3 w-3\" /> {st.label}</span>\n <span className=\"truncate text-muted-foreground\">{t.body.slice(0, 80)}</span>\n </div>\n )\n })}\n </div>\n )}\n </div>\n )\n}\n","import React from 'react'\nimport { useStore, type StoreApi } from 'zustand'\nimport type { EntityLookup } from '@fayz-ai/admin'\nimport type { EntityDef } from '@fayz-ai/core'\nimport type { ConversationsUIState } from './store'\n\n/**\n * App-tunable knobs the inbox UI reads. Kept tiny on purpose: everything here\n * describes the APP's data shape (which people are \"contacts\" in this vertical),\n * never product policy.\n */\nexport interface ResolvedConversationsConfig {\n /** `people.kind` the picker searches. */\n contactKind: string\n /** Search source for the contact picker. Defaults to the person archetype. */\n contactLookup?: EntityLookup\n /** The app's own contact entity — \"Create\" opens ITS form. Agenda's twin is `clientEntityDef`. */\n contactEntityDef?: EntityDef\n}\n\nexport const DEFAULT_CONVERSATIONS_CONFIG: ResolvedConversationsConfig = {\n contactKind: 'contact',\n}\n\nconst StoreContext = React.createContext<StoreApi<ConversationsUIState> | null>(null)\nconst ConfigContext = React.createContext<ResolvedConversationsConfig>(DEFAULT_CONVERSATIONS_CONFIG)\n\nexport function ConversationsContextProvider({\n store,\n config = DEFAULT_CONVERSATIONS_CONFIG,\n children,\n}: {\n store: StoreApi<ConversationsUIState>\n config?: ResolvedConversationsConfig\n children?: React.ReactNode\n}) {\n return (\n <StoreContext.Provider value={store}>\n <ConfigContext.Provider value={config}>{children}</ConfigContext.Provider>\n </StoreContext.Provider>\n )\n}\n\nexport function useConversationsStore<T>(selector: (state: ConversationsUIState) => T): T {\n const store = React.useContext(StoreContext)\n if (!store) throw new Error('useConversationsStore must be used within ConversationsPage')\n return useStore(store, selector)\n}\n\nexport function useConversationsConfig(): ResolvedConversationsConfig {\n return React.useContext(ConfigContext)\n}\n","// ---------------------------------------------------------------------------\n// @fayz-ai/plugin-conversations — domain types for the unified omni-channel\n// inbox (the GoHighLevel \"Conversations\" equivalent). Provider-agnostic.\n// ---------------------------------------------------------------------------\n\nexport type Channel = 'sms' | 'whatsapp' | 'instagram' | 'email' | 'webchat'\n\nexport type ConversationStatus = 'open' | 'snoozed' | 'closed'\n\nexport type MessageDirection = 'inbound' | 'outbound'\n\nexport interface Conversation {\n id: string\n contactName: string\n /**\n * `public.people` id when the thread is tied to a real contact record (the\n * compose modal resolves one via the shared ContactPicker). Absent for legacy\n * threads and for inbound messages from an unknown handle.\n */\n contactPersonId?: string\n /** Phone, @handle, or email depending on channel */\n contactHandle: string\n channel: Channel\n lastMessagePreview: string\n lastMessageAt: string\n /**\n * Who spoke last: `inbound` = the customer, `outbound` = the house.\n *\n * The field the queue is built on — \"waiting on us\" is `status === 'open'`\n * plus this being `inbound`. `unreadCount` is the older approximation and\n * gets the painful case wrong: a thread somebody opened, read, meant to\n * answer later and forgot has zero unread and a customer still waiting.\n *\n * Optional because a thread predating the column's backfill (or a provider\n * that does not report direction) genuinely does not know — see\n * `isWaitingOnUs`, which falls back rather than guessing.\n */\n lastMessageDirection?: MessageDirection\n unreadCount: number\n status: ConversationStatus\n assignedTo?: string\n /** Tailwind-ish accent for the avatar bubble */\n accent: string\n tags: string[]\n // Optional context shown in the contact panel.\n location?: string\n note?: string\n}\n\nexport interface Message {\n id: string\n conversationId: string\n channel: Channel\n direction: MessageDirection\n body: string\n at: string\n author: string\n /** O que aconteceu com ela depois que saiu. Nulo na entrada — ela chegou,\n * não foi entregue por nós. `failed` é o caso que a tela precisa gritar:\n * uma mensagem que nunca saiu não pode parecer uma mensagem enviada. */\n deliveryStatus?: string | null\n /** O id no provedor. Sem ele, não saiu — e é a chave para buscar o JSON cru. */\n providerMessageId?: string | null\n}\n\nexport interface ListConversationsQuery {\n channel?: Channel | 'all'\n status?: ConversationStatus | 'all'\n search?: string\n}\n\nexport interface SendMessageInput {\n conversationId: string\n body: string\n}\n\nexport interface CreateConversationInput {\n contactName: string\n /** `public.people` id, when the contact was resolved/created by the picker. */\n contactPersonId?: string\n /** Phone, @handle, or email depending on channel. */\n contactHandle?: string\n channel: Channel\n /** Optional first outbound message; stamps preview + last_message_at. */\n firstMessage?: string\n /** Optional free-text note surfaced in the contact panel. */\n note?: string\n}\n\nexport const CHANNEL_LABELS: Record<Channel, string> = {\n sms: 'SMS',\n whatsapp: 'WhatsApp',\n instagram: 'Instagram',\n email: 'Email',\n webchat: 'Web Chat',\n}\n\n/**\n * Is this thread waiting on US?\n *\n * One definition, in one place, because the KPI, the queue table and anything\n * that later filters the inbox have to agree — a dashboard that says three and\n * a list that shows one is worse than neither.\n *\n * `lastMessageDirection` is the real answer. `unreadCount` is the fallback for\n * a thread that genuinely does not know its direction (older than migration\n * 001, or a provider that does not report one) — it under-reports rather than\n * over-reports, which is the right way round: a queue that invents work loses\n * trust faster than one that misses an old row.\n */\nexport function isWaitingOnUs(conversation: Conversation): boolean {\n if (conversation.status !== 'open') return false\n if (conversation.lastMessageDirection) return conversation.lastMessageDirection === 'inbound'\n return conversation.unreadCount > 0\n}\n","import React from 'react'\nimport { MessageSquare, Phone, Instagram, Mail, Globe } from 'lucide-react'\nimport { CHANNEL_LABELS, type Channel } from '../types'\n\n// ---------------------------------------------------------------------------\n// Shared channel visuals — one source of truth for the icon + accent color of\n// each channel, reused by the conversation list, thread header and contact\n// panel. Keeps the omni-channel inbox visually consistent.\n// ---------------------------------------------------------------------------\n\nexport const CHANNEL_ICON: Record<Channel, React.ComponentType<{ className?: string }>> = {\n sms: Phone,\n whatsapp: MessageSquare,\n instagram: Instagram,\n email: Mail,\n webchat: Globe,\n}\n\nexport interface ChannelAccent {\n /** Solid brand color (hex) — bubbles, dots, header chips. */\n color: string\n /** Soft tinted background classes for badges. */\n badge: string\n}\n\nexport const CHANNEL_ACCENT: Record<Channel, ChannelAccent> = {\n whatsapp: { color: '#22c55e', badge: 'bg-[#22c55e]/12 text-[#15803d] dark:text-[#4ade80]' },\n sms: { color: '#6366f1', badge: 'bg-[#6366f1]/12 text-[#4338ca] dark:text-[#a5b4fc]' },\n instagram: { color: '#ec4899', badge: 'bg-[#ec4899]/12 text-[#be185d] dark:text-[#f9a8d4]' },\n email: { color: '#0ea5e9', badge: 'bg-[#0ea5e9]/12 text-[#0369a1] dark:text-[#7dd3fc]' },\n webchat: { color: '#f59e0b', badge: 'bg-[#f59e0b]/12 text-[#b45309] dark:text-[#fcd34d]' },\n}\n\nexport { CHANNEL_LABELS }\n","import React from 'react'\nimport { cn } from '@fayz-ai/ui'\nimport { CHANNEL_ACCENT, CHANNEL_ICON, CHANNEL_LABELS } from '../lib/channel'\nimport type { Channel } from '../types'\n\n// ---------------------------------------------------------------------------\n// Small presentational helpers shared across the inbox panes.\n// ---------------------------------------------------------------------------\n\n/** Reactive media-query match — drives the responsive list/thread/panel layout. */\nexport function useMediaQuery(query: string): boolean {\n const [matches, setMatches] = React.useState(\n () => typeof window !== 'undefined' && window.matchMedia(query).matches,\n )\n React.useEffect(() => {\n if (typeof window === 'undefined') return\n const mql = window.matchMedia(query)\n const onChange = () => setMatches(mql.matches)\n onChange()\n mql.addEventListener('change', onChange)\n return () => mql.removeEventListener('change', onChange)\n }, [query])\n return matches\n}\n\nexport function initialsOf(name: string): string {\n return name.replace('@', '').split(/\\s+/).filter(Boolean).map((w) => w[0]).slice(0, 2).join('').toUpperCase()\n}\n\nexport function Avatar({ name, accent, size = 'md', channel }: {\n name: string\n accent: string\n size?: 'sm' | 'md' | 'lg'\n /** When set, renders a small channel glyph badge over the avatar. */\n channel?: Channel\n}) {\n const dims = size === 'lg' ? 'h-14 w-14 text-lg' : size === 'sm' ? 'h-9 w-9 text-xs' : 'h-10 w-10 text-sm'\n const Icon = channel ? CHANNEL_ICON[channel] : null\n return (\n <div className=\"relative shrink-0\">\n <div\n className={cn('flex items-center justify-center rounded-full font-semibold text-white', dims)}\n style={{ backgroundColor: accent }}\n >\n {initialsOf(name)}\n </div>\n {Icon && (\n <span\n className=\"absolute -bottom-0.5 -right-0.5 flex h-4 w-4 items-center justify-center rounded-full ring-2 ring-card\"\n style={{ backgroundColor: CHANNEL_ACCENT[channel!].color }}\n >\n <Icon className=\"h-2.5 w-2.5 text-white\" />\n </span>\n )}\n </div>\n )\n}\n\nexport function ChannelBadge({ channel, className }: { channel: Channel; className?: string }) {\n const Icon = CHANNEL_ICON[channel]\n return (\n <span\n className={cn(\n 'inline-flex items-center gap-1 rounded-full px-1.5 py-0.5 text-[10px] font-medium',\n CHANNEL_ACCENT[channel].badge,\n className,\n )}\n >\n <Icon className=\"h-2.5 w-2.5\" />\n {CHANNEL_LABELS[channel]}\n </span>\n )\n}\n\nexport function relativeTime(iso: string): string {\n const diff = Date.now() - new Date(iso).getTime()\n const mins = Math.round(diff / 60_000)\n if (mins < 1) return 'now'\n if (mins < 60) return `${mins}m`\n const hours = Math.round(mins / 60)\n if (hours < 24) return `${hours}h`\n const days = Math.round(hours / 24)\n if (days < 7) return `${days}d`\n return `${Math.round(days / 7)}w`\n}\n\n/** Day label for the thread date separators. */\nexport function dayLabel(iso: string): string {\n const d = new Date(iso)\n const now = new Date()\n const startOf = (x: Date) => new Date(x.getFullYear(), x.getMonth(), x.getDate()).getTime()\n const dayDiff = Math.round((startOf(now) - startOf(d)) / 86_400_000)\n if (dayDiff <= 0) return 'Today'\n if (dayDiff === 1) return 'Yesterday'\n if (dayDiff < 7) return d.toLocaleDateString([], { weekday: 'long' })\n return d.toLocaleDateString([], { month: 'short', day: 'numeric', year: now.getFullYear() === d.getFullYear() ? undefined : 'numeric' })\n}\n","import React from 'react'\nimport { Search, Inbox as InboxIcon } from 'lucide-react'\nimport { Input, Skeleton, cn } from '@fayz-ai/ui'\nimport { useTranslation } from '@fayz-ai/core'\nimport { useConversationsStore } from '../context'\nimport type { Channel } from '../types'\nimport { Avatar, ChannelBadge, relativeTime } from './shared'\n\nconst FILTERS: Array<Channel | 'all'> = ['all', 'whatsapp', 'sms', 'instagram', 'email', 'webchat']\n\nexport function ConversationList({ className }: { className?: string }) {\n const t = useTranslation()\n const {\n conversations, selectedId, channelFilter, search, loading,\n select, setChannelFilter, setSearch,\n } = useConversationsStore((s) => s)\n\n return (\n <aside className={cn('w-full shrink-0 flex-col border-r border-border bg-card lg:w-[320px]', className)}>\n {/* No title: the shell header already names the page 24px away. Starting\n a conversation moved there too — it is the PAGE's action, not the list's. */}\n <div className=\"border-b border-border px-3 py-3\">\n <div className=\"relative\">\n <Search className=\"pointer-events-none absolute left-2.5 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground\" />\n <Input\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n placeholder={t('conversations.list.search')}\n className=\"pl-8\"\n />\n </div>\n <div className=\"mt-2 flex flex-wrap gap-1\">\n {FILTERS.map((id) => (\n <button\n key={id}\n onClick={() => setChannelFilter(id)}\n className={cn(\n 'rounded-full px-2.5 py-1 text-xs font-medium transition-colors',\n channelFilter === id\n ? 'bg-primary text-primary-foreground'\n : 'bg-muted text-muted-foreground hover:bg-muted/70',\n )}\n >\n {t(`conversations.filter.${id}`)}\n </button>\n ))}\n </div>\n </div>\n\n <div className=\"min-h-0 flex-1 overflow-y-auto\">\n {loading && conversations.length === 0 &&\n Array.from({ length: 6 }, (_, i) => (\n <div key={i} className=\"flex w-full items-start gap-3 border-b border-border/50 px-3 py-3\">\n <Skeleton className=\"h-10 w-10 shrink-0 rounded-full\" />\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center justify-between gap-2\">\n <Skeleton className=\"h-4 w-28\" />\n <Skeleton className=\"h-3 w-8\" />\n </div>\n <Skeleton className=\"mt-1.5 h-4 w-16 rounded-full\" />\n <Skeleton className=\"mt-1.5 h-3 w-3/4\" />\n </div>\n </div>\n ))}\n {!loading && conversations.length === 0 && (\n <div className=\"flex flex-col items-center gap-2 p-8 text-center text-muted-foreground\">\n <InboxIcon className=\"h-6 w-6\" />\n <p className=\"text-sm\">{t('conversations.list.empty')}</p>\n </div>\n )}\n {conversations.map((c) => {\n const active = c.id === selectedId\n const unread = c.unreadCount > 0\n return (\n <button\n key={c.id}\n onClick={() => select(c.id)}\n className={cn(\n 'flex w-full items-start gap-3 border-b border-border/50 px-3 py-3 text-left transition-colors',\n // `text-accent-foreground` JUNTO com `bg-accent`: o par existe\n // para ser usado em par. Só o fundo trocava, e num tema em que\n // `accent` é quase preto o nome e a prévia continuavam\n // `text-foreground` — escuro sobre escuro, ilegível na conversa\n // ABERTA, que é justamente a que se está lendo.\n active ? 'bg-accent text-accent-foreground' : 'hover:bg-muted/50',\n )}\n >\n <Avatar name={c.contactName} accent={c.accent} channel={c.channel} />\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center justify-between gap-2\">\n <span className={cn('truncate text-sm', active ? '' : 'text-foreground', unread ? 'font-semibold' : 'font-medium')}>\n {c.contactName}\n </span>\n <span className={cn('shrink-0 text-[11px]', active ? 'opacity-70' : unread ? 'font-semibold text-primary' : 'text-muted-foreground', active && unread && 'font-semibold opacity-100')}>\n {relativeTime(c.lastMessageAt)}\n </span>\n </div>\n <div className=\"mt-1 flex items-center gap-2\">\n <ChannelBadge channel={c.channel} />\n {c.status !== 'open' && (\n <span className={cn('text-[10px] uppercase tracking-wide', active ? 'opacity-70' : 'text-muted-foreground/70')}>\n {t(`conversations.status.${c.status}`)}\n </span>\n )}\n </div>\n <div className=\"mt-1 flex items-center justify-between gap-2\">\n <span className={cn('truncate text-xs', active ? 'opacity-80' : unread ? 'text-foreground' : 'text-muted-foreground')}>\n {c.lastMessagePreview}\n </span>\n {unread && (\n <span className=\"flex h-[18px] min-w-[18px] shrink-0 items-center justify-center rounded-full bg-primary px-1.5 text-[10px] font-semibold text-primary-foreground\">\n {c.unreadCount}\n </span>\n )}\n </div>\n </div>\n </button>\n )\n })}\n </div>\n </aside>\n )\n}\n","import React from 'react'\nimport { getSupabaseClientOptional, getActiveTenantId } from '@fayz-ai/core'\nimport { providersApi, type ProviderRow } from '../data/providers'\n\n/**\n * Se dá para falar com esta pessoa AGORA, e por qual caminho.\n *\n * ── o defeito que isto existe para matar ────────────────────────────────\n *\n * Na API oficial do WhatsApp, texto livre só sai dentro de 24h contadas da\n * última mensagem DO CLIENTE. Fora dela a Meta recusa — e a recusa chega num\n * log, não na tela. O atendente digita, aperta enviar, vê a mensagem aparecer\n * na conversa, e ela nunca chega ao destinatário.\n *\n * Uma tela que aceita o que não vai entregar é pior que uma tela que recusa.\n *\n * ── e por que isso muda com o provedor ──────────────────────────────────\n *\n * No Evolution não existe janela nenhuma: manda sempre. Então o MESMO campo de\n * texto é seguro num provedor e mentiroso no outro, e qual está valendo é uma\n * configuração que muda numa tela distante daqui. Por isso este gancho lê as\n * duas coisas juntas — a janela da conversa E a capacidade do provedor ativo —\n * e devolve uma resposta só: pode escrever, ou não pode e por quê.\n */\n\nexport type SendMode =\n | { kind: 'free'; until: string | null; minutesLeft: number | null }\n | { kind: 'template'; reason: 'window_closed' | 'never_replied' }\n | { kind: 'blocked'; reason: 'no_provider' }\n\nexport interface WindowState {\n loading: boolean\n mode: SendMode\n provider: ProviderRow | undefined\n}\n\ninterface WindowRow {\n window_open: boolean\n window_expires_at: string | null\n minutes_left: number | null\n last_inbound_at: string | null\n}\n\nexport function useSendWindow(conversationId: string | undefined): WindowState {\n const [row, setRow] = React.useState<WindowRow | null>(null)\n const [providers, setProviders] = React.useState<ProviderRow[] | null>(null)\n\n React.useEffect(() => {\n let alive = true\n providersApi.list().then((p) => { if (alive) setProviders(p) }).catch(() => { if (alive) setProviders([]) })\n return () => { alive = false }\n }, [])\n\n React.useEffect(() => {\n let alive = true\n if (!conversationId) { setRow(null); return }\n const sb = getSupabaseClientOptional() as any\n if (!sb) { setRow(null); return }\n sb.from('v_conversations_window')\n .select('window_open, window_expires_at, minutes_left, last_inbound_at')\n .eq('conversation_id', conversationId).eq('tenant_id', getActiveTenantId())\n .maybeSingle()\n .then(({ data }: { data: WindowRow | null }) => { if (alive) setRow(data ?? null) })\n .catch(() => { if (alive) setRow(null) })\n return () => { alive = false }\n }, [conversationId])\n\n const provider = (providers ?? []).find((p) => p.isActive)\n\n const mode = React.useMemo<SendMode>(() => {\n if (!provider) return { kind: 'blocked', reason: 'no_provider' }\n // O provedor que não exige template não tem janela: manda sempre. Dizer\n // \"janela aberta\" aqui seria inventar um conceito que ele não tem.\n if (!provider.capabilities?.requires_template) {\n return { kind: 'free', until: null, minutesLeft: null }\n }\n if (row?.window_open) {\n return { kind: 'free', until: row.window_expires_at, minutesLeft: row.minutes_left }\n }\n return { kind: 'template', reason: row?.last_inbound_at ? 'window_closed' : 'never_replied' }\n }, [row, provider])\n\n return { loading: providers === null, mode, provider }\n}\n\n/** Quanto falta, em português de gente: \"faltam 3h\" e não um carimbo ISO. */\nexport function humanLeft(minutes: number | null | undefined): string {\n if (minutes == null) return ''\n if (minutes < 60) return `${minutes} min`\n const h = Math.floor(minutes / 60)\n return h < 24 ? `${h}h` : `${Math.floor(h / 24)}d`\n}\n\nexport function SendWindowBanner({ state }: { state: WindowState }) {\n const { mode, provider } = state\n if (mode.kind === 'free' && mode.minutesLeft == null) return null\n\n if (mode.kind === 'free') {\n // Aberta, mas fechando. O relógio importa: quem responde quer saber se dá\n // tempo de resolver agora ou se precisa de template.\n const urgent = (mode.minutesLeft ?? 0) < 120\n return (\n <div className={`flex items-center gap-2 px-4 py-1.5 text-[11px] ${urgent ? 'bg-warning-soft text-warning-soft-foreground' : 'text-muted-foreground'}`}>\n <span className=\"h-1.5 w-1.5 rounded-full bg-success\" />\n Janela aberta — texto livre por mais {humanLeft(mode.minutesLeft)}\n </div>\n )\n }\n\n if (mode.kind === 'blocked') {\n return (\n <div className=\"flex items-center gap-2 bg-destructive-soft px-4 py-2 text-[11px] text-destructive-soft-foreground\">\n <span className=\"h-1.5 w-1.5 rounded-full bg-destructive\" />\n Nenhum provedor de WhatsApp ativo — configure em Ajustes › Conversas.\n </div>\n )\n }\n\n return (\n <div className=\"flex flex-wrap items-center gap-x-2 gap-y-1 bg-warning-soft px-4 py-2 text-[11px] text-warning-soft-foreground\">\n <span className=\"h-1.5 w-1.5 shrink-0 rounded-full bg-warning\" />\n <span className=\"font-medium\">\n {mode.reason === 'never_replied'\n ? 'Esta pessoa nunca respondeu.'\n : 'A janela de 24 horas fechou.'}\n </span>\n <span>\n No {provider?.provider ?? 'provedor'}, texto livre só sai dentro de 24h da\n resposta do cliente. Para falar agora é preciso um template aprovado.\n </span>\n </div>\n )\n}\n","import React from 'react'\nimport { AlertTriangle, Check, CheckCheck, Clock, Loader2, MoreVertical, X } from 'lucide-react'\nimport { getSupabaseClientOptional, getActiveTenantId } from '@fayz-ai/core'\nimport type { Message } from '../types'\n\n/**\n * O que aconteceu com esta mensagem, e o JSON cru de quem devia tê-la entregue.\n *\n * ── por que isto existe ─────────────────────────────────────────────────\n *\n * Duas mensagens ficaram um dia inteiro na caixa com a mesma cara de qualquer\n * outra — balão verde, horário, tudo igual — e nunca chegaram a ninguém. Não\n * havia como saber olhando. A tela dizia \"enviada\" porque a linha existia no\n * banco, e existir no banco não é ter saído.\n *\n * Uma mensagem que falhou precisa parecer que falhou. E quando alguém\n * perguntar \"por quê?\", a resposta não pode ser \"olha o log do servidor\".\n *\n * ── o JSON vem AO VIVO, e não de uma cópia ──────────────────────────────\n *\n * O status de uma mensagem de WhatsApp muda DEPOIS do envio: aceita, enviada,\n * entregue, lida — às vezes horas depois. Uma cópia gravada no momento do envio\n * envelhece na primeira mudança e passa a mentir com precisão de carimbo.\n * Então o detalhe é buscado no provedor na hora em que alguém abre.\n */\n\nconst STATUS: Record<string, { icon: typeof Check; label: string; cls: string }> = {\n queued: { icon: Clock, label: 'na fila', cls: 'text-white/60' },\n sent: { icon: Check, label: 'enviada', cls: 'text-white/70' },\n delivered: { icon: CheckCheck, label: 'entregue', cls: 'text-white/80' },\n read: { icon: CheckCheck, label: 'lida', cls: 'text-sky-200' },\n failed: { icon: AlertTriangle, label: 'NÃO ENVIADA', cls: 'text-white' },\n expired: { icon: AlertTriangle, label: 'expirou', cls: 'text-white' },\n delivery_timeout: { icon: AlertTriangle, label: 'sem confirmação', cls: 'text-white' },\n cancelled: { icon: X, label: 'cancelada', cls: 'text-white' },\n opted_out: { icon: X, label: 'recusada', cls: 'text-white' },\n}\n\nconst BAD = new Set(['failed', 'expired', 'cancelled', 'opted_out', 'delivery_timeout'])\n\nexport function isFailed(m: Message): boolean {\n if (m.direction !== 'outbound') return false\n // O STATUS MANDA. Ele vem do provedor e é a resposta de quem entregou.\n if (m.deliveryStatus) return BAD.has(m.deliveryStatus)\n\n // Sem status, a ausência de id do provedor é o único sinal que resta — mas\n // ela também é o estado normal nos primeiros segundos, enquanto o envio\n // acontece. Pintar de vermelho nesse intervalo faz a tela acusar de falha\n // uma mensagem que está saindo, e foi exatamente o que aconteceu com uma\n // que já tinha sido entregue.\n //\n // Dois minutos: mais que qualquer envio e menos que a paciência de quem\n // está olhando.\n if (m.providerMessageId) return false\n const age = Date.now() - new Date(m.at).getTime()\n // Uma mensagem em fila para o disparador local pode esperar mais que dois\n // minutos sem que nada esteja errado — ele confere a cada volta e a sessão\n // do WhatsApp pode estar religando.\n return Number.isFinite(age) && age > 120_000\n}\n\n/** O carimbo ao lado da hora. Some nas de entrada: elas chegaram. */\nexport function DeliveryMark({ m }: { m: Message }) {\n if (m.direction !== 'outbound') return null\n const key = isFailed(m) ? 'failed' : (m.deliveryStatus ?? 'sent')\n const cfg = STATUS[key] ?? STATUS.sent\n const Icon = cfg.icon\n return (\n <span className={`inline-flex items-center gap-0.5 ${cfg.cls}`} title={cfg.label}>\n <Icon className=\"h-3 w-3\" />\n {BAD.has(key) && <span className=\"text-[9px] font-semibold uppercase\">{cfg.label}</span>}\n </span>\n )\n}\n\nexport function MessageMenu({ m }: { m: Message }) {\n const [open, setOpen] = React.useState(false)\n const [detail, setDetail] = React.useState<unknown>(null)\n const [loading, setLoading] = React.useState(false)\n const [err, setErr] = React.useState<string | null>(null)\n\n async function load() {\n setOpen(true)\n if (!m.providerMessageId) return\n setLoading(true); setErr(null)\n try {\n const sb = getSupabaseClientOptional() as any\n const { data, error } = await sb.functions.invoke('conversations-provider', {\n body: { tenantId: getActiveTenantId(), action: 'message_detail', providerMessageId: m.providerMessageId },\n })\n if (error) {\n const d = (await error.context?.json?.().catch(() => null)) as { message?: string } | null\n throw new Error(d?.message ?? error.message)\n }\n if ((data as any)?.error) throw new Error((data as any).message ?? (data as any).error)\n setDetail((data as any).detail)\n } catch (e: any) {\n setErr(e?.message ?? 'Não consegui buscar')\n } finally {\n setLoading(false)\n }\n }\n\n return (\n <>\n <button\n onClick={() => void load()}\n aria-label=\"Detalhes da mensagem\"\n className=\"opacity-0 transition-opacity group-hover:opacity-60 hover:!opacity-100\"\n >\n <MoreVertical className=\"h-3.5 w-3.5\" />\n </button>\n\n {open && (\n <div className=\"fixed inset-0 z-[120] flex items-center justify-center bg-black/50 p-4\" onClick={() => setOpen(false)}>\n <div className=\"max-h-[80vh] w-full max-w-2xl overflow-hidden rounded-card bg-card shadow-2xl\" onClick={(e) => e.stopPropagation()}>\n <div className=\"flex items-center justify-between border-b px-4 py-3\">\n <div>\n <p className=\"text-sm font-semibold\">Detalhes da mensagem</p>\n <p className=\"text-[11px] text-muted-foreground\">\n {new Date(m.at).toLocaleString()} · {m.direction === 'outbound' ? 'saída' : 'entrada'}\n {m.deliveryStatus ? ` · ${m.deliveryStatus}` : ''}\n </p>\n </div>\n <button onClick={() => setOpen(false)} aria-label=\"Fechar\"><X className=\"h-4 w-4\" /></button>\n </div>\n\n <div className=\"max-h-[60vh] overflow-auto p-4 text-xs\">\n <p className=\"mb-1 font-medium text-muted-foreground\">Nesta base</p>\n <pre className=\"mb-4 overflow-auto rounded bg-muted/40 p-3 text-[11px] leading-relaxed\">\n{JSON.stringify({ id: m.id, direction: m.direction, body: m.body, at: m.at,\n author: m.author, deliveryStatus: m.deliveryStatus,\n providerMessageId: m.providerMessageId }, null, 2)}\n </pre>\n\n <p className=\"mb-1 font-medium text-muted-foreground\">No provedor</p>\n {!m.providerMessageId && (\n // A ausência é a informação: sem id, ninguém do outro lado\n // chegou a aceitar esta mensagem. Ela não atrasou — ela não saiu.\n <p className=\"rounded bg-destructive-soft p-3 text-[11px] text-destructive-soft-foreground\">\n Esta mensagem não tem id no provedor — ela nunca chegou a ser aceita para envio.\n Não há o que buscar do outro lado.\n </p>\n )}\n {loading && <p className=\"flex items-center gap-2 p-3 text-muted-foreground\"><Loader2 className=\"h-3 w-3 animate-spin\" /> buscando…</p>}\n {err && <p className=\"rounded bg-destructive-soft p-3 text-[11px] text-destructive-soft-foreground\">{err}</p>}\n {detail != null && (\n <pre className=\"overflow-auto rounded bg-muted/40 p-3 text-[11px] leading-relaxed\">\n{JSON.stringify(detail, null, 2)}\n </pre>\n )}\n </div>\n </div>\n </div>\n )}\n </>\n )\n}\n","import React from 'react'\nimport { useSendWindow, SendWindowBanner } from './SendWindow'\nimport { DeliveryMark, MessageMenu, isFailed } from './MessageInspector'\nimport { Send, Clock, Archive, PanelRight, MessageSquare, ChevronLeft } from 'lucide-react'\nimport { Button, cn } from '@fayz-ai/ui'\nimport { useTranslation } from '@fayz-ai/core'\nimport { useConversationsStore } from '../context'\nimport { CHANNEL_ACCENT, CHANNEL_LABELS } from '../lib/channel'\nimport type { Conversation, Message } from '../types'\nimport { Avatar, ChannelBadge, dayLabel } from './shared'\n\n// Build a flat render list: day-separator rows interleaved with messages, and\n// each message flagged as the start of a new sender-run (WhatsApp grouping).\ntype Row =\n | { kind: 'day'; id: string; label: string }\n | { kind: 'msg'; id: string; message: Message; startsRun: boolean; endsRun: boolean }\n\nfunction buildRows(messages: Message[]): Row[] {\n const rows: Row[] = []\n let lastDay = ''\n messages.forEach((m, i) => {\n const day = new Date(m.at).toDateString()\n if (day !== lastDay) {\n rows.push({ kind: 'day', id: `day-${day}`, label: dayLabel(m.at) })\n lastDay = day\n }\n const prev = messages[i - 1]\n const next = messages[i + 1]\n const samePrev = prev && prev.direction === m.direction && new Date(prev.at).toDateString() === day\n const sameNext = next && next.direction === m.direction && new Date(next.at).toDateString() === day\n rows.push({ kind: 'msg', id: m.id, message: m, startsRun: !samePrev, endsRun: !sameNext })\n })\n return rows\n}\n\nexport function MessageThread({ selected, onTogglePanel, panelOpen, onBack, className }: {\n selected: Conversation\n onTogglePanel: () => void\n panelOpen: boolean\n onBack?: () => void\n className?: string\n}) {\n const t = useTranslation()\n const { messages, sending, send, setStatus, refreshMessages } = useConversationsStore((s) => s)\n const [draft, setDraft] = React.useState('')\n // Se dá para falar com esta pessoa AGORA, e por qual caminho. Lê a janela de\n // 24h da conversa E a capacidade do provedor ativo: o mesmo campo de texto é\n // seguro num provedor e mentiroso no outro.\n const win = useSendWindow(selected.id)\n const canType = win.mode.kind === 'free'\n const threadRef = React.useRef<HTMLDivElement>(null)\n\n React.useEffect(() => {\n threadRef.current?.scrollTo({ top: threadRef.current.scrollHeight, behavior: 'smooth' })\n }, [messages.length, selected.id])\n\n // O tique duplo chega pelo webhook, no banco, sem passar por aqui. Dez\n // segundos é o intervalo que faz \"entregue\" e \"lida\" aparecerem enquanto a\n // pessoa ainda olha a conversa, e é barato: uma consulta por thread aberta.\n //\n // Para quando a aba sai de foco. Uma caixa esquecida aberta a noite toda não\n // deve consultar 8.640 vezes por nada.\n React.useEffect(() => {\n if (!selected.id) return\n let timer: ReturnType<typeof setInterval> | null = null\n const start = () => { if (!timer) timer = setInterval(() => { void refreshMessages() }, 10_000) }\n const stop = () => { if (timer) { clearInterval(timer); timer = null } }\n const onVis = () => (document.visibilityState === 'visible' ? start() : stop())\n onVis()\n document.addEventListener('visibilitychange', onVis)\n return () => { stop(); document.removeEventListener('visibilitychange', onVis) }\n }, [selected.id, refreshMessages])\n\n async function handleSend() {\n if (!draft.trim()) return\n const body = draft\n setDraft('')\n await send(body)\n }\n\n const accent = CHANNEL_ACCENT[selected.channel]\n const rows = React.useMemo(() => buildRows(messages), [messages])\n\n return (\n <section className={cn('flex min-w-0 flex-1 flex-col bg-muted/20', className)}>\n {/* Header */}\n <div className=\"flex items-center justify-between gap-2 border-b border-border bg-card px-3 py-2.5 md:px-5\">\n <div className=\"flex min-w-0 items-center gap-2 md:gap-3\">\n {onBack && (\n <Button variant=\"ghost\" size=\"icon\" className=\"-ml-1 shrink-0 lg:hidden\" onClick={onBack} aria-label={t('conversations.thread.back')}>\n <ChevronLeft className=\"h-5 w-5\" />\n </Button>\n )}\n <Avatar name={selected.contactName} accent={selected.accent} size=\"sm\" channel={selected.channel} />\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-semibold text-foreground\">{selected.contactName}</p>\n <p className=\"flex items-center gap-1.5 text-xs text-muted-foreground\">\n <ChannelBadge channel={selected.channel} />\n <span className=\"truncate\">{selected.contactHandle}</span>\n </p>\n </div>\n </div>\n <div className=\"flex shrink-0 items-center gap-1.5\">\n {/* ABRIR O LEAD, no lugar de \"Adiar\" e \"Encerrar\".\n Aqueles dois são vocabulário de suporte — uma fila de chamados que\n se despacha. Aqui a conversa é uma VENDA em andamento: ninguém\n \"encerra\" um lead que respondeu, e adiar é o que a fila da rota já\n faz com data. O que faltava era o caminho da conversa para a\n ficha, e ele não existia em lugar nenhum da tela. */}\n <Button variant=\"outline\" size=\"sm\" onClick={() => setStatus('closed')} aria-label=\"Arquivar\">\n <Archive className=\"h-3.5 w-3.5 sm:mr-1\" /> <span className=\"hidden sm:inline\">Arquivar</span>\n </Button>\n <Button\n variant={panelOpen ? 'secondary' : 'ghost'}\n size=\"icon\"\n onClick={onTogglePanel}\n aria-label={t('conversations.thread.details')}\n >\n <PanelRight className=\"h-4 w-4\" />\n </Button>\n </div>\n </div>\n\n {/* Messages */}\n <div ref={threadRef} className=\"min-h-0 flex-1 overflow-y-auto px-5 py-4\">\n {rows.map((row) => {\n if (row.kind === 'day') {\n return (\n <div key={row.id} className=\"my-3 flex items-center justify-center\">\n <span className=\"rounded-full bg-muted px-3 py-0.5 text-[11px] font-medium text-muted-foreground\">\n {row.label}\n </span>\n </div>\n )\n }\n const m = row.message\n const outbound = m.direction === 'outbound'\n const failed = isFailed(m)\n return (\n <div\n key={row.id}\n className={cn('group flex items-center gap-1', outbound ? 'justify-end' : 'justify-start', row.startsRun ? 'mt-2.5' : 'mt-0.5')}\n >\n {/* O menu fica FORA do balão e do lado de dentro da conversa, para\n não empurrar o texto nem cobrir a hora. Aparece no hover. */}\n {outbound && <MessageMenu m={m} />}\n <div\n className={cn(\n 'max-w-[68%] px-3.5 py-2 text-sm shadow-sm',\n outbound ? 'rounded-2xl text-white' : 'rounded-2xl bg-card text-foreground',\n // Tail only on the last bubble of a run, on the sender's side.\n outbound && row.endsRun && 'rounded-br-sm',\n !outbound && row.endsRun && 'rounded-bl-sm',\n // UMA MENSAGEM QUE FALHOU PRECISA PARECER QUE FALHOU. Duas\n // ficaram um dia na caixa com a cara de qualquer outra e nunca\n // chegaram a ninguém — não havia como saber olhando.\n outbound && failed && 'ring-2 ring-destructive',\n )}\n style={outbound ? { backgroundColor: failed ? 'hsl(var(--destructive))' : accent.color } : undefined}\n >\n <p className=\"whitespace-pre-wrap break-words\">{m.body}</p>\n <div className={cn('mt-0.5 flex items-center justify-end gap-1 text-[10px]', outbound ? 'text-white/70' : 'text-muted-foreground')}>\n {new Date(m.at).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}\n <DeliveryMark m={m} />\n </div>\n </div>\n {!outbound && <MessageMenu m={m} />}\n </div>\n )\n })}\n {rows.length === 0 && (\n <div className=\"flex h-full flex-col items-center justify-center text-muted-foreground\">\n <MessageSquare className=\"h-7 w-7\" />\n <p className=\"mt-2 text-sm\">{t('conversations.thread.empty')}</p>\n </div>\n )}\n </div>\n\n {/* Composer */}\n {/* TODO(follow-up): wire real emoji picker + attachment upload (removed the\n dead placeholder buttons rather than shipping non-functional UI). */}\n <div className=\"border-t border-border bg-card\">\n <SendWindowBanner state={win} />\n <div className=\"flex items-end gap-2 px-4 py-3\">\n <textarea\n value={draft}\n onChange={(e) => setDraft(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault()\n void handleSend()\n }\n }}\n rows={1}\n disabled={!canType}\n placeholder={\n canType\n ? t('conversations.thread.reply', { channel: CHANNEL_LABELS[selected.channel] })\n : win.mode.kind === 'blocked'\n ? 'Envio bloqueado'\n : 'Fora da janela — envie um template'\n }\n className=\"max-h-32 min-h-[40px] flex-1 resize-none rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground outline-none focus:border-primary disabled:cursor-not-allowed disabled:bg-muted/40 disabled:text-muted-foreground\"\n />\n <Button onClick={() => void handleSend()} disabled={!canType || sending || !draft.trim()} aria-label={t('conversations.thread.send')}>\n <Send className=\"h-4 w-4\" />\n </Button>\n </div>\n </div>\n </section>\n )\n}\n","import React from 'react'\nimport { ExternalLink, Flame, Snowflake, Sun, Building2 } from 'lucide-react'\nimport { getSupabaseClientOptional, getActiveTenantId } from '@fayz-ai/core'\n\n/**\n * O lead, dentro da conversa.\n *\n * ── por que estes campos e não a ficha inteira ──────────────────────────\n *\n * Quem está respondendo tem uma pergunta só: \"com quem eu estou falando, e isto\n * é importante?\". A ficha inteira responde isso e mais quarenta coisas, e o\n * custo de ler quarenta é não ler nenhuma.\n *\n * Então: nome, empresa, temperatura, e os motivos que a produziram. O resto\n * está a um clique — e o clique existe, que é o outro defeito que isto conserta:\n * dava para ver o telefone do lead na conversa e não dava para chegar nele.\n *\n * ── sem import entre plugins ────────────────────────────────────────────\n *\n * Isto vive no plugin de conversas e lê tabelas do CRM. Não importa nada de\n * `plugin-crm`: as tabelas são do schema público com RLS por inquilino, e um\n * import amarraria a caixa de entrada a um plugin que pode não estar montado.\n * Mesmo princípio do `TalkToPerson`, que faz o caminho inverso.\n */\n\ninterface LeadBrief {\n id: string\n name: string\n company: string | null\n status: string | null\n sourceName: string | null\n temperature: 'hot' | 'warm' | 'cold' | null\n fitScore: number | null\n interestScore: number | null\n reasons: string[]\n}\n\nconst TEMP: Record<string, { icon: typeof Flame; cls: string; label: string }> = {\n hot: { icon: Flame, cls: 'bg-destructive-soft text-destructive-soft-foreground', label: 'Quente' },\n warm: { icon: Sun, cls: 'bg-warning-soft text-warning-soft-foreground', label: 'Morno' },\n cold: { icon: Snowflake, cls: 'bg-info-soft text-info-soft-foreground', label: 'Frio' },\n}\n\nexport function LeadCard({ personId, handle }: { personId?: string | null; handle?: string | null }) {\n const [lead, setLead] = React.useState<LeadBrief | null | undefined>(undefined)\n\n React.useEffect(() => {\n let alive = true\n const sb = getSupabaseClientOptional() as any\n const tenantId = getActiveTenantId()\n if (!sb || !tenantId || (!personId && !handle)) { setLead(null); return }\n\n ;(async () => {\n try {\n let q = sb.from('people')\n .select('id, name, metadata, phone')\n .eq('tenant_id', tenantId).eq('kind', 'lead').limit(1)\n // Pelo id quando a conversa já está vinculada; pelo telefone quando\n // não — e por DÍGITOS, porque o mesmo número aparece em três grafias.\n q = personId\n ? q.eq('id', personId)\n : q.like('phone', `%${String(handle).replace(/\\D/g, '').slice(-8)}`)\n const { data } = await q.maybeSingle()\n if (!alive) return\n if (!data) { setLead(null); return }\n\n const meta = (data.metadata ?? {}) as any\n let score: any = null\n try {\n const { data: s } = await sb.from('v_crm_lead_score')\n .select('temperature, fit_score, interest_score, reasons')\n .eq('lead_id', data.id).maybeSingle()\n score = s\n } catch { /* sem nota, o cartão mostra o resto */ }\n\n setLead({\n id: data.id, name: data.name,\n company: meta.company ?? null,\n status: meta.status ?? null,\n sourceName: meta.sourceName ?? null,\n temperature: score?.temperature ?? null,\n fitScore: score?.fit_score ?? null,\n interestScore: score?.interest_score ?? null,\n reasons: Array.isArray(score?.reasons) ? score.reasons : [],\n })\n } catch { if (alive) setLead(null) }\n })()\n return () => { alive = false }\n }, [personId, handle])\n\n if (lead === undefined) return <div className=\"px-4 py-3 text-xs text-muted-foreground\">carregando…</div>\n if (!lead) {\n return (\n <div className=\"border-t border-border px-4 py-3\">\n <p className=\"text-xs text-muted-foreground\">\n Este contato ainda não é um lead cadastrado.\n </p>\n </div>\n )\n }\n\n const t = lead.temperature ? TEMP[lead.temperature] : null\n const Icon = t?.icon\n\n return (\n <div className=\"border-t border-border px-4 py-3\">\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-semibold\">{lead.name}</p>\n {lead.company && (\n <p className=\"flex items-center gap-1 truncate text-xs text-muted-foreground\">\n <Building2 className=\"h-3 w-3 shrink-0\" /> {lead.company}\n </p>\n )}\n </div>\n {/* O caminho que faltava: da conversa para a ficha. */}\n <a\n href={`#/sales/leads/${lead.id}`}\n className=\"inline-flex shrink-0 items-center gap-1 rounded-button border bg-card px-2 py-1 text-[11px] font-medium hover:bg-muted\"\n >\n Abrir lead <ExternalLink className=\"h-3 w-3\" />\n </a>\n </div>\n\n <div className=\"mt-2 flex flex-wrap items-center gap-1.5\">\n {t && Icon && (\n <span className={`inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-[10px] font-medium ${t.cls}`}>\n <Icon className=\"h-3 w-3\" /> {t.label}\n {lead.interestScore != null && <span className=\"tabular-nums opacity-70\">{lead.interestScore}</span>}\n </span>\n )}\n {lead.status && (\n <span className=\"rounded-full bg-muted px-2 py-0.5 text-[10px] capitalize text-muted-foreground\">{lead.status}</span>\n )}\n {lead.sourceName && (\n <span className=\"rounded-full bg-muted px-2 py-0.5 text-[10px] text-muted-foreground\">{lead.sourceName}</span>\n )}\n </div>\n\n {/* O PORQUÊ da temperatura. Sem ele o chip é um número que ninguém\n confere, e quem está respondendo não sabe o que puxar na conversa. */}\n {lead.reasons.length > 0 && (\n <ul className=\"mt-2 space-y-0.5\">\n {lead.reasons.slice(0, 4).map((r, i) => (\n <li key={i} className=\"flex gap-1.5 text-[11px] text-muted-foreground\">\n <span className=\"text-muted-foreground/40\">•</span><span>{r}</span>\n </li>\n ))}\n </ul>\n )}\n </div>\n )\n}\n","import React from 'react'\nimport { LeadCard } from './LeadCard'\nimport { MapPin, User, Tag, Link2, StickyNote, X } from 'lucide-react'\nimport { Button, cn } from '@fayz-ai/ui'\nimport { useTranslation } from '@fayz-ai/core'\nimport { CHANNEL_LABELS } from '../lib/channel'\nimport type { Conversation } from '../types'\nimport { Avatar, ChannelBadge } from './shared'\n\nfunction Section({ icon: Icon, title, children }: {\n icon: React.ComponentType<{ className?: string }>\n title: string\n children: React.ReactNode\n}) {\n return (\n <div className=\"border-t border-border px-4 py-3\">\n <p className=\"mb-1.5 flex items-center gap-1.5 text-[11px] font-semibold uppercase tracking-wide text-muted-foreground\">\n <Icon className=\"h-3 w-3\" /> {title}\n </p>\n {children}\n </div>\n )\n}\n\nexport function ContactPanel({ contact, onClose, className }: {\n contact: Conversation\n onClose?: () => void\n className?: string\n}) {\n const t = useTranslation()\n return (\n <aside className={cn('flex shrink-0 flex-col overflow-y-auto border-l border-border bg-card', className)}>\n {onClose && (\n <div className=\"flex items-center justify-between border-b border-border px-3 py-2 xl:hidden\">\n <span className=\"text-sm font-semibold text-foreground\">{t('conversations.contact.details')}</span>\n <Button variant=\"ghost\" size=\"icon\" onClick={onClose} aria-label={t('conversations.contact.closeDetails')}>\n <X className=\"h-4 w-4\" />\n </Button>\n </div>\n )}\n <div className=\"flex flex-col items-center gap-2 px-4 py-5 text-center\">\n <Avatar name={contact.contactName} accent={contact.accent} size=\"lg\" channel={contact.channel} />\n <div>\n <p className=\"text-sm font-semibold text-foreground\">{contact.contactName}</p>\n <p className=\"text-xs text-muted-foreground\">{contact.contactHandle}</p>\n </div>\n <ChannelBadge channel={contact.channel} />\n </div>\n\n {/* O LEAD PRIMEIRO. \"Canal / Status / Responsável\" descrevem a\n conversa; quem está respondendo precisa saber de QUEM ela é — e\n precisa do caminho para a ficha, que não existia. */}\n <LeadCard personId={contact.contactPersonId} handle={contact.contactHandle} />\n\n <Section icon={User} title={t('conversations.contact.details')}>\n <dl className=\"space-y-1.5 text-sm\">\n <div className=\"flex items-center justify-between gap-2\">\n <dt className=\"text-muted-foreground\">{t('conversations.contact.channel')}</dt>\n <dd className=\"text-foreground\">{CHANNEL_LABELS[contact.channel]}</dd>\n </div>\n <div className=\"flex items-center justify-between gap-2\">\n <dt className=\"text-muted-foreground\">{t('conversations.contact.status')}</dt>\n <dd className=\"text-foreground\">{t(`conversations.status.${contact.status}`)}</dd>\n </div>\n {contact.assignedTo && (\n <div className=\"flex items-center justify-between gap-2\">\n <dt className=\"text-muted-foreground\">{t('conversations.contact.assignedTo')}</dt>\n <dd className=\"text-foreground\">{contact.assignedTo}</dd>\n </div>\n )}\n {contact.location && (\n <div className=\"flex items-center justify-between gap-2\">\n <dt className=\"flex items-center gap-1 text-muted-foreground\"><MapPin className=\"h-3 w-3\" /> {t('conversations.contact.location')}</dt>\n <dd className=\"text-foreground\">{contact.location}</dd>\n </div>\n )}\n </dl>\n </Section>\n\n {contact.tags.length > 0 && (\n <Section icon={Tag} title={t('conversations.contact.tags')}>\n <div className=\"flex flex-wrap gap-1.5\">\n {contact.tags.map((tag) => (\n <span key={tag} className=\"rounded-full bg-muted px-2 py-0.5 text-xs font-medium text-foreground\">\n {tag}\n </span>\n ))}\n </div>\n </Section>\n )}\n\n {contact.note && (\n <Section icon={StickyNote} title={t('conversations.contact.note')}>\n <p className=\"text-sm text-foreground\">{contact.note}</p>\n </Section>\n )}\n\n <Section icon={Link2} title={t('conversations.contact.linkedRecords')}>\n <p className=\"text-xs text-muted-foreground\">{t('conversations.contact.noLinkedRecords')}</p>\n </Section>\n </aside>\n )\n}\n","import { errorMessage } from '@fayz-ai/core'\nimport React from 'react'\nimport { Button, cn, toast } from '@fayz-ai/ui'\nimport { useTranslation } from '@fayz-ai/core'\nimport {\n useLimitGuard, invalidateLimit, ContactPicker, RightRailPage,\n type ContactPickerValue,\n} from '@fayz-ai/admin'\nimport { useConversationsStore, useConversationsConfig } from '../context'\nimport { CHANNEL_ACCENT, CHANNEL_ICON, CHANNEL_LABELS } from '../lib/channel'\nimport type { Channel } from '../types'\n\nconst CHANNELS: Channel[] = ['whatsapp', 'sms', 'instagram', 'email', 'webchat']\n\n/**\n * Which field of a person IS the handle on a given channel. Phone channels read\n * the phone, email reads the email; Instagram and web chat have no counterpart\n * on a person record, so they always have to be typed. Deliberately NOT\n * cross-filling (an email in a WhatsApp handle looked plausible on screen and\n * was simply wrong).\n */\nfunction personHandleFor(channel: Channel, contact: ContactPickerValue | null): string {\n if (!contact) return ''\n if (channel === 'email') return contact.email ?? ''\n if (channel === 'sms' || channel === 'whatsapp') return contact.phone ?? ''\n return ''\n}\n\nconst HANDLE_LABEL_KEY: Record<Channel, string> = {\n whatsapp: 'conversations.new.handleLabel.phone',\n sms: 'conversations.new.handleLabel.phone',\n email: 'conversations.new.handleLabel.email',\n instagram: 'conversations.new.handleLabel.instagram',\n webchat: 'conversations.new.handleLabel.webchat',\n}\n\n/**\n * Starting a conversation, in the right column.\n *\n * It was a dialog over the inbox — over the very list you read to decide\n * whether a thread already exists. Beside it, that list stays legible while the\n * contact is chosen.\n */\nexport function NewConversationPanel({\n open,\n onOpenChange,\n}: {\n open: boolean\n onOpenChange: (open: boolean) => void\n}) {\n const t = useTranslation()\n const create = useConversationsStore((s) => s.create)\n const config = useConversationsConfig()\n const guardConversations = useLimitGuard('conversations_month')\n\n const [channel, setChannel] = React.useState<Channel>('whatsapp')\n const [contact, setContact] = React.useState<ContactPickerValue | null>(null)\n // Handle the user typed themselves. The one taken FROM the contact is derived\n // (see personHandleFor) and shown on the picker's chip — same as the agenda,\n // which never had a second phone field.\n const [typedHandle, setTypedHandle] = React.useState('')\n // While the picker's inline create form is open it already asks for phone and\n // email, so showing our own handle field would ask for the phone twice.\n const [creatingContact, setCreatingContact] = React.useState(false)\n const [firstMessage, setFirstMessage] = React.useState('')\n const [submitting, setSubmitting] = React.useState(false)\n\n // Reset the form each time the modal opens. `pickerKey` remounts the picker so\n // its internal search text resets too.\n const [pickerKey, setPickerKey] = React.useState(0)\n React.useEffect(() => {\n if (open) {\n setChannel('whatsapp')\n setContact(null)\n setTypedHandle('')\n setCreatingContact(false)\n setFirstMessage('')\n setSubmitting(false)\n setPickerKey((k) => k + 1)\n }\n }, [open])\n\n // What we'd message on this channel: the contact's own datum when they have\n // one, otherwise whatever the user typed. Recomputed on every channel switch,\n // so flipping WhatsApp → Email swaps phone for email with no stale state.\n const derivedHandle = personHandleFor(channel, contact)\n const effectiveHandle = derivedHandle || typedHandle\n const handleLabel = t(HANDLE_LABEL_KEY[channel])\n\n const canSubmit = (contact?.name.trim().length ?? 0) > 0 && !submitting\n\n async function handleSubmit(e: React.FormEvent) {\n e.preventDefault()\n if (!canSubmit) return\n setSubmitting(true)\n try {\n // Plan quantity guard (client-side, before the store call): opens the\n // global UpgradeModal and aborts when the monthly cap is reached. Inside\n // the try on purpose — it hits the network to count usage, and a rejection\n // out here used to escape unhandled, leaving the modal open with no\n // feedback at all (indistinguishable from a dead button).\n if ((await guardConversations()) === 'blocked') return\n await create({\n channel,\n contactName: contact!.name.trim(),\n contactPersonId: contact?.id,\n contactHandle: effectiveHandle.trim() || undefined,\n firstMessage: firstMessage.trim() || undefined,\n })\n invalidateLimit('conversations_month')\n onOpenChange(false)\n } catch (err) {\n // A failed insert (unresolved tenant, RLS rejection, offline…) previously\n // threw past the `finally` — `onOpenChange(false)` never ran, so the modal\n // stayed open with no feedback and the thread silently vanished. Surface\n // the failure and keep the form open so the user can retry.\n const message = errorMessage(err)\n toast.error(t('conversations.new.createFailed'), { description: message })\n } finally {\n setSubmitting(false)\n }\n }\n\n return (\n <RightRailPage\n title={t('conversations.new.title')}\n open={open}\n onClose={() => onOpenChange(false)}\n width=\"max-w-md\"\n >\n <form onSubmit={handleSubmit} className=\"flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto px-5 py-4\">\n {/* Channel picker */}\n <div>\n <label className=\"mb-1.5 block text-xs font-medium text-muted-foreground\">\n {t('conversations.new.channel')}\n </label>\n <div className=\"flex flex-wrap gap-1.5\">\n {CHANNELS.map((ch) => {\n const Icon = CHANNEL_ICON[ch]\n const active = channel === ch\n return (\n <button\n key={ch}\n type=\"button\"\n onClick={() => setChannel(ch)}\n aria-pressed={active}\n className={cn(\n 'inline-flex items-center gap-1.5 rounded-full px-3 py-1.5 text-xs font-medium transition-colors',\n active\n ? 'text-white'\n : 'bg-muted text-muted-foreground hover:bg-muted/70',\n )}\n style={active ? { backgroundColor: CHANNEL_ACCENT[ch].color } : undefined}\n >\n <Icon className=\"h-3 w-3\" />\n {CHANNEL_LABELS[ch]}\n </button>\n )\n })}\n </div>\n </div>\n\n {/* Contact — shared find-or-create flow (@fayz-ai/admin), the same one\n the agenda uses to pick a client. The picker owns its label, the\n search field, the chip and the channel handle, so this composer and\n the appointment modal render the SAME thing from the same code. */}\n <ContactPicker\n key={pickerKey}\n value={contact}\n onChange={setContact}\n kind={config.contactKind}\n lookup={config.contactLookup}\n createEntityDef={config.contactEntityDef}\n allowFreeText\n onCreatingChange={setCreatingContact}\n autoFocus\n label={t('conversations.new.contactName')}\n placeholder={t('conversations.new.contactNamePlaceholder')}\n secondaryText={derivedHandle || undefined}\n handleField={{\n label: handleLabel,\n derived: derivedHandle || undefined,\n value: typedHandle,\n onChange: setTypedHandle,\n fieldLabel: t('conversations.new.handle'),\n placeholder: t('conversations.new.handlePlaceholder'),\n }}\n />\n\n {/* First message */}\n <div>\n <label htmlFor=\"conv-first-message\" className=\"mb-1.5 block text-xs font-medium text-muted-foreground\">\n {t('conversations.new.firstMessage')}\n </label>\n <textarea\n id=\"conv-first-message\"\n value={firstMessage}\n onChange={(e) => setFirstMessage(e.target.value)}\n rows={3}\n placeholder={t('conversations.new.firstMessagePlaceholder')}\n className=\"max-h-40 min-h-[64px] w-full resize-none rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground outline-none focus:border-primary\"\n />\n </div>\n\n {/* No Cancel: the column header already holds the way out. */}\n <div className=\"mt-auto pt-2\">\n <Button type=\"submit\" disabled={!canSubmit} className=\"w-full\">\n {submitting ? t('conversations.new.creating') : t('conversations.new.create')}\n </Button>\n </div>\n </form>\n </RightRailPage>\n )\n}\n","import React from 'react'\nimport { MessageSquare, SquarePen } from 'lucide-react'\nimport { Button, PageHeaderActions, cn } from '@fayz-ai/ui'\nimport { PermissionGate } from '@fayz-ai/admin'\nimport { useTranslation } from '@fayz-ai/core'\nimport { useConversationsStore } from '../context'\nimport { ConversationList } from './ConversationList'\nimport { MessageThread } from './MessageThread'\nimport { ContactPanel } from './ContactPanel'\nimport { NewConversationPanel } from './NewConversationPanel'\nimport { useMediaQuery } from './shared'\n\nexport function InboxView() {\n const t = useTranslation()\n const { conversations, selectedId, deselect } = useConversationsStore((s) => s)\n // xl+ shows the contact panel inline (three panes); below that it's an\n // on-demand slide-over so the list + thread keep room to breathe.\n const isWidePanel = useMediaQuery('(min-width: 1280px)')\n const [panelOpen, setPanelOpen] = React.useState(false)\n const [newOpen, setNewOpen] = React.useState(false)\n const selected = conversations.find((c) => c.id === selectedId) ?? null\n\n // Open the panel by default only when it fits inline; collapse it otherwise.\n React.useEffect(() => {\n setPanelOpen(isWidePanel)\n }, [isWidePanel])\n\n return (\n <>\n {/* Outside the inbox row: with a header slot this portals and takes no\n space; without one it falls in as a block above the screen, rather\n than a column wedged between the list and the thread. */}\n <PermissionGate feature=\"conversations\" action=\"create\">\n <PageHeaderActions>\n <Button\n size=\"sm\"\n onClick={() => setNewOpen(true)}\n aria-label={t('conversations.list.new')}\n data-testid=\"conversations-new\"\n >\n <SquarePen className=\"h-3.5 w-3.5 sm:mr-1.5\" />\n <span className=\"hidden sm:inline\">{t('conversations.list.new')}</span>\n </Button>\n </PageHeaderActions>\n </PermissionGate>\n\n <NewConversationPanel open={newOpen} onOpenChange={setNewOpen} />\n\n <div className=\"relative flex h-full min-h-0 overflow-hidden bg-background\">\n {/* Conversation list — single-pane below lg (full width), fixed rail at lg+.\n Below lg it yields to the thread once a conversation is open. */}\n <ConversationList className={cn(selected ? 'hidden lg:flex' : 'flex')} />\n\n {/* Thread — hidden below lg until a conversation is selected. */}\n {selected ? (\n <MessageThread\n selected={selected}\n panelOpen={panelOpen}\n onTogglePanel={() => setPanelOpen((v) => !v)}\n onBack={deselect}\n className=\"flex\"\n />\n ) : (\n <section className=\"hidden min-w-0 flex-1 flex-col items-center justify-center bg-muted/20 text-muted-foreground lg:flex\">\n <MessageSquare className=\"h-9 w-9\" />\n <p className=\"mt-2 text-sm\">{t('conversations.empty.select')}</p>\n </section>\n )}\n\n {/* Contact panel — inline column at xl, slide-over overlay below xl. */}\n {selected && panelOpen && (\n <>\n <button\n className=\"absolute inset-0 z-10 bg-black/40 xl:hidden\"\n aria-label=\"Close details\"\n onClick={() => setPanelOpen(false)}\n />\n <ContactPanel\n contact={selected}\n onClose={() => setPanelOpen(false)}\n className=\"absolute inset-y-0 right-0 z-20 w-[340px] max-w-[88%] shadow-2xl xl:static xl:z-auto xl:w-[300px] xl:max-w-none xl:shadow-none\"\n />\n </>\n )}\n </div>\n </>\n )\n}\n","import React from 'react'\nimport type { StoreApi } from 'zustand/vanilla'\nimport { ConversationsContextProvider, type ResolvedConversationsConfig } from '../context'\nimport type { ConversationsUIState } from '../store'\nimport type { Channel } from '../types'\nimport { InboxView } from './InboxView'\n\nconst CHANNELS: Channel[] = ['sms', 'whatsapp', 'instagram', 'email', 'webchat']\n\n/** Quem o link de entrada pediu para abrir.\n *\n * A rota da shell é por hash, então os parâmetros vêm depois do `?` DENTRO do\n * hash — `#/conversations?personId=…`. `window.location.search` está vazio\n * aqui, e foi o primeiro lugar onde procurei.\n */\nfunction requestedContact(): { personId?: string; name: string; handle: string; channel: Channel } | null {\n try {\n const hash = window.location.hash\n const qs = hash.includes('?') ? hash.slice(hash.indexOf('?') + 1) : ''\n if (!qs) return null\n const p = new URLSearchParams(qs)\n const handle = (p.get('handle') ?? '').trim()\n const name = (p.get('name') ?? '').trim()\n const personId = (p.get('personId') ?? '').trim() || undefined\n // Sem por onde falar não há conversa para abrir. Criar uma thread sem\n // handle produziria um registro que nunca entrega nada.\n if (!handle) return null\n const raw = (p.get('channel') ?? 'whatsapp').toLowerCase()\n const channel = (CHANNELS as string[]).includes(raw) ? (raw as Channel) : 'whatsapp'\n return { personId, name: name || handle, handle, channel }\n } catch {\n return null\n }\n}\n\nexport function ConversationsPage({ store, config }: {\n store: StoreApi<ConversationsUIState>\n config?: ResolvedConversationsConfig\n}) {\n React.useEffect(() => {\n const wanted = requestedContact()\n if (!wanted) {\n void store.getState().load()\n return\n }\n\n // `openForPerson` carrega a caixa sozinho quando precisa, então não há\n // `load()` aqui: os dois juntos disputariam a mesma lista e a thread recém\n // aberta poderia ser substituída pela resposta mais lenta.\n void store.getState().openForPerson(wanted).catch(() => {\n void store.getState().load()\n })\n\n // O parâmetro é uma ORDEM, não um estado: cumprida uma vez, sai da barra de\n // endereços. Sem isto, um F5 meia hora depois reabriria — ou pior,\n // recriaria — a conversa daquela pessoa, e o link ficaria colado no\n // histórico do navegador.\n try {\n const hash = window.location.hash\n const clean = hash.includes('?') ? hash.slice(0, hash.indexOf('?')) : hash\n window.history.replaceState(null, '', clean || '#/conversations')\n } catch { /* a barra fica como está; a conversa já abriu */ }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n return (\n <ConversationsContextProvider store={store} config={config}>\n <InboxView />\n </ConversationsContextProvider>\n )\n}\n","import type { Channel } from '../types'\n\n// Solid brand hex per channel — mirrors CHANNEL_ACCENT in ../channel.ts but\n// React-free so the data providers can stamp a sensible avatar accent on new\n// conversations without importing the icon layer.\nexport const CHANNEL_ACCENT_HEX: Record<Channel, string> = {\n whatsapp: '#22c55e',\n sms: '#6366f1',\n instagram: '#ec4899',\n email: '#0ea5e9',\n webchat: '#f59e0b',\n}\n","import type { ConversationsProvider } from './types'\nimport { CHANNEL_ACCENT_HEX } from './accents'\nimport type {\n Conversation,\n Message,\n ListConversationsQuery,\n SendMessageInput,\n CreateConversationInput,\n ConversationStatus,\n} from '../types'\n\n// Deterministic relative timestamps (no Date.now at module init for stable seeds).\nfunction minutesAgo(base: number, mins: number): string {\n return new Date(base - mins * 60_000).toISOString()\n}\n\nfunction seed(): { conversations: Conversation[]; messages: Message[] } {\n const base = new Date('2026-06-16T14:00:00Z').getTime()\n\n const conversations: Conversation[] = [\n {\n id: 'c1', contactName: 'Marina Alves', contactHandle: '+55 11 99876-1020', channel: 'whatsapp',\n lastMessagePreview: 'Perfect, can we book for Friday at 3pm?', lastMessageAt: minutesAgo(base, 4),\n lastMessageDirection: 'inbound',\n unreadCount: 2, status: 'open', assignedTo: 'You', accent: '#22c55e', tags: ['Hot lead'],\n location: 'São Paulo, BR', note: 'Referred by Instagram ad — interested in full color + cut.',\n },\n {\n id: 'c2', contactName: 'Jordan Pierce', contactHandle: '+1 (415) 555-0142', channel: 'sms',\n lastMessagePreview: 'Got it — sending the deposit now.', lastMessageAt: minutesAgo(base, 22),\n lastMessageDirection: 'inbound',\n unreadCount: 0, status: 'open', assignedTo: 'You', accent: '#6366f1', tags: ['Customer'],\n location: 'San Francisco, US',\n },\n {\n id: 'c3', contactName: '@thehairloft', contactHandle: 'thehairloft', channel: 'instagram',\n lastMessagePreview: 'Do you offer balayage on weekends?', lastMessageAt: minutesAgo(base, 51),\n lastMessageDirection: 'inbound',\n unreadCount: 1, status: 'open', accent: '#ec4899', tags: ['New'],\n },\n {\n id: 'c4', contactName: 'David Whitman', contactHandle: 'david@whitman.co', channel: 'email',\n lastMessagePreview: 'Re: Proposal — looks great, one question on pricing…', lastMessageAt: minutesAgo(base, 95),\n // Read, never answered — zero unread and a customer still waiting. The\n // exact case `unreadCount` alone gets wrong.\n lastMessageDirection: 'inbound',\n unreadCount: 0, status: 'open', assignedTo: 'Sofia', accent: '#0ea5e9', tags: ['Proposal'],\n location: 'Austin, US', note: 'Evaluating the retainer tier. Decision expected this week.',\n },\n {\n id: 'c5', contactName: 'Website visitor', contactHandle: 'live chat · acme.com', channel: 'webchat',\n lastMessagePreview: 'Is anyone available to chat?', lastMessageAt: minutesAgo(base, 140),\n lastMessageDirection: 'inbound',\n unreadCount: 0, status: 'snoozed', accent: '#f59e0b', tags: [],\n },\n {\n id: 'c6', contactName: 'Priya Nair', contactHandle: '+44 7700 900123', channel: 'whatsapp',\n lastMessagePreview: 'Thank you! See you next week 🙌', lastMessageAt: minutesAgo(base, 1440),\n lastMessageDirection: 'outbound',\n unreadCount: 0, status: 'closed', assignedTo: 'You', accent: '#14b8a6', tags: ['Customer'],\n location: 'London, UK',\n },\n ]\n\n const messages: Message[] = [\n msg('m1', 'c1', 'whatsapp', 'inbound', 'Hi! I saw your ad — do you have availability this week?', 'Marina Alves', minutesAgo(base, 18)),\n msg('m2', 'c1', 'whatsapp', 'outbound', 'Hi Marina! Yes, we do. What service are you interested in?', 'You', minutesAgo(base, 15)),\n msg('m3', 'c1', 'whatsapp', 'inbound', 'A full color + cut.', 'Marina Alves', minutesAgo(base, 9)),\n msg('m4', 'c1', 'whatsapp', 'inbound', 'Perfect, can we book for Friday at 3pm?', 'Marina Alves', minutesAgo(base, 4)),\n\n msg('m5', 'c2', 'sms', 'outbound', 'Your appointment is confirmed for tomorrow at 10am.', 'You', minutesAgo(base, 40)),\n msg('m6', 'c2', 'sms', 'inbound', 'Got it — sending the deposit now.', 'Jordan Pierce', minutesAgo(base, 22)),\n\n msg('m7', 'c3', 'instagram', 'inbound', 'Do you offer balayage on weekends?', '@thehairloft', minutesAgo(base, 51)),\n\n msg('m8', 'c4', 'email', 'inbound', 'Re: Proposal — looks great, one question on pricing for the retainer tier.', 'David Whitman', minutesAgo(base, 95)),\n msg('m9', 'c4', 'email', 'outbound', 'Happy to walk you through it — are you free for a quick call tomorrow?', 'Sofia', minutesAgo(base, 80)),\n\n msg('m10', 'c5', 'webchat', 'inbound', 'Is anyone available to chat?', 'Website visitor', minutesAgo(base, 140)),\n\n msg('m11', 'c6', 'whatsapp', 'outbound', 'You are all set for next Tuesday. Anything else?', 'You', minutesAgo(base, 1500)),\n msg('m12', 'c6', 'whatsapp', 'inbound', 'Thank you! See you next week 🙌', 'Priya Nair', minutesAgo(base, 1440)),\n ]\n\n return { conversations, messages }\n}\n\nfunction msg(\n id: string, conversationId: string, channel: Message['channel'],\n direction: Message['direction'], body: string, author: string, at: string,\n): Message {\n return { id, conversationId, channel, direction, body, author, at }\n}\n\nexport interface MockConversationsConfig {\n /** Tenant id (value or getter) used to namespace the localStorage snapshot. */\n tenantId?: string | (() => string | undefined)\n /** Display name stamped as the author of outbound messages. */\n selfAuthor?: string\n}\n\ninterface Snapshot {\n conversations: Conversation[]\n messages: Message[]\n counter: number\n}\n\n// ---------------------------------------------------------------------------\n// In-memory mock inbox with best-effort localStorage persistence, so a browser\n// reload keeps whatever the demo created/sent within the session. The store is\n// namespaced per tenant; SSR / no-window environments degrade to pure memory.\n// ---------------------------------------------------------------------------\nexport function createMockConversationsProvider(\n config?: MockConversationsConfig,\n): ConversationsProvider {\n const selfAuthor = config?.selfAuthor ?? 'You'\n\n function resolveTenant(): string {\n const raw = typeof config?.tenantId === 'function' ? config.tenantId() : config?.tenantId\n return raw || 'default'\n }\n\n const storageKey = () => `saas:mock:conversations:${resolveTenant()}`\n\n function hasStorage(): boolean {\n try {\n return typeof window !== 'undefined' && !!window.localStorage\n } catch {\n return false\n }\n }\n\n function load(): Snapshot {\n if (hasStorage()) {\n try {\n const raw = window.localStorage.getItem(storageKey())\n if (raw) {\n const parsed = JSON.parse(raw) as Snapshot\n if (Array.isArray(parsed.conversations) && Array.isArray(parsed.messages)) {\n return {\n conversations: parsed.conversations,\n messages: parsed.messages,\n counter: parsed.counter ?? 100,\n }\n }\n }\n } catch {\n // Corrupt snapshot — fall through to a fresh seed.\n }\n }\n const seeded = seed()\n return { conversations: seeded.conversations, messages: seeded.messages, counter: 100 }\n }\n\n const state = load()\n\n function persist(): void {\n if (!hasStorage()) return\n try {\n window.localStorage.setItem(\n storageKey(),\n JSON.stringify({\n conversations: state.conversations,\n messages: state.messages,\n counter: state.counter,\n } satisfies Snapshot),\n )\n } catch {\n // Quota / privacy mode — keep working in memory only.\n }\n }\n\n // Persist the initial seed so a first reload is already stable.\n persist()\n\n return {\n async listConversations(query?: ListConversationsQuery): Promise<Conversation[]> {\n let list = [...state.conversations]\n if (query?.channel && query.channel !== 'all') list = list.filter((c) => c.channel === query.channel)\n if (query?.status && query.status !== 'all') list = list.filter((c) => c.status === query.status)\n if (query?.search) {\n const q = query.search.toLowerCase()\n list = list.filter(\n (c) => c.contactName.toLowerCase().includes(q) || c.lastMessagePreview.toLowerCase().includes(q),\n )\n }\n return list.sort((a, b) => b.lastMessageAt.localeCompare(a.lastMessageAt))\n },\n\n async getMessages(conversationId: string): Promise<Message[]> {\n return state.messages\n .filter((m) => m.conversationId === conversationId)\n .sort((a, b) => a.at.localeCompare(b.at))\n },\n\n async createConversation(input: CreateConversationInput): Promise<Conversation> {\n const now = new Date().toISOString()\n const firstMessage = input.firstMessage?.trim()\n const id = `c${++state.counter}`\n const conversation: Conversation = {\n id,\n contactName: input.contactName.trim(),\n contactPersonId: input.contactPersonId,\n contactHandle: input.contactHandle?.trim() ?? '',\n channel: input.channel,\n lastMessagePreview: firstMessage ?? '',\n lastMessageAt: now,\n // A thread the house opened is not waiting on the house. Left undefined\n // when there is no first message: nobody has spoken, so there is no\n // last speaker to name, and `isWaitingOnUs` falls back to unread (0).\n lastMessageDirection: firstMessage ? 'outbound' : undefined,\n unreadCount: 0,\n status: 'open',\n assignedTo: selfAuthor,\n accent: CHANNEL_ACCENT_HEX[input.channel],\n tags: [],\n note: input.note?.trim() || undefined,\n }\n state.conversations.unshift(conversation)\n if (firstMessage) {\n state.messages.push({\n id: `m${++state.counter}`,\n conversationId: id,\n channel: input.channel,\n direction: 'outbound',\n body: firstMessage,\n author: selfAuthor,\n at: now,\n })\n }\n persist()\n return conversation\n },\n\n async sendMessage(input: SendMessageInput): Promise<Message> {\n const conv = state.conversations.find((c) => c.id === input.conversationId)\n const created: Message = {\n id: `m${++state.counter}`,\n conversationId: input.conversationId,\n channel: conv?.channel ?? 'sms',\n direction: 'outbound',\n body: input.body,\n author: selfAuthor,\n at: new Date().toISOString(),\n }\n state.messages.push(created)\n if (conv) {\n conv.lastMessagePreview = input.body\n conv.lastMessageAt = created.at\n conv.lastMessageDirection = 'outbound'\n conv.unreadCount = 0\n if (conv.status === 'closed') conv.status = 'open'\n }\n persist()\n return created\n },\n\n async markRead(conversationId: string): Promise<void> {\n const conv = state.conversations.find((c) => c.id === conversationId)\n if (conv && conv.unreadCount !== 0) {\n conv.unreadCount = 0\n persist()\n }\n },\n\n async setStatus(conversationId: string, status: ConversationStatus): Promise<Conversation> {\n const conv = state.conversations.find((c) => c.id === conversationId)\n if (!conv) throw new Error('Conversation not found')\n conv.status = status\n persist()\n return conv\n },\n }\n}\n","// Physical table names for the conversations plugin (plg_conversations* prefix).\n// Import T and reference T.<key> in the data provider so a rename lands in\n// exactly one place.\nexport const T = {\n conversations: 'plg_conversations',\n messages: 'plg_conversation_messages',\n channels: 'plg_conversations_channels',\n} as const\n","import { getSupabaseClientOptional } from '@fayz-ai/core'\nimport type { ConversationsProvider } from './types'\nimport { T } from './tables'\nimport { CHANNEL_ACCENT_HEX } from './accents'\nimport type {\n Conversation,\n Message,\n ListConversationsQuery,\n SendMessageInput,\n CreateConversationInput,\n ConversationStatus,\n MessageDirection,\n} from '../types'\n\n// ---------------------------------------------------------------------------\n// Supabase-backed conversations provider — reads/writes the project's\n// `conversations` + `conversation_messages` tables (tenant-scoped via RLS +\n// an explicit tenant filter from the active-org context). Mirrors the inline\n// client-casting style of @fayz-ai/core's createSupabaseProvider; the global\n// client is `unknown`-typed so each call narrows the shape it needs.\n//\n// Real channel connectors (Twilio, WhatsApp Cloud, Meta, IMAP) deliver inbound\n// rows into these tables out-of-band; this provider is the read/compose surface.\n// ---------------------------------------------------------------------------\n\nexport interface SupabaseConversationsConfig {\n /** Supabase client; defaults to the global one registered by createFayzApp. */\n supabaseClient?: unknown\n /** Active tenant id (value or getter) used to scope reads and stamp writes. */\n tenantId?: string | (() => string | undefined)\n /** Display name stamped as the author of outbound messages. */\n selfAuthor?: string\n}\n\ntype Row = Record<string, unknown>\n\nfunction mapConversation(r: Row): Conversation {\n return {\n id: String(r.id),\n contactName: (r.contact_name as string) ?? '',\n contactPersonId: (r.contact_person_id as string | null) ?? undefined,\n contactHandle: (r.contact_handle as string) ?? '',\n channel: (r.channel as Conversation['channel']) ?? 'sms',\n lastMessagePreview: (r.last_message_preview as string) ?? '',\n lastMessageAt: (r.last_message_at as string) ?? '',\n // Kept current by the trigger in migration 001, never written from here:\n // the column has one writer on purpose (last_message_preview/at have five,\n // which is the mistake this one was not going to repeat).\n lastMessageDirection: (r.last_message_direction as MessageDirection | null) ?? undefined,\n unreadCount: Number(r.unread_count ?? 0),\n status: (r.status as ConversationStatus) ?? 'open',\n assignedTo: (r.assigned_to as string | null) ?? undefined,\n accent: (r.accent as string) ?? '#6366f1',\n tags: (r.tags as string[] | null) ?? [],\n location: (r.location as string | null) ?? undefined,\n note: (r.note as string | null) ?? undefined,\n }\n}\n\nfunction mapMessage(r: Row): Message {\n return {\n id: String(r.id),\n conversationId: String(r.conversation_id),\n channel: (r.channel as Message['channel']) ?? 'sms',\n direction: (r.direction as Message['direction']) ?? 'inbound',\n body: (r.body as string) ?? '',\n at: (r.at as string) ?? '',\n author: (r.author as string) ?? '',\n deliveryStatus: (r.delivery_status as string) ?? null,\n providerMessageId: (r.provider_message_id as string) ?? null,\n }\n}\n\nexport function createSupabaseConversationsProvider(\n config?: SupabaseConversationsConfig,\n): ConversationsProvider {\n /** O que o provedor chama de status, no vocabulário que a nossa coluna aceita.\n * Um valor desconhecido vira `sent`: a mensagem saiu, e inventar um estado\n * que o CHECK recusa transforma um envio bem-sucedido num erro. */\nconst DELIVERY = new Set(['queued','sent','delivered','read','failed','expired','delivery_timeout','cancelled','opted_out'])\nfunction mapDeliveryStatus(raw: unknown): string {\n const v = String(raw ?? '').toLowerCase()\n if (v === 'accepted' || v === 'received') return 'sent'\n return DELIVERY.has(v) ? v : 'sent'\n}\n\nconst selfAuthor = config?.selfAuthor ?? 'You'\n\n function resolveTenantId(): string | undefined {\n if (!config?.tenantId) return undefined\n return typeof config.tenantId === 'function' ? config.tenantId() : config.tenantId\n }\n\n function client(): { from: (t: string) => Row } {\n const supabase = (config?.supabaseClient ?? getSupabaseClientOptional()) as\n | { from: (t: string) => Row }\n | null\n if (!supabase) {\n throw new Error(\n '[plugin-conversations] Supabase client not available. Pass supabaseClient or register the global client via createFayzApp.',\n )\n }\n return supabase\n }\n\n return {\n async listConversations(query?: ListConversationsQuery): Promise<Conversation[]> {\n let q = (client().from(T.conversations) as { select: (s: string) => Row }).select('*')\n\n const tenantId = resolveTenantId()\n if (tenantId) q = (q as { eq: (c: string, v: string) => Row }).eq('tenant_id', tenantId)\n if (query?.channel && query.channel !== 'all') {\n q = (q as { eq: (c: string, v: string) => Row }).eq('channel', query.channel)\n }\n if (query?.status && query.status !== 'all') {\n q = (q as { eq: (c: string, v: string) => Row }).eq('status', query.status)\n }\n if (query?.search) {\n const term = `%${query.search}%`\n q = (q as { or: (c: string) => Row }).or(\n `contact_name.ilike.${term},last_message_preview.ilike.${term}`,\n )\n }\n q = (q as { order: (c: string, o: unknown) => Row }).order('last_message_at', {\n ascending: false,\n })\n\n const { data, error } = (await q) as { data: Row[] | null; error: unknown }\n if (error) throw error\n return (data ?? []).map(mapConversation)\n },\n\n async getMessages(conversationId: string): Promise<Message[]> {\n const selected = (\n client().from(T.messages) as { select: (s: string) => Row }\n ).select('*')\n const filtered = (selected as { eq: (c: string, v: string) => Row }).eq(\n 'conversation_id',\n conversationId,\n )\n const ordered = (filtered as { order: (c: string, o: unknown) => Row }).order('at', {\n ascending: true,\n })\n const { data, error } = (await ordered) as { data: Row[] | null; error: unknown }\n if (error) throw error\n return (data ?? []).map(mapMessage)\n },\n\n async createConversation(input: CreateConversationInput): Promise<Conversation> {\n const tenantId = resolveTenantId()\n // Insert with a null tenant_id fails the plg_conversations RLS WITH CHECK\n // (tenant_id ∈ user_tenant_ids()) with an opaque Postgres error. When the\n // active tenant hasn't resolved yet (bootstrap race), fail fast with an\n // actionable message the compose modal can surface via toast.\n if (!tenantId) {\n throw new Error('[plugin-conversations] Active tenant not resolved — cannot create conversation. Try again in a moment.')\n }\n const now = new Date().toISOString()\n const firstMessage = input.firstMessage?.trim()\n\n const convRow: Row = {\n contact_name: input.contactName.trim(),\n // Only written when the picker resolved a real person — an app that\n // hasn't run migration 002 yet would reject an unknown column, and\n // omitting it keeps the free-text path working there.\n ...(input.contactPersonId ? { contact_person_id: input.contactPersonId } : {}),\n contact_handle: input.contactHandle?.trim() || null,\n channel: input.channel,\n last_message_preview: firstMessage || null,\n last_message_at: now,\n unread_count: 0,\n status: 'open',\n assigned_to: selfAuthor,\n accent: CHANNEL_ACCENT_HEX[input.channel],\n tags: [],\n note: input.note?.trim() || null,\n }\n if (tenantId) convRow.tenant_id = tenantId\n\n const { data: created, error } = (await (\n (client().from(T.conversations) as { insert: (r: Row) => Row }).insert(convRow) as {\n select: () => { single: () => Promise<{ data: Row | null; error: unknown }> }\n }\n )\n .select()\n .single()) as { data: Row | null; error: unknown }\n if (error) throw error\n if (!created) throw new Error('Conversation not created')\n\n // Seed the thread with the first outbound message when provided.\n if (firstMessage) {\n const msgRow: Row = {\n conversation_id: String(created.id),\n channel: input.channel,\n direction: 'outbound',\n body: firstMessage,\n author: selfAuthor,\n at: now,\n }\n if (tenantId) msgRow.tenant_id = tenantId\n await (client().from(T.messages) as { insert: (r: Row) => Promise<unknown> }).insert(msgRow)\n }\n\n return mapConversation(created)\n },\n\n async sendMessage(input: SendMessageInput): Promise<Message> {\n\n const tenantId = resolveTenantId()\n\n // Resolve the channel from the parent conversation so the message matches.\n const convSelected = (client().from(T.conversations) as { select: (s: string) => Row }).select(\n 'channel, contact_handle',\n )\n const convFiltered = (convSelected as { eq: (c: string, v: string) => Row }).eq(\n 'id',\n input.conversationId,\n )\n const { data: conv } = (await (\n convFiltered as { maybeSingle: () => Promise<{ data: Row | null }> }\n ).maybeSingle()) as { data: Row | null }\n const channel = (conv?.channel as Message['channel']) ?? 'sms'\n\n const at = new Date().toISOString()\n const row: Row = {\n conversation_id: input.conversationId,\n channel,\n direction: 'outbound',\n body: input.body,\n author: selfAuthor,\n at,\n }\n if (tenantId) row.tenant_id = tenantId\n\n const { data: created, error } = (await (\n (client().from(T.messages) as { insert: (r: Row) => Row }).insert(row) as {\n select: () => { single: () => Promise<{ data: Row | null; error: unknown }> }\n }\n )\n .select()\n .single()) as { data: Row | null; error: unknown }\n if (error) throw error\n\n // E AGORA A MENSAGEM SAI DE VERDADE.\n //\n // Até aqui `sendMessage` gravava a linha e terminava. A caixa mostrava a\n // mensagem enviada, o destinatário nunca recebia nada, e não havia erro\n // em lugar nenhum — duas mensagens ficaram meses paradas nesta tabela\n // antes de alguém perguntar por quê.\n //\n // O envio é DEPOIS da gravação, de propósito: se a entrega falhar, a\n // linha continua existindo com a falha registrada. O contrário — mandar\n // e só então gravar — perde a mensagem quando a gravação falha, e aí\n // ninguém sabe o que foi dito.\n //\n // A falha não é silenciosa: ela sobe. Quem chamou decide o que mostrar.\n // O que o envio devolveu, para o objeto RETORNADO carregar. A primeira\n // versão devolvia a linha capturada ANTES do envio: sem id do provedor e\n // sem status. A caixa então desenhava vermelho — \"sem id, não saiu\" — uma\n // mensagem que tinha acabado de ser entregue. O banco estava certo e a\n // tela mentia até o próximo recarregamento.\n let sentId: string | null = null\n let sentStatus: string | null = null\n\n if (channel === 'whatsapp') {\n const handle = String((conv as Row | null)?.contact_handle ?? '')\n try {\n const { data: out, error: sendErr } = await (client() as any).functions.invoke(\n 'conversations-provider',\n { body: { tenantId, action: 'send', to: handle, text: input.body } },\n )\n if (sendErr) {\n const detail = (await sendErr.context?.json?.().catch(() => null)) as { message?: string } | null\n throw new Error(detail?.message ?? sendErr.message)\n }\n if ((out as any)?.error) throw new Error((out as any).message ?? (out as any).error)\n sentId = (out as any)?.providerMessageId ?? null\n // `queued` é uma resposta, não uma falha: o provedor desta casa só é\n // alcançável da máquina dela, e a nuvem deixou a linha para o\n // disparador local. O balão fica cinza com o relógio até ele passar.\n sentStatus = (out as any)?.queued ? 'queued' : mapDeliveryStatus((out as any)?.status)\n if (created?.id) {\n await ((client().from(T.messages) as { update: (r: Row) => Row }).update({\n provider_message_id: sentId,\n // O provedor fala o dialeto dele. A Tyxter devolve `accepted`,\n // que NÃO existe no CHECK desta coluna — o update explodia, caía\n // no catch, e a mensagem era marcada como falha mesmo tendo\n // saído. Foi assim que \"opa\" apareceu verde e contou como erro.\n delivery_status: sentStatus,\n }) as { eq: (c: string, v: string) => Promise<unknown> }).eq('id', String(created.id))\n }\n } catch (err) {\n if (created?.id) {\n await ((client().from(T.messages) as { update: (r: Row) => Row }).update({\n delivery_status: 'failed',\n }) as { eq: (c: string, v: string) => Promise<unknown> }).eq('id', String(created.id))\n }\n throw err\n }\n }\n\n // Roll the parent conversation forward (preview / timestamp / unread / reopen).\n await (\n (client().from(T.conversations) as {\n update: (r: Row) => Row\n }).update({\n last_message_preview: input.body,\n last_message_at: at,\n unread_count: 0,\n status: 'open',\n }) as { eq: (c: string, v: string) => Promise<unknown> }\n ).eq('id', input.conversationId)\n\n return {\n ...mapMessage(created ?? row),\n providerMessageId: sentId,\n deliveryStatus: sentStatus,\n }\n },\n\n async markRead(conversationId: string): Promise<void> {\n const { error } = (await (\n (client().from(T.conversations) as { update: (r: Row) => Row }).update({\n unread_count: 0,\n }) as { eq: (c: string, v: string) => Promise<{ error: unknown }> }\n ).eq('id', conversationId)) as { error: unknown }\n if (error) throw error\n },\n\n async setStatus(conversationId: string, status: ConversationStatus): Promise<Conversation> {\n const updated = (client().from(T.conversations) as { update: (r: Row) => Row }).update({\n status,\n })\n const filtered = (updated as { eq: (c: string, v: string) => Row }).eq('id', conversationId)\n const selected = (filtered as { select: () => Row }).select()\n const { data, error } = (await (\n selected as { single: () => Promise<{ data: Row | null; error: unknown }> }\n ).single()) as { data: Row | null; error: unknown }\n if (error) throw error\n if (!data) throw new Error('Conversation not found')\n return mapConversation(data)\n },\n }\n}\n","import { createStore, type StoreApi } from 'zustand/vanilla'\nimport type { ConversationsProvider } from './data/types'\nimport type {\n Conversation,\n Message,\n Channel,\n ConversationStatus,\n CreateConversationInput,\n} from './types'\n\n/** Quem, por onde, e como chamá-lo. Vem de outro módulo — hoje a ficha do lead\n * no CRM — que sabe a pessoa e não sabe nada sobre conversas. */\nexport interface OpenForPersonInput {\n /** `public.people` id. É por ele que a conversa é reconhecida; o telefone é\n * o desempate para threads antigas, criadas antes de haver vínculo. */\n personId?: string\n name: string\n handle: string\n channel: Channel\n}\n\n/** Só os dígitos. `+55 11 99999-0000` e `5511999990000` são o mesmo telefone, e\n * comparar como texto cru abriria uma segunda thread para a mesma pessoa. */\nfunction handleKey(h: string | undefined | null): string {\n const v = (h ?? '').trim()\n if (!v) return ''\n return /[@a-z]/i.test(v) ? v.toLowerCase() : v.replace(/\\D/g, '')\n}\n\nexport interface ConversationsUIState {\n conversations: Conversation[]\n messages: Message[]\n selectedId: string | null\n refreshMessages: () => Promise<void>\n channelFilter: Channel | 'all'\n search: string\n loading: boolean\n sending: boolean\n\n load(): Promise<void>\n select(id: string): Promise<void>\n deselect(): void\n setChannelFilter(channel: Channel | 'all'): Promise<void>\n setSearch(search: string): Promise<void>\n create(input: CreateConversationInput): Promise<Conversation>\n /** Abrir a conversa DESTA pessoa neste canal, criando-a se ainda não houver. */\n openForPerson(input: OpenForPersonInput): Promise<Conversation>\n send(body: string): Promise<void>\n setStatus(status: ConversationStatus): Promise<void>\n}\n\nexport function createConversationsStore(\n provider: ConversationsProvider,\n): StoreApi<ConversationsUIState> {\n return createStore<ConversationsUIState>((set, get) => ({\n conversations: [],\n messages: [],\n selectedId: null,\n channelFilter: 'all',\n search: '',\n loading: false,\n sending: false,\n\n async load() {\n set({ loading: true })\n const conversations = await provider.listConversations({\n channel: get().channelFilter,\n search: get().search || undefined,\n })\n const selectedId = get().selectedId ?? conversations[0]?.id ?? null\n set({ conversations, loading: false, selectedId })\n if (selectedId) await get().select(selectedId)\n },\n\n async select(id: string) {\n set({ selectedId: id })\n const messages = await provider.getMessages(id)\n set({ messages })\n await provider.markRead(id)\n set((s) => ({\n conversations: s.conversations.map((c) => (c.id === id ? { ...c, unreadCount: 0 } : c)),\n }))\n },\n\n /** Relê as mensagens da conversa aberta, sem tocar em mais nada.\n *\n * O status de uma mensagem de WhatsApp muda DEPOIS do envio — entregue,\n * lida, às vezes minutos depois — e quem avisa é o webhook, que escreve no\n * banco sem a tela saber. Sem isto a caixa mostra para sempre o status do\n * instante do envio: um tique só, numa mensagem que já foi lida.\n *\n * Não usa `select()` de propósito: aquele marca a conversa como lida e\n * zera o contador, e uma releitura de fundo não é um gesto de ninguém. */\n async refreshMessages() {\n const id = get().selectedId\n if (!id) return\n try {\n const messages = await provider.getMessages(id)\n // Só escreve se mudou algo. `set` a cada dez segundos redesenharia a\n // thread inteira e roubaria a seleção de texto de quem está lendo.\n const before = get().messages\n const same = before.length === messages.length &&\n before.every((m, i) => m.id === messages[i].id && m.deliveryStatus === messages[i].deliveryStatus)\n if (!same) set({ messages })\n } catch { /* a caixa continua com o que tem; a próxima volta tenta de novo */ }\n },\n\n deselect() {\n set({ selectedId: null, messages: [] })\n },\n\n async setChannelFilter(channel) {\n set({ channelFilter: channel })\n await get().load()\n },\n\n async setSearch(search) {\n set({ search })\n await get().load()\n },\n\n async create(input) {\n const created = await provider.createConversation(input)\n // The thread EXISTS the moment the insert returns, so resolve on that and\n // show it optimistically. Clear filters that would hide it, prepend it,\n // select it — all local.\n set((s) => ({\n channelFilter: 'all',\n search: '',\n conversations: [created, ...s.conversations.filter((c) => c.id !== created.id)],\n selectedId: created.id,\n }))\n\n // Reconciliation (authoritative list + the thread's messages) runs in the\n // BACKGROUND. It used to be awaited here, which made the caller — the\n // compose modal — hostage to two extra round-trips: on a slow pool the\n // conversation was already in the database while the modal sat open\n // looking broken, inviting a second click and a duplicate thread.\n // Failures are non-fatal: the optimistic row above already renders.\n void (async () => {\n try {\n const conversations = await provider.listConversations({})\n // Read-after-write guard: a refetch that races the insert may not\n // return the new row yet — keep the local one rather than dropping it.\n const merged = conversations.some((c) => c.id === created.id)\n ? conversations\n : [created, ...conversations]\n set({ conversations: merged })\n await get().select(created.id)\n } catch {\n /* optimistic state stands; the next load() reconciles */\n }\n })()\n\n return created\n },\n\n // Chegar aqui vindo de fora — da ficha de um lead, por exemplo — não pode\n // criar uma segunda thread com alguém com quem a casa já conversa. Procura\n // primeiro pela PESSOA, e só então pelo telefone: o vínculo é a verdade, e\n // o handle é o que resgata threads criadas antes de o vínculo existir.\n async openForPerson({ personId, name, handle, channel }) {\n // Uma caixa ainda não carregada não tem como saber que a thread existe.\n if (get().conversations.length === 0 && !get().loading) {\n try { await get().load() } catch { /* segue para a criação */ }\n }\n const key = handleKey(handle)\n const existing = get().conversations.find((c) =>\n c.channel === channel &&\n ((personId && c.contactPersonId === personId) ||\n (key !== '' && handleKey(c.contactHandle) === key)))\n\n if (existing) {\n // Limpa o que esconderia a thread: chegar por link e cair numa lista\n // filtrada é indistinguível de a conversa não existir.\n set({ channelFilter: 'all', search: '' })\n await get().select(existing.id)\n return existing\n }\n\n return get().create({\n contactName: name,\n contactPersonId: personId,\n contactHandle: handle,\n channel,\n })\n },\n\n async send(body: string) {\n const id = get().selectedId\n if (!id || !body.trim()) return\n set({ sending: true })\n const created = await provider.sendMessage({ conversationId: id, body: body.trim() })\n set((s) => ({\n sending: false,\n messages: [...s.messages, created],\n conversations: s.conversations.map((c) =>\n // `lastMessageDirection` acompanha a prévia, senão a fila mente. O\n // gatilho da migration 001 carimba a coluna no banco, mas a lista só\n // volta a ler no próximo `load()` — e até lá a thread que ACABOU de\n // ser respondida continua em \"esperando nós\", que é exatamente a\n // pergunta que essa coluna existe para responder.\n //\n // Antes da 001 isto não aparecia: a coluna vinha sempre indefinida e\n // `isWaitingOnUs` caía no `unreadCount`. A migration não criou o\n // defeito, tornou-o visível.\n c.id === id\n ? { ...c, lastMessagePreview: created.body, lastMessageAt: created.at, lastMessageDirection: 'outbound' as const }\n : c,\n ),\n }))\n },\n\n async setStatus(status) {\n const id = get().selectedId\n if (!id) return\n const updated = await provider.setStatus(id, status)\n set((s) => ({\n conversations: s.conversations.map((c) => (c.id === id ? updated : c)),\n }))\n },\n }))\n}\n","export const en: Record<string, string> = {\n 'conversations.title': 'Conversations',\n 'conversations.subtitle': 'Unified inbox across every channel',\n\n // Conversation list\n 'conversations.list.search': 'Search conversations',\n 'conversations.list.loading': 'Loading…',\n 'conversations.list.empty': 'No conversations',\n 'conversations.list.new': 'New conversation',\n\n // Channel filters\n 'conversations.filter.all': 'All',\n 'conversations.filter.whatsapp': 'WhatsApp',\n 'conversations.filter.sms': 'SMS',\n 'conversations.filter.instagram': 'Instagram',\n 'conversations.filter.email': 'Email',\n 'conversations.filter.webchat': 'Web',\n\n // Status labels\n 'conversations.status.open': 'Open',\n 'conversations.status.snoozed': 'Snoozed',\n 'conversations.status.closed': 'Closed',\n\n // Empty state\n 'conversations.empty.select': 'Select a conversation to start chatting',\n\n // Thread\n 'conversations.thread.back': 'Back to conversations',\n 'conversations.thread.snooze': 'Snooze',\n 'conversations.thread.close': 'Close',\n 'conversations.thread.details': 'Toggle contact details',\n 'conversations.thread.empty': 'No messages yet',\n 'conversations.thread.reply': 'Reply via {{channel}}…',\n 'conversations.thread.send': 'Send',\n\n // Contact panel\n 'conversations.contact.details': 'Details',\n 'conversations.contact.closeDetails': 'Close details',\n 'conversations.contact.channel': 'Channel',\n 'conversations.contact.status': 'Status',\n 'conversations.contact.assignedTo': 'Assigned to',\n 'conversations.contact.location': 'Location',\n 'conversations.contact.tags': 'Tags',\n 'conversations.contact.note': 'Note',\n 'conversations.contact.linkedRecords': 'Linked records',\n 'conversations.contact.noLinkedRecords': 'No linked records yet.',\n\n // New-conversation modal\n 'conversations.new.title': 'New conversation',\n 'conversations.new.channel': 'Channel',\n 'conversations.new.contactName': 'Contact name',\n 'conversations.new.contactNamePlaceholder': 'e.g. Jane Doe',\n 'conversations.new.handle': 'Phone / handle / email',\n 'conversations.new.handlePlaceholder': '+1 555 000 0000',\n // The handle field only surfaces when the picked contact has nothing usable\n // for the active channel — otherwise it is derived and shown on the chip.\n 'conversations.new.addHandle': 'Add {label}',\n 'conversations.new.handleLabel.phone': 'phone',\n 'conversations.new.handleLabel.email': 'email',\n 'conversations.new.handleLabel.instagram': 'Instagram handle',\n 'conversations.new.handleLabel.webchat': 'web chat id',\n 'conversations.new.firstMessage': 'First message',\n 'conversations.new.firstMessagePlaceholder': 'Write the first message (optional)…',\n 'conversations.new.cancel': 'Cancel',\n 'conversations.new.create': 'Start conversation',\n 'conversations.new.creating': 'Starting…',\n 'conversations.new.createFailed': 'Could not create the conversation',\n\n // Dashboard — the queue of people waiting on a reply\n 'conversations.dashboard.waiting': 'Waiting on you',\n 'conversations.dashboard.waitingOldest': 'Oldest waiting {{time}}',\n 'conversations.dashboard.waitingNone': 'Nobody waiting',\n 'conversations.dashboard.openThreads': 'Open conversations',\n 'conversations.dashboard.openThreadsSub': '{{count}} waiting on a reply',\n 'conversations.dashboard.needsYou': 'Needs you',\n 'conversations.dashboard.needsYouEmpty': 'Nobody waiting. Inbox is clear.',\n 'conversations.dashboard.who': 'Who',\n 'conversations.dashboard.waited': 'Waiting',\n 'conversations.waited.minutes': '{{count}} min',\n 'conversations.waited.hours': '{{count}} h',\n 'conversations.waited.days': '{{count}} d',\n\n // Inbox settings\n 'conversations.settings.title': 'Conversations',\n 'conversations.settings.channels': 'Your numbers',\n 'conversations.settings.channelsHelp': 'Where messages come in and go out. Connect and manage channels in the Integrations tab.',\n 'conversations.settings.channelsEmpty': 'No channel connected yet — until then the inbox stays empty. Connect WhatsApp in the Integrations tab.',\n 'conversations.settings.channelsUnavailable': 'Could not load your channels right now.',\n 'conversations.settings.channelDedicated': 'Your own number',\n 'conversations.settings.channelShared': 'Platform number',\n 'conversations.settings.status.active': 'Active',\n 'conversations.settings.status.requested': 'Requested',\n 'conversations.settings.status.provisioning': 'Provisioning',\n 'conversations.settings.status.provisioned': 'Provisioned',\n 'conversations.settings.status.verifying': 'Verifying',\n 'conversations.settings.status.failed': 'Failed',\n 'conversations.settings.status.released': 'Released',\n 'conversations.settings.status.disconnected': 'Disconnected',\n}\n","export const ptBR: Record<string, string> = {\n 'conversations.title': 'Conversas',\n 'conversations.subtitle': 'Caixa de entrada unificada de todos os canais',\n\n // Lista de conversas\n 'conversations.list.search': 'Buscar conversas',\n 'conversations.list.loading': 'Carregando…',\n 'conversations.list.empty': 'Nenhuma conversa',\n 'conversations.list.new': 'Nova conversa',\n\n // Filtros de canal\n 'conversations.filter.all': 'Todas',\n 'conversations.filter.whatsapp': 'WhatsApp',\n 'conversations.filter.sms': 'SMS',\n 'conversations.filter.instagram': 'Instagram',\n 'conversations.filter.email': 'E-mail',\n 'conversations.filter.webchat': 'Web',\n\n // Rótulos de status\n 'conversations.status.open': 'Aberta',\n 'conversations.status.snoozed': 'Adiada',\n 'conversations.status.closed': 'Encerrada',\n\n // Estado vazio\n 'conversations.empty.select': 'Selecione uma conversa para começar a conversar',\n\n // Thread\n 'conversations.thread.back': 'Voltar às conversas',\n 'conversations.thread.snooze': 'Adiar',\n 'conversations.thread.close': 'Encerrar',\n 'conversations.thread.details': 'Alternar detalhes do contato',\n 'conversations.thread.empty': 'Nenhuma mensagem ainda',\n 'conversations.thread.reply': 'Responder via {{channel}}…',\n 'conversations.thread.send': 'Enviar',\n\n // Painel do contato\n 'conversations.contact.details': 'Detalhes',\n 'conversations.contact.closeDetails': 'Fechar detalhes',\n 'conversations.contact.channel': 'Canal',\n 'conversations.contact.status': 'Status',\n 'conversations.contact.assignedTo': 'Responsável',\n 'conversations.contact.location': 'Localização',\n 'conversations.contact.tags': 'Etiquetas',\n 'conversations.contact.note': 'Nota',\n 'conversations.contact.linkedRecords': 'Registros vinculados',\n 'conversations.contact.noLinkedRecords': 'Nenhum registro vinculado ainda.',\n\n // Modal de nova conversa\n 'conversations.new.title': 'Nova conversa',\n 'conversations.new.channel': 'Canal',\n 'conversations.new.contactName': 'Nome do contato',\n 'conversations.new.contactNamePlaceholder': 'ex.: Maria Silva',\n 'conversations.new.handle': 'Telefone / usuário / e-mail',\n 'conversations.new.handlePlaceholder': '+55 11 99999-0000',\n // O campo de handle só aparece quando o contato escolhido não tem o dado do\n // canal ativo — caso contrário ele é derivado e mostrado no chip.\n 'conversations.new.addHandle': 'Adicionar {label}',\n 'conversations.new.handleLabel.phone': 'telefone',\n 'conversations.new.handleLabel.email': 'e-mail',\n 'conversations.new.handleLabel.instagram': '@ do Instagram',\n 'conversations.new.handleLabel.webchat': 'id do chat',\n 'conversations.new.firstMessage': 'Primeira mensagem',\n 'conversations.new.firstMessagePlaceholder': 'Escreva a primeira mensagem (opcional)…',\n 'conversations.new.cancel': 'Cancelar',\n 'conversations.new.create': 'Iniciar conversa',\n 'conversations.new.creating': 'Iniciando…',\n 'conversations.new.createFailed': 'Não foi possível criar a conversa',\n\n // Painel — a fila de quem está esperando resposta\n 'conversations.dashboard.waiting': 'Esperando resposta',\n 'conversations.dashboard.waitingOldest': 'A mais antiga há {{time}}',\n 'conversations.dashboard.waitingNone': 'Ninguém na fila',\n 'conversations.dashboard.openThreads': 'Conversas abertas',\n 'conversations.dashboard.openThreadsSub': '{{count}} esperando resposta',\n 'conversations.dashboard.needsYou': 'Precisa de você',\n 'conversations.dashboard.needsYouEmpty': 'Ninguém esperando. Caixa de entrada em dia.',\n 'conversations.dashboard.who': 'Quem',\n 'conversations.dashboard.waited': 'Esperando há',\n 'conversations.waited.minutes': '{{count}} min',\n 'conversations.waited.hours': '{{count}} h',\n 'conversations.waited.days': '{{count}} d',\n\n // Configurações da caixa de entrada\n 'conversations.settings.title': 'Conversas',\n 'conversations.settings.channels': 'Seus números',\n 'conversations.settings.channelsHelp': 'Por onde a mensagem entra e sai. Ligue e gerencie os canais na aba Integrações.',\n 'conversations.settings.channelsEmpty': 'Nenhum canal ligado ainda — enquanto isso, a caixa de entrada fica vazia. Ligue o WhatsApp na aba Integrações.',\n 'conversations.settings.channelsUnavailable': 'Não foi possível consultar seus canais agora.',\n 'conversations.settings.channelDedicated': 'Número só seu',\n 'conversations.settings.channelShared': 'Número da plataforma',\n 'conversations.settings.status.active': 'Ativo',\n 'conversations.settings.status.requested': 'Solicitado',\n 'conversations.settings.status.provisioning': 'Provisionando',\n 'conversations.settings.status.provisioned': 'Provisionado',\n 'conversations.settings.status.verifying': 'Verificando',\n 'conversations.settings.status.failed': 'Falhou',\n 'conversations.settings.status.released': 'Devolvido',\n 'conversations.settings.status.disconnected': 'Desconectado',\n}\n","import { en } from './en'\nimport { ptBR } from './pt-BR'\n\nexport const conversationsLocales: Record<string, Record<string, string>> = {\n en,\n 'pt-BR': ptBR,\n}\n","import React, { useEffect, useMemo } from 'react'\nimport type { StoreApi } from 'zustand'\nimport type { DashboardWidgetDef } from '@fayz-ai/core'\nimport { useTranslation } from '@fayz-ai/core'\nimport {\n KpiCard, TableWidget, defineKpiWidget, defineTableWidget,\n type TableWidgetProps,\n} from '@fayz-ai/ui'\nimport {\n ConversationsContextProvider,\n useConversationsStore,\n type ResolvedConversationsConfig,\n} from '../context'\nimport type { ConversationsUIState } from '../store'\nimport { isWaitingOnUs, type Conversation } from '../types'\n\n// ---------------------------------------------------------------------------\n// Inbox widgets for the dashboard registry.\n//\n// This plugin registered nothing here until now, which meant the one number a\n// front desk actually opens the day for — \"who is waiting on me\" — existed on\n// no surface but the inbox itself. A dashboard that shows revenue and pipeline\n// while somebody has been waiting since Tuesday is reporting on the past.\n//\n// ── What \"waiting\" means ──────────────────────────────────────────────────\n//\n// `isWaitingOnUs` (see ../types), and not a rule spelled out again here: the\n// KPI, the table and anything that later filters the inbox have to agree, and a\n// dashboard that says three while the list shows one is worse than neither.\n//\n// It reads `lastMessageDirection`, which migration 001 added and a trigger\n// keeps current. The first pass of this file used `unreadCount > 0` because the\n// column did not exist — an approximation that missed the case that hurts\n// most: a thread somebody opened, read, meant to answer later and forgot. Zero\n// unread, customer still waiting. The fallback is still there for threads that\n// genuinely do not know their direction.\n// ---------------------------------------------------------------------------\n\n/** The store dedups the fetch, so every widget may ensure its own data. */\nfunction useEnsureConversations() {\n const load = useConversationsStore((s) => s.load)\n const loaded = useConversationsStore((s) => s.conversations.length > 0)\n useEffect(() => { if (!loaded) void load() }, [])\n}\n\nfunction useWaiting(): { waiting: Conversation[]; open: number; loading: boolean } {\n useEnsureConversations()\n const conversations = useConversationsStore((s) => s.conversations)\n const loading = useConversationsStore((s) => s.loading)\n return useMemo(() => {\n const open = conversations.filter((c) => c.status === 'open')\n const waiting = open\n .filter(isWaitingOnUs)\n // Oldest first: the list is a queue, and a queue that puts the newest\n // arrival on top is the reason somebody waits three days.\n .sort((a, b) => Date.parse(a.lastMessageAt) - Date.parse(b.lastMessageAt))\n return { waiting, open: open.length, loading }\n }, [conversations, loading])\n}\n\n/**\n * How long they have been waiting, in the coarsest unit that is still true.\n *\n * Minutes for the first hour, then hours, then days — an exact \"2h 14min\" reads\n * as precision about something nobody measures that finely, and it is one more\n * number to parse in a cell that exists to be scanned.\n */\nfunction waitedFor(iso: string, t: (key: string, vars?: Record<string, string>) => string): string {\n const ms = Date.now() - Date.parse(iso)\n if (!Number.isFinite(ms) || ms < 0) return '—'\n const minutes = Math.floor(ms / 60000)\n if (minutes < 60) return t('conversations.waited.minutes', { count: String(Math.max(minutes, 1)) })\n const hours = Math.floor(minutes / 60)\n if (hours < 24) return t('conversations.waited.hours', { count: String(hours) })\n return t('conversations.waited.days', { count: String(Math.floor(hours / 24)) })\n}\n\n// ---------------------------------------------------------------------------\n// KPIs\n// ---------------------------------------------------------------------------\n\nfunction WaitingKpi() {\n const t = useTranslation()\n const { waiting, loading } = useWaiting()\n const oldest = waiting[0]\n return (\n <KpiCard\n label={t('conversations.dashboard.waiting')}\n icon=\"MessageCircleWarning\"\n loading={loading && waiting.length === 0}\n value={String(waiting.length)}\n // The count alone does not say whether this is a busy morning or a\n // failure: three people waiting ten minutes is a queue, one person\n // waiting two days is an apology.\n sub={\n oldest\n ? t('conversations.dashboard.waitingOldest', { time: waitedFor(oldest.lastMessageAt, t) })\n : t('conversations.dashboard.waitingNone')\n }\n // More is worse here, so the arrow has to be read the other way round.\n invertTrend\n />\n )\n}\n\nfunction OpenThreadsKpi() {\n const t = useTranslation()\n const { open, waiting, loading } = useWaiting()\n return (\n <KpiCard\n label={t('conversations.dashboard.openThreads')}\n icon=\"MessagesSquare\"\n loading={loading && open === 0}\n value={String(open)}\n sub={t('conversations.dashboard.openThreadsSub', { count: String(waiting.length) })}\n />\n )\n}\n\n// ---------------------------------------------------------------------------\n// The queue itself\n// ---------------------------------------------------------------------------\n\nfunction WaitingTable() {\n const t = useTranslation()\n const { waiting, loading } = useWaiting()\n\n // Column type borrowed from the widget that consumes it rather than from\n // @tanstack/react-table directly: this plugin has no table dependency of its\n // own, and adding one to name a type would put a second copy of the table\n // library in the tree of every app that installs the inbox.\n const columns = useMemo<TableWidgetProps<Conversation>['columns']>(() => [\n {\n accessorKey: 'contactName',\n header: t('conversations.dashboard.who'),\n cell: ({ row }) => (\n <span className=\"font-medium\">{row.original.contactName}</span>\n ),\n },\n {\n id: 'preview',\n header: '',\n // The message, not just the name: half of triage is deciding which of\n // five waiting people is the one worth answering first, and that is\n // decided by what they said.\n cell: ({ row }) => (\n <span className=\"line-clamp-1 text-xs text-muted-foreground\">{row.original.lastMessagePreview}</span>\n ),\n },\n {\n id: 'channel',\n header: t('conversations.contact.channel'),\n cell: ({ row }) => (\n <span className=\"text-xs text-muted-foreground\">\n {t(`conversations.filter.${row.original.channel}`)}\n </span>\n ),\n },\n {\n id: 'waited',\n header: t('conversations.dashboard.waited'),\n cell: ({ row }) => (\n <span className=\"whitespace-nowrap text-xs font-medium tabular-nums\">\n {waitedFor(row.original.lastMessageAt, t)}\n </span>\n ),\n },\n ], [t])\n\n return (\n <TableWidget\n title={t('conversations.dashboard.needsYou')}\n icon=\"MessageCircleWarning\"\n columns={columns}\n data={waiting}\n loading={loading && waiting.length === 0}\n emptyMessage={t('conversations.dashboard.needsYouEmpty')}\n // Straight into the thread. A widget that names a person waiting and then\n // makes you go find them in a list is a notification, not a queue.\n onRowClick={(row) => { window.location.hash = `/conversations?thread=${row.id}` }}\n />\n )\n}\n\n// ---------------------------------------------------------------------------\n// Registry builder — each widget carries its own provider, so it renders on the\n// app home with no inbox page around it.\n// ---------------------------------------------------------------------------\n\nexport function createConversationsDashboardWidgets(ctx: {\n store: StoreApi<ConversationsUIState>\n config: ResolvedConversationsConfig\n}): DashboardWidgetDef[] {\n const withCtx = (Inner: React.ComponentType): React.ComponentType<unknown> => {\n const Wrapped = () => (\n <ConversationsContextProvider store={ctx.store} config={ctx.config}>\n <Inner />\n </ConversationsContextProvider>\n )\n Wrapped.displayName = `ConversationsWidget(${Inner.displayName ?? Inner.name})`\n return Wrapped\n }\n\n return [\n // Visible on the home by DEFAULT, unlike most plugin widgets. The rest of\n // the registry is opt-in because a dashboard of everything is a dashboard\n // of nothing — but an unanswered customer is the one item on any home\n // screen that is time-critical, and hiding it behind Customize means it\n // only reaches the people who already knew to look.\n defineKpiWidget({\n id: 'conversations.kpi.waiting',\n title: 'conversations.dashboard.waiting',\n domain: 'conversations',\n defaultOrder: -1,\n component: withCtx(WaitingKpi),\n }),\n defineKpiWidget({\n id: 'conversations.kpi.open',\n title: 'conversations.dashboard.openThreads',\n domain: 'conversations',\n defaultOrder: 4,\n defaultVisible: false,\n component: withCtx(OpenThreadsKpi),\n }),\n defineTableWidget({\n id: 'conversations.table.waiting',\n title: 'conversations.dashboard.needsYou',\n domain: 'conversations',\n span: 2,\n defaultOrder: 10,\n component: withCtx(WaitingTable),\n }),\n ]\n}\n","import React from 'react'\nimport { Check, Loader2, QrCode, RefreshCw, ShieldAlert, ShieldCheck, Zap } from 'lucide-react'\nimport { SettingsGroup } from '@fayz-ai/admin'\nimport { Button, Input, toast } from '@fayz-ai/ui'\nimport { providersApi, type ProviderId, type ProviderRow, type ProviderNumber } from '../data/providers'\n\n/**\n * Onde a casa escolhe por onde o WhatsApp sai.\n *\n * ── os dois não são intercambiáveis, e a tela diz isso ──────────────────\n *\n * A tentação é desenhar dois cartões iguais e um botão de rádio. Seria mentira:\n *\n * Tyxter API oficial. Iniciar conversa EXIGE template aprovado pela Meta.\n * A Meta mede qualidade e limite do número, e responde por eles.\n * Evolution WhatsApp Web. Manda texto para quem quiser, sem template e sem\n * janela — e o número pode ser banido sem aviso.\n *\n * Quem troca de um para o outro precisa saber o que está trocando. Por isso\n * cada cartão carrega a consequência escrita, e não só o nome.\n *\n * ── a chave não volta ───────────────────────────────────────────────────\n *\n * O campo de chave está sempre vazio, mesmo com credencial guardada. Não é\n * esquecimento: o segredo não sai do servidor, nem mascarado, porque\n * `tx_live_••••` ainda é a chave inteira viajando até o navegador para ser\n * escondida na renderização. `hasCredential` responde a única pergunta que a\n * tela precisa fazer — já foi posta? — e o cadeado diz o resto.\n */\n\nconst COPY: Record<ProviderId, {\n name: string\n what: string\n strength: string\n cost: string\n fields: Array<{ key: string; label: string; placeholder: string; secret?: boolean }>\n}> = {\n tyxter: {\n name: 'Tyxter',\n what: 'API oficial do WhatsApp Business, via revenda.',\n strength: 'Entrega confiável, sem risco de banimento. A Meta mede a qualidade do número.',\n cost: 'Para iniciar conversa é obrigatório um template aprovado pela Meta (~1 dia). Texto livre só dentro de 24h da resposta do cliente.',\n fields: [\n { key: 'api_key', label: 'Chave da API', placeholder: 'tx_live_…', secret: true },\n { key: 'default_sender_id', label: 'Número remetente (id)', placeholder: 'cm…' },\n ],\n },\n evolution: {\n name: 'Evolution API',\n what: 'Gateway auto-hospedado, sobre o WhatsApp Web.',\n strength: 'Manda texto livre para quem quiser, sem template e sem janela de 24h.',\n cost: 'O número pode ser banido sem aviso, e a sessão cai sozinha. Aquecer o número não é opcional aqui.',\n fields: [\n { key: 'base_url', label: 'URL da instância', placeholder: 'https://evo.seudominio.com' },\n { key: 'api_key', label: 'Chave da API', placeholder: 'sua apikey', secret: true },\n { key: 'instance', label: 'Nome da instância', placeholder: 'chefcontrol' },\n ],\n },\n}\n\nconst ALL: ProviderId[] = ['tyxter', 'evolution']\n\nfunction StatusChip({ row }: { row: ProviderRow | undefined }) {\n if (!row || row.status === 'unconfigured') {\n return <span className=\"rounded-full bg-muted px-2 py-0.5 text-[10px] text-muted-foreground\">não configurado</span>\n }\n const map: Record<string, string> = {\n connected: 'bg-success-soft text-success-soft-foreground',\n connecting: 'bg-warning-soft text-warning-soft-foreground',\n disconnected: 'bg-muted text-muted-foreground',\n error: 'bg-destructive-soft text-destructive-soft-foreground',\n }\n const label: Record<string, string> = {\n connected: 'conectado', connecting: 'aguardando teste',\n disconnected: 'desconectado', error: 'com erro',\n }\n return (\n <span className={`rounded-full px-2 py-0.5 text-[10px] font-medium ${map[row.status] ?? 'bg-muted'}`}>\n {label[row.status] ?? row.status}\n </span>\n )\n}\n\nfunction NumberRow({ n }: { n: ProviderNumber }) {\n return (\n <div className=\"flex flex-wrap items-center gap-x-3 gap-y-1 py-1.5 text-xs\">\n <span className={`h-1.5 w-1.5 shrink-0 rounded-full ${n.ready ? 'bg-success' : 'bg-muted-foreground/40'}`} />\n <span className=\"font-medium tabular-nums\">{n.phone ?? n.id}</span>\n {n.displayName && <span className=\"text-muted-foreground\">{n.displayName}</span>}\n {/* Qualidade e tier só existem no provedor oficial. Nulo vira \"—\": o\n Evolution não é medido pela Meta, e fingir um \"bom\" aqui seria\n inventar a única informação que decide quanto se pode mandar. */}\n <span className=\"text-muted-foreground\">\n qualidade: <span className=\"text-foreground\">{n.quality ?? '—'}</span>\n </span>\n <span className=\"text-muted-foreground\">\n limite: <span className=\"text-foreground\">{n.tier ?? '—'}</span>\n </span>\n {n.used24h != null && n.allowance != null && (\n <span className=\"text-muted-foreground\">\n 24h: <span className=\"text-foreground tabular-nums\">{n.used24h}/{n.used24h + n.allowance}</span>\n </span>\n )}\n {!n.ready && <span className=\"text-warning\">não pronto para enviar</span>}\n </div>\n )\n}\n\nexport function WhatsAppProviders() {\n const [rows, setRows] = React.useState<ProviderRow[] | null>(null)\n const [draft, setDraft] = React.useState<Record<string, Record<string, string>>>({})\n const [busy, setBusy] = React.useState<string | null>(null)\n const [numbers, setNumbers] = React.useState<Record<string, ProviderNumber[]>>({})\n const [qr, setQr] = React.useState<{ provider: string; image: string | null; code: string | null } | null>(null)\n\n const load = React.useCallback(async () => {\n try { setRows(await providersApi.list()) } catch { setRows([]) }\n }, [])\n React.useEffect(() => { void load() }, [load])\n\n const byId = React.useMemo(\n () => Object.fromEntries((rows ?? []).map((r) => [r.provider, r])) as Record<ProviderId, ProviderRow | undefined>,\n [rows],\n )\n\n function field(p: ProviderId, key: string): string {\n const row = byId[p]\n // A chave nunca é preenchida de volta — ver o cabeçalho.\n if (key === 'api_key') return draft[p]?.[key] ?? ''\n return draft[p]?.[key] ?? String((row?.config as any)?.[key] ?? '')\n }\n\n function setField(p: ProviderId, key: string, v: string) {\n setDraft((d) => ({ ...d, [p]: { ...(d[p] ?? {}), [key]: v } }))\n }\n\n async function run(id: string, fn: () => Promise<void>) {\n setBusy(id)\n try { await fn() } catch (e: any) { toast.error(e?.message ?? 'Falhou') } finally { setBusy(null) }\n }\n\n const save = (p: ProviderId) => run(`save:${p}`, async () => {\n const d = draft[p] ?? {}\n const config: Record<string, unknown> = {}\n for (const f of COPY[p].fields) {\n if (f.secret) continue\n const v = field(p, f.key)\n if (v) config[f.key] = v\n }\n if (p === 'tyxter' && !config.api_base_url) config.api_base_url = 'https://api.tyxter.com'\n await providersApi.save(p, config, d.api_key)\n setDraft((x) => ({ ...x, [p]: { ...(x[p] ?? {}), api_key: '' } }))\n await load()\n toast.success('Guardado')\n })\n\n const test = (p: ProviderId) => run(`test:${p}`, async () => {\n const h = await providersApi.health(p)\n await load()\n toast[h.ok ? 'success' : 'error'](h.ok ? `Conectado — ${h.detail ?? 'ok'}` : `Não conectou: ${h.detail ?? '?'}`)\n })\n\n const refreshNumbers = (p: ProviderId) => run(`numbers:${p}`, async () => {\n const d = await providersApi.numbers(p)\n setNumbers((n) => ({ ...n, [p]: d.numbers }))\n if (d.numbers.length === 0) toast.error('Nenhum número nesta conta')\n })\n\n const activate = (p: ProviderId) => run(`on:${p}`, async () => {\n await providersApi.activate(p)\n await load()\n toast.success(`${COPY[p].name} está valendo agora`)\n })\n\n const pair = (p: ProviderId) => run(`qr:${p}`, async () => {\n const d = await providersApi.pair(p)\n setQr({ provider: p, image: d.qr, code: d.code })\n if (!d.qr && !d.code) toast.error('A instância não devolveu QR — já está pareada?')\n })\n\n return (\n <SettingsGroup\n title=\"WhatsApp — por onde as mensagens saem\"\n description=\"Dois caminhos, um valendo de cada vez. Troque quando quiser; a configuração do outro fica guardada.\"\n >\n {rows === null && (\n <div className=\"flex items-center gap-2 py-6 text-sm text-muted-foreground\">\n <Loader2 className=\"h-4 w-4 animate-spin\" /> carregando…\n </div>\n )}\n\n {rows !== null && ALL.map((p) => {\n const row = byId[p]\n const copy = COPY[p]\n const active = Boolean(row?.isActive)\n const caps = row?.capabilities ?? {}\n\n return (\n <div\n key={p}\n className={`rounded-card border p-4 my-2 ${active ? 'border-primary bg-primary/[0.03]' : 'bg-card'}`}\n data-testid={`provider-${p}`}\n >\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"text-sm font-semibold\">{copy.name}</span>\n {active && (\n <span className=\"inline-flex items-center gap-1 rounded-full bg-primary px-2 py-0.5 text-[10px] font-medium text-primary-foreground\">\n <Check className=\"h-3 w-3\" /> valendo agora\n </span>\n )}\n <StatusChip row={row} />\n {row?.hasCredential && (\n <span className=\"inline-flex items-center gap-1 text-[10px] text-muted-foreground\">\n <ShieldCheck className=\"h-3 w-3\" /> chave guardada\n </span>\n )}\n {/* O risco de banimento é a diferença que decide a escolha, e ela\n fica ao lado do nome — não num rodapé que ninguém lê. */}\n {caps.ban_risk === 'high' && (\n <span className=\"inline-flex items-center gap-1 text-[10px] text-warning\">\n <ShieldAlert className=\"h-3 w-3\" /> risco de banimento\n </span>\n )}\n </div>\n\n <p className=\"mt-1 text-xs text-muted-foreground\">{copy.what}</p>\n <div className=\"mt-2 grid gap-1 sm:grid-cols-2\">\n <p className=\"text-[11px] text-success-soft-foreground\">✓ {copy.strength}</p>\n <p className=\"text-[11px] text-warning\">! {copy.cost}</p>\n </div>\n\n <div className=\"mt-3 grid gap-2 sm:grid-cols-2\">\n {copy.fields.map((f) => (\n <label key={f.key} className=\"block\">\n <span className=\"text-[11px] text-muted-foreground\">{f.label}</span>\n <Input\n className=\"mt-0.5 h-8 text-sm\"\n type={f.secret ? 'password' : 'text'}\n autoComplete=\"off\"\n placeholder={f.secret && row?.hasCredential ? '•••••••• (guardada)' : f.placeholder}\n value={field(p, f.key)}\n onChange={(e) => setField(p, f.key, e.target.value)}\n />\n </label>\n ))}\n </div>\n\n <div className=\"mt-3 flex flex-wrap gap-2\">\n <Button size=\"sm\" variant=\"outline\" disabled={busy === `save:${p}`} onClick={() => save(p)}>\n {busy === `save:${p}` ? <Loader2 className=\"h-3 w-3 animate-spin\" /> : 'Guardar'}\n </Button>\n <Button size=\"sm\" variant=\"outline\" disabled={!row?.hasCredential || busy === `test:${p}`} onClick={() => test(p)}>\n {busy === `test:${p}` ? <Loader2 className=\"h-3 w-3 animate-spin\" /> : <><RefreshCw className=\"mr-1 h-3 w-3\" /> Testar</>}\n </Button>\n <Button size=\"sm\" variant=\"outline\" disabled={!row?.hasCredential || busy === `numbers:${p}`} onClick={() => refreshNumbers(p)}>\n Números\n </Button>\n {/* Só aparece onde existe. O Tyxter não pareia por QR, e um botão\n desabilitado ainda promete algo que nunca vai acontecer. */}\n {caps.qr_pairing && (\n <Button size=\"sm\" variant=\"outline\" disabled={!row?.hasCredential || busy === `qr:${p}`} onClick={() => pair(p)}>\n <QrCode className=\"mr-1 h-3 w-3\" /> Parear\n </Button>\n )}\n {!active && (\n <Button size=\"sm\" disabled={!row?.hasCredential || busy === `on:${p}`} onClick={() => activate(p)}>\n <Zap className=\"mr-1 h-3 w-3\" /> Usar este\n </Button>\n )}\n </div>\n\n {numbers[p]?.length ? (\n <div className=\"mt-3 border-t pt-2\">\n {numbers[p].map((n) => <NumberRow key={n.id} n={n} />)}\n </div>\n ) : null}\n\n {qr?.provider === p && (\n <div className=\"mt-3 border-t pt-3\">\n <p className=\"text-xs text-muted-foreground mb-2\">\n Abra o WhatsApp no celular → Aparelhos conectados → Conectar aparelho.\n </p>\n {qr.image && <img src={qr.image} alt=\"QR\" className=\"h-48 w-48 rounded border bg-white p-2\" />}\n {qr.code && <p className=\"mt-2 font-mono text-lg tracking-widest\">{qr.code}</p>}\n </div>\n )}\n </div>\n )\n })}\n </SettingsGroup>\n )\n}\n","// Channel accounts — the numbers a message can leave by (migration 003).\n//\n// Read-only by construction: the table grants members SELECT and nothing else,\n// so this accessor has no write path to offer. Rows are created by the claim\n// function and advanced by the provider's webhooks.\nimport { getSupabaseClientOptional } from '@fayz-ai/core'\nimport { T } from './tables'\nimport type { Channel } from '../types'\n\n/** Whose number it is. `fallback` is the product's, shared by every tenant. */\nexport type MessagingChannelKind = 'fallback' | 'dedicated'\n\n/** The provider's own lifecycle, kept verbatim. Only `active` may carry a send. */\nexport type MessagingChannelStatus =\n | 'requested'\n | 'provisioning'\n | 'provisioned'\n | 'verifying'\n | 'active'\n | 'failed'\n | 'released'\n | 'disconnected'\n\nexport interface MessagingChannelAccount {\n id: string\n /** `null` on the product fallback row — it belongs to the software, not a tenant. */\n tenantId: string | null\n channel: Channel\n provider: string\n providerNumberId: string\n phoneE164: string | null\n kind: MessagingChannelKind\n status: MessagingChannelStatus\n displayName: string | null\n metadata: Record<string, unknown>\n createdAt: string\n updatedAt: string\n}\n\nexport interface ListMessagingChannelsOptions {\n /** Narrow to one provider ('tyxter'). Omitted: every provider in the pool. */\n provider?: string\n /** Narrow to one channel ('whatsapp'). */\n channel?: Channel\n /** Supabase client override; defaults to the globally registered one. */\n supabaseClient?: unknown\n}\n\ntype Row = Record<string, unknown>\n\nfunction mapChannel(r: Row): MessagingChannelAccount {\n return {\n id: String(r.id),\n tenantId: (r.tenant_id as string | null) ?? null,\n channel: (r.channel as Channel) ?? 'whatsapp',\n provider: String(r.provider ?? ''),\n providerNumberId: String(r.provider_number_id ?? ''),\n phoneE164: (r.phone_e164 as string | null) ?? null,\n kind: (r.kind as MessagingChannelKind) ?? 'dedicated',\n status: (r.status as MessagingChannelStatus) ?? 'requested',\n displayName: (r.display_name as string | null) ?? null,\n metadata: (r.metadata as Record<string, unknown>) ?? {},\n createdAt: String(r.created_at ?? ''),\n updatedAt: String(r.updated_at ?? ''),\n }\n}\n\n/**\n * Every number this tenant can send by — its own, plus the product fallback.\n *\n * No tenant filter is applied, and that is deliberate: RLS already returns the\n * tenant's rows and the fallback row, and the fallback has no tenant to filter\n * on. Filtering here would hide exactly the row the panel most needs to show.\n */\nexport async function listMessagingChannels(\n options: ListMessagingChannelsOptions = {},\n): Promise<MessagingChannelAccount[]> {\n const supabase = (options.supabaseClient ?? getSupabaseClientOptional()) as\n | { from: (t: string) => Row }\n | null\n if (!supabase) {\n throw new Error(\n '[plugin-conversations] Supabase client not available. Pass supabaseClient or register the global client via createFayzApp.',\n )\n }\n\n let q = (supabase.from(T.channels) as { select: (s: string) => Row }).select('*')\n if (options.provider) q = (q as { eq: (c: string, v: string) => Row }).eq('provider', options.provider)\n if (options.channel) q = (q as { eq: (c: string, v: string) => Row }).eq('channel', options.channel)\n // Dedicated before fallback: the tenant's own number is the answer whenever\n // there is one, and the readers of this list want it first.\n q = (q as { order: (c: string, o: unknown) => Row }).order('kind', { ascending: true })\n\n const { data, error } = (await q) as { data: Row[] | null; error: unknown }\n if (error) throw error\n return (data ?? []).map(mapChannel)\n}\n\n// ── The claim's lifecycle, as a thing the UI can render ─────────────────────\n//\n// Everything below is PURE. The claim itself is an edge function call and lives\n// in ../integrations/tyxter/data.ts; what belongs here is the vocabulary of the\n// table — which states a number walks through, which of them mean \"still\n// coming\", and which single row is the tenant's own. Keeping it here rather than\n// in the panel is what lets it be tested without React.\n\n/**\n * The happy path, in order. Observed live against the provider's sandbox on\n * 2026-08-24: a provision answered `requested` and the number then walked\n * `provisioned` → `verifying` → `active` in about a second. `provisioning` is in\n * the provider's enum and was not seen at 150ms polling — kept here because the\n * enum has it and a step missing from a timeline reads as a step that failed.\n */\nexport const CHANNEL_CLAIM_TIMELINE: MessagingChannelStatus[] = [\n 'requested',\n 'provisioning',\n 'provisioned',\n 'verifying',\n 'active',\n]\n\n/**\n * The states in which the tenant already has a claim and must not start another.\n *\n * Mirrors `LIVE_CHANNEL_STATUSES` in functions/_shared/tyxter-runtime.js, which\n * is where the edge function enforces it. Two copies because an edge function\n * may not import from src/ — the vitest suite asserts they agree, so a change to\n * one that is not made to the other fails a test rather than letting the panel\n * offer a button the server will refuse.\n */\nexport const LIVE_CHANNEL_STATUSES: MessagingChannelStatus[] = [\n 'requested',\n 'provisioning',\n 'provisioned',\n 'verifying',\n 'active',\n]\n\n/** Terminal, and not a reason to stop the tenant trying again. */\nexport function isDeadChannelStatus(status: MessagingChannelStatus): boolean {\n return !LIVE_CHANNEL_STATUSES.includes(status)\n}\n\n/** On its way, but not carrying messages yet — the state the panel polls in. */\nexport function isPendingChannelStatus(status: MessagingChannelStatus): boolean {\n return LIVE_CHANNEL_STATUSES.includes(status) && status !== 'active'\n}\n\n/** The tenant's own number, live or on its way. Null when they have none. */\nexport function findDedicatedChannel(\n channels: MessagingChannelAccount[],\n tenantId?: string | null,\n): MessagingChannelAccount | null {\n return channels.find(\n (channel) => channel.kind === 'dedicated'\n && (!tenantId || channel.tenantId === tenantId)\n && !isDeadChannelStatus(channel.status),\n ) ?? null\n}\n\n/** The product's shared number, which every tenant sends by until it has its own. */\nexport function findFallbackChannel(\n channels: MessagingChannelAccount[],\n): MessagingChannelAccount | null {\n return channels.find((channel) => channel.kind === 'fallback') ?? null\n}\n\n/**\n * Can this tenant ask for a number right now?\n *\n * The same rule the edge function answers 409 on, asked before the round trip\n * so the button is disabled rather than the click refused.\n */\nexport function canClaimDedicated(\n channels: MessagingChannelAccount[],\n tenantId?: string | null,\n): boolean {\n return findDedicatedChannel(channels, tenantId) === null\n}\n","import * as React from 'react'\nimport { WhatsAppProviders } from './WhatsAppProviders'\nimport { useTranslation } from '@fayz-ai/core'\nimport { PluginSettingsPanel, SettingsGroup } from '@fayz-ai/admin'\nimport { Badge } from '@fayz-ai/ui'\nimport {\n listMessagingChannels,\n isPendingChannelStatus,\n type MessagingChannelAccount,\n} from '../data/channels'\n\n// ---------------------------------------------------------------------------\n// The inbox's settings screen.\n//\n// It exists for one reason: without it the WhatsApp connector is UNREACHABLE.\n// Connectors are surfaced by PluginSettingsPanel's Integrations tab, and that\n// tab only exists inside a plugin settings panel — so a plugin that declares a\n// connector and no settings route has declared a connector nobody can open.\n// Every app that mounted the inbox alone was in exactly that state.\n//\n// The General tab is deliberately not a copy of the connector's panel. That one\n// manages the Tyxter account (claim a number, register with Meta). This one\n// answers the question somebody asks BEFORE they know a connector exists:\n// \"why isn't my WhatsApp showing up in here?\" — so it states, plainly, which\n// numbers this inbox can send and receive by, and sends you one tab over when\n// the answer is \"none\".\n// ---------------------------------------------------------------------------\n\nfunction channelLabel(\n account: MessagingChannelAccount,\n t: (key: string) => string,\n): string {\n const key = `conversations.filter.${account.channel}`\n const translated = t(key)\n return translated === key ? account.channel : translated\n}\n\nfunction ChannelRow({ account }: { account: MessagingChannelAccount }) {\n const t = useTranslation()\n const pending = isPendingChannelStatus(account.status)\n return (\n <div className=\"flex items-center justify-between gap-3 border-b py-2.5 last:border-b-0\">\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium\">\n {account.phoneE164 ?? account.displayName ?? channelLabel(account, t)}\n </p>\n <p className=\"text-xs text-muted-foreground\">\n {channelLabel(account, t)}\n {' · '}\n {t(\n account.kind === 'dedicated'\n ? 'conversations.settings.channelDedicated'\n : 'conversations.settings.channelShared',\n )}\n </p>\n </div>\n {/* The provider's own word for the state, translated where we have a\n translation and passed through where we do not — a status invented\n here would drift from what the connector panel says one tab over. */}\n <Badge variant={account.status === 'active' ? 'default' : pending ? 'secondary' : 'outline'}>\n {t(`conversations.settings.status.${account.status}`) === `conversations.settings.status.${account.status}`\n ? account.status\n : t(`conversations.settings.status.${account.status}`)}\n </Badge>\n </div>\n )\n}\n\nfunction ConversationsGeneralSettings() {\n const t = useTranslation()\n const [channels, setChannels] = React.useState<MessagingChannelAccount[] | null>(null)\n // `null` = still loading, `[]` = loaded and empty, `'error'` = could not ask.\n // Three states and not two, because \"nenhum número conectado\" and \"não\n // consegui perguntar\" send somebody to opposite places.\n const [failed, setFailed] = React.useState(false)\n\n React.useEffect(() => {\n let cancelled = false\n listMessagingChannels()\n .then((rows) => { if (!cancelled) setChannels(rows) })\n .catch(() => { if (!cancelled) { setFailed(true); setChannels([]) } })\n return () => { cancelled = true }\n }, [])\n\n return (\n <div className=\"space-y-6\">\n {/* Primeiro por ONDE sai, depois QUAIS números saíram. A ordem inversa\n mostrava uma lista vazia antes de explicar que faltava escolher um\n provedor — e uma lista vazia parece defeito, não pendência. */}\n <WhatsAppProviders />\n\n <SettingsGroup\n title={t('conversations.settings.channels')}\n description={t('conversations.settings.channelsHelp')}\n >\n {channels === null && (\n <p className=\"py-2 text-sm text-muted-foreground\">{t('conversations.list.loading')}</p>\n )}\n {channels !== null && failed && (\n <p className=\"py-2 text-sm text-muted-foreground\">\n {t('conversations.settings.channelsUnavailable')}\n </p>\n )}\n {channels !== null && !failed && channels.length === 0 && (\n <p className=\"py-2 text-sm text-muted-foreground\">\n {t('conversations.settings.channelsEmpty')}\n </p>\n )}\n {channels?.map((account) => <ChannelRow key={account.id} account={account} />)}\n </SettingsGroup>\n </div>\n )\n}\nConversationsGeneralSettings.displayName = 'ConversationsGeneralSettings'\n\nexport function ConversationsSettingsTab() {\n const t = useTranslation()\n return (\n <PluginSettingsPanel\n title={t('conversations.settings.title')}\n generalSettings={<ConversationsGeneralSettings />}\n hostPluginId=\"conversations\"\n routeBase=\"/settings/conversations\"\n />\n )\n}\nConversationsSettingsTab.displayName = 'ConversationsSettingsTab'\n","import { countByTenant, type OnboardingProgramDef } from '@fayz-ai/core'\nimport { T } from '../data/tables'\n\n/**\n * Implantação da caixa de entrada — um canal e um hábito.\n *\n * Ordem 30, à frente da base do workspace (50) e do CRM (80), e isso é uma\n * opinião: numa operação que atende, o cliente escreve antes de qualquer outra\n * coisa acontecer. Convidar a equipe e desenhar o funil são trabalho de quem já\n * tem alguém escrevendo; ligar o canal é o que faz aparecer o primeiro.\n *\n * Dois passos, e o segundo não é uma tela. A caixa de entrada é o módulo que\n * mais parece pronto vazio: ela abre, tem filtro por canal, tem busca — e não\n * chega mensagem nenhuma porque ninguém ligou o número. O passo que fecha é o\n * único que prova que o caminho inteiro existe: uma mensagem que entrou e foi\n * respondida daqui.\n */\nexport function buildConversationsOnboarding(): OnboardingProgramDef {\n return {\n id: 'conversations',\n title: 'Quem fala com você',\n order: 30,\n role: 'frontdesk',\n instructions: `Você está ligando a caixa de entrada. O risco aqui não é a pessoa não achar a tela — é ela achar a tela, ver que está vazia e concluir que o produto não funciona.\n\n- Comece pelo CANAL. Sem número ligado não entra mensagem nenhuma, e todo o resto do módulo é decoração. O caminho é Configurações › Conversas › Integrações.\n- Explique a diferença entre o número da plataforma e um número dedicado ANTES de ela escolher: no compartilhado ela já começa a receber hoje; no dedicado sai o nome dela, mas ainda passa por um cadastro na Meta.\n- O segundo passo é a prova: peça para ela mandar uma mensagem do próprio celular para o número e responder daqui. É o que mostra que ida e volta funcionam.\n- Se ela já tem WhatsApp Business no celular, diga com todas as letras o que muda: as conversas passam a ficar aqui, onde a equipe inteira vê, em vez de num aparelho só.`,\n steps: [\n {\n key: 'channel',\n title: 'Ligue o canal onde seu cliente já fala',\n description: 'Sem número conectado, a caixa de entrada abre vazia para sempre.',\n icon: 'MessageCircle',\n route: '/settings/conversations/_integrations',\n priority: 'urgent',\n // O passo É a tela: um app que monte o inbox sem a rota de configurações\n // não tem onde ligar canal nenhum, e a linha ficaria aberta para sempre.\n requiresRoute: true,\n agentHint:\n 'Abra Integrações e apresente as duas opções pelo que elas custam à pessoa HOJE: o número da plataforma começa a receber agora, o dedicado sai com o nome dela mas espera o cadastro na Meta.',\n howTo:\n 'Configurações › Conversas › Integrações › Tyxter. O número da plataforma já está ligado e é compartilhado com outros negócios — dá para começar a receber por ele hoje. Um número dedicado sai só com o seu nome, libera os seus próprios modelos de mensagem e tem limite de envio próprio; ele é alugado no mês e ainda passa por um cadastro rápido na Meta antes do primeiro envio.',\n celebrate: 'Canal ligado — agora dá para te acharem 📲',\n script: [\n { kind: 'goto', route: '/settings/conversations/_integrations' },\n {\n kind: 'ask',\n text: 'Estamos nas Integrações da caixa de entrada. Mostre o número da plataforma e o dedicado, explique o que muda em cada um e ajude a pessoa a escolher.',\n },\n ],\n check: async () => (await countByTenant(T.channels)) > 0,\n },\n {\n key: 'first-reply',\n title: 'Receba e responda a primeira mensagem',\n description: 'A prova de que a ida e a volta funcionam.',\n icon: 'Send',\n route: '/conversations',\n priority: 'high',\n agentHint:\n 'Não é passo de cadastro. Peça para a pessoa mandar uma mensagem do próprio celular para o número ligado e responder aqui de dentro — e fique com ela até a resposta chegar do outro lado.',\n howTo:\n 'Pegue o seu celular, mande uma mensagem qualquer para o número que você acabou de ligar e volte aqui: a conversa aparece em Conversas. Responda por aqui e confira que chegou no celular. É o teste que prova que a caixa de entrada está no ar — e é também como a equipe vai trabalhar todo dia, com a conversa num lugar que todo mundo vê em vez de num aparelho só.',\n celebrate: 'Ida e volta funcionando 💬',\n // A conversa existe assim que a primeira mensagem entra, então a\n // contagem de threads é a checagem — e não a de mensagens, que seria\n // fechada por um envio que ninguém respondeu.\n check: async () => (await countByTenant(T.conversations)) > 0,\n },\n ],\n }\n}\n","// The one place that names the Tyxter number-claim data plane.\n//\n// Kept apart from connectorDef.tsx for the reason data.ts exists on the Kiwify\n// and Stripe connectors: the credential path is the half that moves when the\n// platform broker lands, and when it does only this file should change.\nimport {\n CONNECTOR_RUNTIME_TOKEN_HEADER,\n connectorRuntimeToken,\n getSupabaseClientOptional,\n} from '@fayz-ai/core'\n\n/** The edge function that holds every phone-number call this connector makes. */\nexport const TYXTER_NUMBER_CLAIM_FUNCTION = 'tyxter-number-claim'\n\n/** The channel row, as the function hands it back. A subset of the table on purpose. */\nexport interface ClaimedChannel {\n id: string\n channel: string\n provider: string\n /** Null while the row is still a reservation with no number behind it. */\n providerNumberId: string | null\n phoneE164: string | null\n kind: string\n status: string\n displayName: string | null\n}\n\n/**\n * Where to send the human to finish the Meta registration, and how good the\n * link is.\n *\n * `source` is surfaced rather than hidden because the three are not equivalent:\n * 'session' returns the person to this very panel and names the tenant to Meta;\n * 'error_link' is the provider's own pre-targeted page; 'default' is the generic\n * dashboard, which works but assumes the browser is signed into the right\n * organization. The panel says less when the source is worse.\n */\nexport interface MetaRegistrationHandoff {\n source: 'session' | 'error_link' | 'default'\n url: string\n note: string | null\n sessionId?: string\n expiresAt?: string | null\n}\n\nexport interface ClaimResult {\n channel: ClaimedChannel\n metaRegistration: MetaRegistrationHandoff | null\n}\n\ninterface MinimalClient {\n functions: {\n invoke: (\n name: string,\n options: { body: unknown; headers?: Record<string, string> },\n ) => Promise<{ data: unknown; error: { message: string } | null }>\n }\n}\n\nfunction client(): MinimalClient {\n const supabase = getSupabaseClientOptional() as MinimalClient | null\n if (!supabase) throw new Error('Sem conexão com o banco para falar com a Tyxter.')\n return supabase\n}\n\n/**\n * One call to the data plane.\n *\n * The Supabase session rides in `Authorization` — the gateway checks it, and the\n * function re-checks membership in the tenant against it, because renting a\n * number costs money every month.\n *\n * The runtime token is BEST-EFFORT here, unlike on Kiwify. There it identifies\n * whose credential to redeem and a call without it is unanswerable; here the\n * credential is the platform's own, held once for the whole product, and the\n * function falls back to machine redemption keyed by the tenant. An app that\n * installed no token source can still claim a number, which is the right\n * outcome — refusing would be enforcing a requirement that does not exist.\n */\nasync function invoke<T>(body: Record<string, unknown>): Promise<T> {\n let headers: Record<string, string> | undefined\n try {\n headers = { [CONNECTOR_RUNTIME_TOKEN_HEADER]: await connectorRuntimeToken({ tenantId: String(body.tenantId ?? '') }) }\n } catch {\n headers = undefined\n }\n\n const { data, error } = await client().functions.invoke(TYXTER_NUMBER_CLAIM_FUNCTION, {\n body,\n ...(headers ? { headers } : {}),\n })\n if (error) throw new Error(error.message)\n return data as T\n}\n\n/**\n * A page address, cleaned up for `return_url`.\n *\n * The provider's allowlist refuses a URL carrying a fragment, credentials or a\n * non-https scheme, so the fragment goes — and this app is a hash router, which\n * means the fragment is the WHOLE route. Losing it returns the human to the\n * app's front door rather than to this panel, which is a smaller failure than a\n * session the provider refuses to create at all.\n */\nexport function returnUrlFrom(href: string | null | undefined): string | null {\n if (!href) return null\n try {\n const url = new URL(href)\n url.hash = ''\n url.username = ''\n url.password = ''\n return url.toString()\n } catch {\n return null\n }\n}\n\n/** The page the human is standing on, as a `return_url` the provider will accept. */\nexport function currentReturnUrl(): string | null {\n return typeof window === 'undefined' ? null : returnUrlFrom(window.location.href)\n}\n\n/** Rent a dedicated number for this tenant, in the given area code. */\nexport function claimDedicatedNumber(input: { tenantId: string; ddd: string }): Promise<ClaimResult> {\n return invoke<ClaimResult>({\n action: 'claim',\n tenantId: input.tenantId,\n ddd: input.ddd,\n returnUrl: currentReturnUrl(),\n })\n}\n\n/**\n * Re-read the tenant's numbers at the provider and sync the rows.\n *\n * The poll the panel runs while a claim is pending. It is not redundant with the\n * webhook: four of the five lifecycle steps emit no event at all (verified\n * live — a full provision to `active` produced zero `phone_number.*` events), so\n * without this the row would sit on \"Solicitado\" until the day the number is\n * disconnected.\n */\nexport function refreshDedicatedNumbers(tenantId: string): Promise<{ channels: ClaimedChannel[] }> {\n return invoke<{ channels: ClaimedChannel[] }>({ action: 'status', tenantId })\n}\n\n/** The link that hands the human to Meta's Embedded Signup, which no key can do headlessly. */\nexport function metaRegistrationHandoff(tenantId: string): Promise<{\n metaRegistration: MetaRegistrationHandoff | null\n channel: ClaimedChannel\n}> {\n return invoke({ action: 'handoff', tenantId, returnUrl: currentReturnUrl() })\n}\n","// \"Can this app take a Pix inside a conversation?\" — asked from the browser,\n// answered by the provider.\n//\n// It is a function call and not a table read because the answer does not live in\n// this pool. It lives at the provider, behind the PLATFORM's key, which a\n// browser must never hold — so `tyxter-payment-preflight` asks on the panel's\n// behalf and hands back only the verdict. There is nothing to cache here: a\n// merchant who has just finished the credential handoff is exactly the person\n// pressing the button again.\nimport { getActiveTenantId, getSupabaseClientOptional } from '@fayz-ai/core'\n\n/** The edge function that holds both questions. */\nexport const PAYMENT_PREFLIGHT_FUNCTION = 'tyxter-payment-preflight'\n\n/**\n * Which rail is actually behind the payments channel.\n *\n * The distinction that matters and is easy to collapse: `sandbox_default` is\n * READY — charges open, settle and fire webhooks — but the money is not real,\n * and the transparent-Pix provider options are refused. A green badge over it\n * is how a salon finds out on the day of its first real charge, so the panel\n * says \"sandbox\" beside the tick instead.\n */\nexport type PaymentActiveMode = 'sandbox_default' | 'iniciador' | 'abacate_pay'\n\nexport interface PaymentReadiness {\n /** The provider's own verdict. True for the sandbox fixture too — see `sandbox`. */\n ready: boolean\n status: string\n mode: PaymentActiveMode | null\n /** `mode === 'sandbox_default'`: ready, but not with real money. */\n sandbox: boolean\n environment: string | null\n /** Why not, when the provider says why. */\n reason: string | null\n}\n\n/** The browser handoff where the merchant enters credentials this app never sees. */\nexport interface PaymentSetupSession {\n setupUrl: string | null\n requestId: string | null\n expiresAt: string | null\n environment: string | null\n}\n\ninterface MinimalClient {\n functions: {\n invoke: (\n name: string,\n options: { body: unknown },\n ) => Promise<{ data: unknown; error: { message: string } | null }>\n }\n}\n\nasync function invoke<T>(body: Record<string, unknown>, tenantId?: string): Promise<T> {\n const supabase = getSupabaseClientOptional() as MinimalClient | null\n if (!supabase) throw new Error('Sem conexão com o banco para verificar os pagamentos.')\n const tenant = tenantId ?? getActiveTenantId()\n if (!tenant) throw new Error('Sem negócio selecionado.')\n\n const { data, error } = await supabase.functions.invoke(PAYMENT_PREFLIGHT_FUNCTION, {\n body: { ...body, tenantId: tenant },\n })\n if (!error) return data as T\n\n // supabase-js buries the function's own body in the error context. Without\n // digging it out every refusal reads as \"Edge Function returned a non-2xx\n // status code\", which is what \"not connected\", \"no key\" and a CORS preflight\n // all look like — while the function answered each with a sentence.\n let message = ''\n try {\n const payload = (await (error as { context?: { json?: () => Promise<unknown> } })\n ?.context?.json?.()) as { message?: string; error?: string } | undefined\n message = String(payload?.message ?? payload?.error ?? '').trim()\n } catch {\n /* no JSON body */\n }\n throw new Error(message || error.message)\n}\n\nexport async function readPaymentReadiness(tenantId?: string): Promise<PaymentReadiness> {\n const answer = await invoke<{ payments: PaymentReadiness }>({ action: 'status' }, tenantId)\n return answer.payments\n}\n\nexport async function openPaymentSetupSession(tenantId?: string): Promise<PaymentSetupSession> {\n return await invoke<PaymentSetupSession>({ action: 'connect' }, tenantId)\n}\n\n/**\n * Is the `pay` action safe to offer in a message template?\n *\n * The sandbox fixture counts, deliberately: a build being dogfooded on sandbox\n * credentials must be able to exercise the whole flow, and the panel is where\n * the \"sandbox\" qualifier is shown. What is refused is a channel with nothing\n * selected behind it, where a Pix button would produce a charge that never opens.\n */\nexport function canOfferPayment(readiness: PaymentReadiness | null): boolean {\n if (!readiness?.ready) return false\n return readiness.mode !== null\n}\n","// Tyxter — WhatsApp as a channel of the inbox, and the first messaging\n// connector on the standard (docs/integrations/CONNECTORS.md).\n//\n// It is the connector with NO connect form at all, and that is the whole design\n// rather than an omission. FayaLabs holds one Tyxter project per product and\n// one dashboard key with it; a tenant gets WhatsApp by agreeing to use it, not\n// by going to find a key that was never issued to them. So `fields` is empty,\n// no credential sink is involved, and connecting writes one thing: the\n// connections row that says this tenant switched it on. The key is redeemed\n// server-side, per call, by the edge functions that land in WP3–WP6.\n//\n// `authKind: 'api-key'` still, and not 'oauth': Tyxter has no consent screen and\n// no app to install, so declaring OAuth would ship a button with no flow behind\n// it (the Bling failure named in the standard).\n//\n// No SQL of its own for the CONNECTION — that is a connections row (029).\n// The table this plugin does add, plg_conversations_channels, is not connection\n// state: it is which phone number a message leaves by, which outlives any one\n// connector and is read by the inbox itself.\nimport React, { useCallback, useEffect, useRef, useState } from 'react'\nimport { ExternalLink, Loader2 } from 'lucide-react'\nimport { Button, Input, toast } from '@fayz-ai/ui'\nimport {\n connectionRuns,\n connectionStatus,\n createConnectionStore,\n useActiveTenantId,\n type ConnectorDefinition,\n} from '@fayz-ai/core'\nimport {\n CHANNEL_CLAIM_TIMELINE,\n canClaimDedicated,\n findDedicatedChannel,\n findFallbackChannel,\n isPendingChannelStatus,\n listMessagingChannels,\n type MessagingChannelAccount,\n type MessagingChannelStatus,\n} from '../../data/channels'\nimport {\n claimDedicatedNumber,\n metaRegistrationHandoff,\n refreshDedicatedNumbers,\n type MetaRegistrationHandoff,\n} from './data'\nimport {\n canOfferPayment,\n openPaymentSetupSession,\n readPaymentReadiness,\n type PaymentReadiness,\n} from '../../data/payment-preflight'\n\nexport const TYXTER_CONNECTOR_ID = 'tyxter'\n\n/**\n * Four seconds.\n *\n * Nobody is staring at a spinner here — the caller is an automation reacting to\n * a booking — but the budget is what stops a slow provider from holding the\n * automation executor's worker while the next twenty events queue behind it.\n * Tyxter answers `POST /v1/messages` with a 202 and does the delivery\n * asynchronously, so the call being timed is an accept, not a send.\n */\nexport const TYXTER_LATENCY_BUDGET_MS = 4000\n\n// One account per tenant: the platform's project is the same one for everybody,\n// and what the row records is consent, so there is no instance to key on.\nconst store = createConnectionStore(TYXTER_CONNECTOR_ID)\n\nconst KIND_LABEL: Record<MessagingChannelAccount['kind'], string> = {\n fallback: 'Número da plataforma',\n dedicated: 'Número dedicado',\n}\n\nconst STATUS_LABEL: Record<MessagingChannelAccount['status'], string> = {\n requested: 'Solicitado',\n provisioning: 'Em preparação',\n provisioned: 'Preparado',\n verifying: 'Em verificação',\n active: 'Ativo',\n failed: 'Falhou',\n released: 'Liberado',\n disconnected: 'Desconectado',\n}\n\n/** How long between polls while a claim is on its way. */\nconst POLL_INTERVAL_MS = 5000\n\n/**\n * The lifecycle, drawn.\n *\n * Five steps and only the last one sends. Drawn as a list rather than a single\n * badge because the wait is the thing being explained: a person who asked for a\n * phone line and sees \"Em preparação\" for two minutes needs to know how many\n * steps are left, not just which one is current.\n */\nfunction ClaimTimeline({ status }: { status: MessagingChannelStatus }) {\n if (status === 'failed' || status === 'released' || status === 'disconnected') {\n return (\n <p className=\"text-xs text-destructive\">\n {status === 'failed'\n ? 'O pedido não foi concluído. Você pode tentar de novo.'\n : `Este número saiu do ar (${STATUS_LABEL[status]}).`}\n </p>\n )\n }\n const current = CHANNEL_CLAIM_TIMELINE.indexOf(status)\n return (\n <ol className=\"space-y-1\">\n {CHANNEL_CLAIM_TIMELINE.map((step, index) => {\n const done = current > index\n const now = current === index\n return (\n <li\n key={step}\n className={`flex items-center gap-2 text-xs ${\n now ? 'font-medium text-foreground' : done ? 'text-muted-foreground' : 'text-muted-foreground/60'\n }`}\n >\n <span aria-hidden className={`h-1.5 w-1.5 rounded-full ${done || now ? 'bg-primary' : 'bg-border'}`} />\n {STATUS_LABEL[step]}\n {now && status !== 'active' && <Loader2 className=\"h-3 w-3 animate-spin\" />}\n </li>\n )\n })}\n </ol>\n )\n}\n\n/**\n * The handoff a machine cannot make.\n *\n * A rented number is not a registered one: Meta has never seen it, and WhatsApp\n * Embedded Signup is a browser window a person clicks through — the provider's\n * own connect endpoint refuses a Salvy number with `meta_registration_required`\n * and says there is no headless equivalent. So this is the honest end of the\n * flow, and it is prominent rather than tucked away, because a claim that stops\n * here without saying so looks like a claim that worked.\n */\nfunction MetaRegistrationCard({ handoff }: { handoff: MetaRegistrationHandoff }) {\n return (\n <div className=\"space-y-2 rounded-md border border-primary/40 bg-primary/5 p-3\">\n <p className=\"text-xs font-medium text-foreground\">Falta liberar o número na Meta</p>\n <p className=\"text-xs text-muted-foreground\">\n O número já é seu, mas ainda não pode enviar: a Meta exige que alguém conclua o cadastro do\n WhatsApp numa janela do navegador. É rápido, e só precisa ser feito uma vez.\n </p>\n <Button asChild size=\"sm\">\n <a href={handoff.url} target=\"_blank\" rel=\"noopener noreferrer\">\n Concluir cadastro na Meta\n <ExternalLink className=\"ml-1.5 h-3.5 w-3.5\" />\n </a>\n </Button>\n {handoff.source === 'default' && (\n <p className=\"text-[11px] text-muted-foreground\">\n Confira se a conta aberta no navegador é a certa antes de concluir.\n </p>\n )}\n </div>\n )\n}\n\n/**\n * Can the customer pay inside the conversation, and if not, what has to happen?\n *\n * The answer is not in this pool. It is at the provider, behind the platform's\n * key, so `tyxter-payment-preflight` asks and this renders the verdict. Three\n * states and they are deliberately not two:\n *\n * ready, real a payment connection is selected. Nothing to do.\n * ready, sandbox charges open and settle, with money that is not real.\n * Shown as ready WITH the word \"sandbox\", because a green\n * tick over a fixture is discovered on the day of the first\n * real charge.\n * not ready the handoff button, which opens a session at the provider\n * rather than linking to a dashboard the merchant then has\n * to navigate.\n *\n * The credentials are typed at the provider and never reach this app. That is\n * the accepted redundancy with the app's own AbacatePay connector, and it is\n * also the reason this panel has no form: a secret that never enters the\n * product is one the product cannot leak.\n */\nfunction TyxterPaymentsPanel() {\n const [readiness, setReadiness] = useState<PaymentReadiness | null>(null)\n const [error, setError] = useState<string | null>(null)\n const [loading, setLoading] = useState(true)\n const [handoff, setHandoff] = useState<string | null>(null)\n const [opening, setOpening] = useState(false)\n\n useEffect(() => {\n let cancelled = false\n void readPaymentReadiness()\n .then((value) => { if (!cancelled) setReadiness(value) })\n .catch((caught: unknown) => {\n if (!cancelled) setError(caught instanceof Error ? caught.message : 'Não deu para verificar agora.')\n })\n .finally(() => { if (!cancelled) setLoading(false) })\n return () => { cancelled = true }\n }, [])\n\n const connect = async () => {\n setOpening(true)\n setError(null)\n try {\n const session = await openPaymentSetupSession()\n setHandoff(session.setupUrl)\n // Opened by the merchant and not by us: a popup fired from an async\n // callback is the one a browser blocks, and a blocked popup reads as a\n // button that does nothing.\n } catch (caught) {\n setError(caught instanceof Error ? caught.message : 'Não deu para abrir a configuração.')\n } finally {\n setOpening(false)\n }\n }\n\n const ready = canOfferPayment(readiness)\n\n return (\n <div className=\"space-y-3 border-t pt-4\">\n <div>\n <p className=\"text-sm font-medium text-foreground\">Pagamento no WhatsApp</p>\n <p className=\"mt-0.5 text-xs text-muted-foreground\">\n Com isso ligado, o cliente recebe o Pix na própria conversa e o agendamento fica marcado\n como pago sozinho.\n </p>\n </div>\n\n {loading && <p className=\"text-xs text-muted-foreground\">Verificando…</p>}\n\n {!loading && ready && (\n <p className=\"text-xs text-foreground\">\n <span className=\"font-medium text-emerald-600\">Pronto para cobrar</span>\n {readiness?.sandbox && (\n <span className=\"ml-2 rounded bg-amber-100 px-1.5 py-0.5 text-[11px] font-medium text-amber-800\">\n sandbox — o dinheiro não é real\n </span>\n )}\n </p>\n )}\n\n {!loading && !ready && (\n <div className=\"space-y-2\">\n <p className=\"text-xs text-muted-foreground\">\n Ainda não dá para cobrar por aqui. A conta de pagamento é configurada na Tyxter, em uma\n página deles — a sua chave não passa por este app.\n {readiness?.reason ? ` (${readiness.reason})` : ''}\n </p>\n {handoff ? (\n <a\n href={handoff}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"inline-block rounded-md border px-3 py-1.5 text-xs font-medium hover:bg-muted\"\n >\n Abrir a configuração de pagamento →\n </a>\n ) : (\n <button\n type=\"button\"\n onClick={() => { void connect() }}\n disabled={opening}\n className=\"rounded-md border px-3 py-1.5 text-xs font-medium hover:bg-muted disabled:opacity-50\"\n >\n {opening ? 'Abrindo…' : 'Configurar pagamento'}\n </button>\n )}\n </div>\n )}\n\n {error && <p className=\"text-xs text-destructive\">{error}</p>}\n </div>\n )\n}\n\n/**\n * Which numbers this tenant sends by, and the button that gets it one of its own.\n *\n * ── Why this polls ──────────────────────────────────────────────────────────\n * Verified against the provider's sandbox on 2026-08-24: provisioning a number\n * walked requested → provisioned → verifying → active in about a second and\n * emitted ZERO `phone_number.*` webhooks. The deliverable events are verified /\n * disconnected / released / quality_changed / messaging_limit_*, and none of\n * them fired. So the webhook (WP4) is not the mechanism that advances a claim —\n * asking is, and this panel asks every few seconds while the row is pending and\n * stops the moment it is not.\n */\nfunction TyxterExtraPanel() {\n const tenantId = useActiveTenantId()\n const [channels, setChannels] = useState<MessagingChannelAccount[] | null>(null)\n const [error, setError] = useState<string | null>(null)\n const [ddd, setDdd] = useState('')\n const [claiming, setClaiming] = useState(false)\n const [handoff, setHandoff] = useState<MetaRegistrationHandoff | null>(null)\n const mounted = useRef(true)\n\n useEffect(() => {\n mounted.current = true\n return () => { mounted.current = false }\n }, [])\n\n const load = useCallback(async () => {\n try {\n const rows = await listMessagingChannels({ provider: TYXTER_CONNECTOR_ID, channel: 'whatsapp' })\n if (mounted.current) { setChannels(rows); setError(null) }\n } catch {\n if (mounted.current) setError('Não conseguimos ler os números agora.')\n }\n }, [])\n\n useEffect(() => { void load() }, [load])\n\n const dedicated = channels ? findDedicatedChannel(channels, tenantId) : null\n const fallback = channels ? findFallbackChannel(channels) : null\n const pending = dedicated ? isPendingChannelStatus(dedicated.status) : false\n\n // The poll, and the reason it is keyed on `pending`: a screen that keeps\n // asking after the answer arrived is a cost with no reader.\n useEffect(() => {\n if (!pending || !tenantId) return undefined\n const timer = setInterval(() => {\n void refreshDedicatedNumbers(tenantId).then(() => load()).catch(() => {})\n }, POLL_INTERVAL_MS)\n return () => clearInterval(timer)\n }, [pending, tenantId, load])\n\n // The link is asked for once the number exists and while it cannot send.\n // Not during the wait — there is nothing to register yet — and not once the\n // row carries a Meta id, because asking then costs two provider calls per\n // per panel open to be told what the row already said.\n const registered = !!dedicated?.metadata?.meta_phone_number_id\n useEffect(() => {\n if (!tenantId || !dedicated || dedicated.status !== 'active' || registered || handoff) return\n void metaRegistrationHandoff(tenantId)\n .then((answer) => { if (mounted.current) setHandoff(answer.metaRegistration) })\n .catch(() => {})\n }, [tenantId, dedicated, registered, handoff])\n\n const canClaim = !!channels && !!tenantId && canClaimDedicated(channels, tenantId)\n\n async function onClaim() {\n if (!tenantId || !/^\\d{2}$/.test(ddd)) return\n setClaiming(true)\n try {\n const result = await claimDedicatedNumber({ tenantId, ddd })\n setHandoff(result.metaRegistration)\n setDdd('')\n await load()\n toast.success('Número solicitado. Acompanhe aqui o andamento.')\n } catch (caught) {\n toast.error(caught instanceof Error ? caught.message : 'Não foi possível pedir o número.')\n } finally {\n if (mounted.current) setClaiming(false)\n }\n }\n\n return (\n <div className=\"space-y-3\">\n <div>\n <p className=\"text-sm font-medium text-foreground\">Números</p>\n <p className=\"mt-0.5 text-xs text-muted-foreground\">\n Por onde as suas mensagens de WhatsApp saem hoje.\n </p>\n </div>\n\n {error && <p className=\"text-xs text-destructive\">{error}</p>}\n\n {channels && channels.length === 0 && (\n <p className=\"text-xs text-muted-foreground\">\n Nenhum número configurado ainda. Assim que a plataforma liberar o número compartilhado,\n ele aparece aqui.\n </p>\n )}\n\n {channels && channels.length > 0 && (\n <ul className=\"space-y-2\">\n {channels.map((channel) => (\n <li\n key={channel.id}\n className=\"flex flex-wrap items-baseline justify-between gap-2 rounded-md border bg-muted/30 p-3 text-xs\"\n >\n <span className=\"font-medium text-foreground\">\n {channel.phoneE164 ?? channel.providerNumberId}\n </span>\n <span className=\"text-muted-foreground\">\n {KIND_LABEL[channel.kind]} · {STATUS_LABEL[channel.status]}\n </span>\n </li>\n ))}\n </ul>\n )}\n\n {dedicated && (\n <div className=\"space-y-2 rounded-md border p-3\">\n <p className=\"text-xs font-medium text-foreground\">Seu número dedicado</p>\n <ClaimTimeline status={dedicated.status} />\n {dedicated.status === 'active' && !handoff && (\n <p className=\"text-xs text-muted-foreground\">\n Ativo. As suas mensagens saem por ele, com o seu nome e sem dividir com ninguém.\n </p>\n )}\n </div>\n )}\n\n {handoff && <MetaRegistrationCard handoff={handoff} />}\n\n {canClaim && (\n <div className=\"space-y-2 border-t pt-3\">\n <p className=\"text-xs font-medium text-foreground\">Quer um número só seu?</p>\n <p className=\"text-xs text-muted-foreground\">\n {fallback\n ? 'Hoje as suas mensagens saem pelo número da plataforma, dividido com outros negócios. Um número dedicado sai só com o seu nome, libera os seus próprios modelos de mensagem e tem limite de envio próprio.'\n : 'Um número dedicado sai só com o seu nome e tem limite de envio próprio.'}\n </p>\n <div className=\"flex items-end gap-2\">\n <label className=\"text-xs text-muted-foreground\">\n DDD\n <Input\n value={ddd}\n onChange={(e) => setDdd(e.target.value.replace(/\\D/g, '').slice(0, 2))}\n inputMode=\"numeric\"\n placeholder=\"21\"\n className=\"mt-1 w-20\"\n aria-label=\"DDD do número dedicado\"\n />\n </label>\n <Button size=\"sm\" disabled={claiming || !/^\\d{2}$/.test(ddd)} onClick={() => void onClaim()}>\n {claiming && <Loader2 className=\"mr-1.5 h-3.5 w-3.5 animate-spin\" />}\n Solicitar número dedicado\n </Button>\n </div>\n <p className=\"text-[11px] text-muted-foreground\">\n O número é alugado no mês e, depois que chegar, ainda precisa de um cadastro rápido na\n Meta para começar a enviar.\n </p>\n </div>\n )}\n\n <TyxterPaymentsPanel />\n </div>\n )\n}\n\nexport const tyxterConnectorDef: ConnectorDefinition = {\n id: TYXTER_CONNECTOR_ID,\n hostPluginId: 'conversations',\n name: 'Tyxter',\n description:\n 'WhatsApp já pronto: confirmações e lembretes saem por um número da plataforma, e a resposta do cliente cai na sua caixa de entrada.',\n icon: 'MessageCircle',\n logoUrl: 'https://assets.fayz.ai/connectors/tyxter.png',\n category: 'messaging',\n authKind: 'api-key',\n // Sending is in line with the automation that asked for it; the reply, the\n // delivery status and the template approvals arrive later and file runs.\n planes: ['request', 'sync'],\n direction: 'outbound',\n docsUrl: 'https://tyxter.com/docs',\n // A message that does not go out must never take the booking with it. The\n // opposite verdict from a payment gateway, and for the opposite reason.\n latencyBudgetMs: TYXTER_LATENCY_BUDGET_MS,\n onFailure: 'degrade',\n permissions: [\n {\n kind: 'data',\n resource: 'conversation',\n actions: ['read', 'write'],\n reason:\n 'Abrir a conversa do cliente no WhatsApp e mantê-la em dia — é onde a mensagem enviada e a resposta dele ficam juntas.',\n },\n {\n kind: 'data',\n resource: 'message',\n actions: ['read', 'write'],\n reason:\n 'Guardar cada mensagem enviada e recebida, com o status que o WhatsApp devolve (enviada, entregue, lida).',\n },\n {\n kind: 'data',\n resource: 'message_template',\n actions: ['read', 'write'],\n reason:\n 'Ler os modelos de mensagem deste app e guardar o que a Meta respondeu sobre cada um: aprovado, recusado ou pausado.',\n },\n ],\n // No form: the key is the platform's. The connect step says so on its own\n // (`connectors.authorize.nothingToFill`) rather than drawing an empty grid.\n fields: [],\n invokedAt: [\n {\n key: 'messaging.send',\n label: 'Uma automação dispara e a mensagem sai no WhatsApp do cliente',\n touches: ['conversation', 'message'],\n },\n {\n key: 'conversations.inbound',\n label: 'O cliente responde, e a resposta entra na caixa de entrada',\n plane: 'sync',\n trigger: 'webhook',\n touches: ['conversation', 'message'],\n },\n {\n key: 'messaging.template_sync',\n label: 'Os modelos de mensagem vão para aprovação da Meta e o resultado volta para cá',\n plane: 'sync',\n trigger: 'scheduled',\n touches: ['message_template'],\n },\n ],\n howItSyncs: [\n 'As mensagens saem por um número de WhatsApp da plataforma, que já vem pronto — você não cadastra chave nenhuma nem precisa de um chip só seu.',\n 'Como esse número é dividido com outros negócios, o nome do seu vai escrito no começo da mensagem, e só modelos já aprovados pela Meta podem sair por ele.',\n 'Se você pedir um número dedicado, ele passa a ser o remetente assim que ficar ativo — aí a mensagem sai só com o seu nome e nada mais é dividido.',\n 'A resposta do cliente volta para Conversas em segundos, e botões como Confirmar e Cancelar já atualizam o agendamento sozinhos.',\n ],\n ...connectionStatus(\n TYXTER_CONNECTOR_ID,\n (connection) => ({ connected: !!connection?.active, plane: 'request' }),\n { store: () => store },\n ),\n ...connectionRuns(store),\n // Nothing to receive: the hub split out a credential half that does not exist\n // for this connector, so `values` is empty and consent is the whole act.\n saveConnection: async () => {\n await store.save({ active: true })\n },\n disconnect: () => store.disconnect(),\n disconnectNote:\n 'Desconectar para de enviar e de receber mensagens no WhatsApp. As conversas que já existem continuam aqui.',\n ExtraPanel: TyxterExtraPanel,\n}\n","// AUTO-GENERATED from 000_baseline.sql, 001_a_caixa_sabe_quem_falou_por_ultimo.sql, 002_a_casa_escolhe_por_onde_o_whatsapp_sai.sql, 003_a_campanha_sai_no_ritmo_que_o_numero_aguenta.sql, 004_a_tela_sabe_se_pode_falar_antes_de_deixar_escrever.sql — regenerate with scripts/embed-migrations.mjs\n// SQL files are the source of truth; this inline copy lets the manifest declare\n// migrations as data. Do not edit by hand — run the embed script instead.\n\nexport const MIGRATION_000_BASELINE = `-- ============================================================================\n-- plugins/plugin-conversations/src/migrations/000_baseline.sql — what installing this provisions, as one file.\n--\n-- GENERATED by scripts/emit-unit-baselines.mjs (#338). Do not edit by hand:\n-- change the schema, regenerate, and let scripts/check-chain-equivalence.mjs\n-- prove the result still matches.\n--\n-- This replaced 9 migration files. They are kept, unapplied, in\n-- migrations.archive/ — a year of decisions is worth reading even when it is no\n-- longer worth replaying.\n--\n-- Object order is fixed rather than dependency-sorted: schemas, types,\n-- sequences, tables, defaults, functions, constraints, views, indexes, foreign\n-- keys, triggers, row security, policies, grants. Dependencies BETWEEN units\n-- are carried by the order packages/db/chain.json declares.\n-- ============================================================================\n\n-- A LANGUAGE sql function binds its body at CREATE time, and 570 functions\n-- sorted by name are not sorted by who calls whom. This is what lets them load\n-- in any order; every body is still compiled on first call, so a genuinely\n-- broken reference surfaces then rather than never. Session-scoped.\nSET check_function_bodies = false;\n\n\n\n-- ── default privileges: reset ───────────────────────────────────────────\n-- Cleared BEFORE anything is created, so every object below gets the ACL the\n-- dump describes rather than the image's defaults on top of it. Restored at\n-- the end of this file.\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public REVOKE ALL ON SEQUENCES FROM anon, authenticated, service_role, fayz_connector$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public REVOKE ALL ON SEQUENCES FROM anon, authenticated, service_role, fayz_connector$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public REVOKE ALL ON SEQUENCES FROM anon, authenticated, service_role, fayz_connector$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public REVOKE ALL ON SEQUENCES FROM anon, authenticated, service_role, fayz_connector$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public REVOKE ALL ON FUNCTIONS FROM anon, authenticated, service_role, fayz_connector$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public REVOKE ALL ON FUNCTIONS FROM anon, authenticated, service_role, fayz_connector$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public REVOKE ALL ON FUNCTIONS FROM anon, authenticated, service_role, fayz_connector$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public REVOKE ALL ON FUNCTIONS FROM anon, authenticated, service_role, fayz_connector$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public REVOKE ALL ON TABLES FROM anon, authenticated, service_role, fayz_connector$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public REVOKE ALL ON TABLES FROM anon, authenticated, service_role, fayz_connector$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public REVOKE ALL ON TABLES FROM anon, authenticated, service_role, fayz_connector$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public REVOKE ALL ON TABLES FROM anon, authenticated, service_role, fayz_connector$stmt$;\nEND $dp$;\n\n\n-- ── table ───────────────────────────────────────────────────────────────\n\nCREATE TABLE public.plg_conversation_messages (\n id uuid DEFAULT gen_random_uuid() NOT NULL,\n tenant_id uuid NOT NULL,\n conversation_id uuid NOT NULL,\n channel text,\n direction text,\n body text NOT NULL,\n author text,\n at timestamp with time zone DEFAULT now(),\n provider_message_id text,\n delivery_status text,\n sender_kind text,\n sender_label text,\n subject_type text,\n subject_id text,\n CONSTRAINT plg_conversation_messages_channel_check CHECK (((channel IS NULL) OR (channel = ANY (ARRAY['sms'::text, 'whatsapp'::text, 'instagram'::text, 'email'::text, 'webchat'::text])))),\n CONSTRAINT plg_conversation_messages_delivery_status_check CHECK (((delivery_status IS NULL) OR (delivery_status = ANY (ARRAY['queued'::text, 'sent'::text, 'delivered'::text, 'read'::text, 'failed'::text, 'expired'::text, 'delivery_timeout'::text, 'cancelled'::text, 'opted_out'::text])))),\n CONSTRAINT plg_conversation_messages_direction_check CHECK ((direction = ANY (ARRAY['inbound'::text, 'outbound'::text]))),\n CONSTRAINT plg_conversation_messages_sender_kind_check CHECK (((sender_kind IS NULL) OR (sender_kind = ANY (ARRAY['user'::text, 'system'::text, 'ai'::text, 'automation'::text]))))\n);\n\nCREATE TABLE public.plg_conversations (\n id uuid DEFAULT gen_random_uuid() NOT NULL,\n tenant_id uuid NOT NULL,\n contact_name text NOT NULL,\n contact_handle text,\n channel text NOT NULL,\n last_message_preview text,\n last_message_at timestamp with time zone DEFAULT now(),\n unread_count integer DEFAULT 0,\n status text DEFAULT 'open'::text,\n assigned_to text,\n accent text,\n tags text[],\n location text,\n note text,\n created_at timestamp with time zone DEFAULT now() NOT NULL,\n updated_at timestamp with time zone DEFAULT now() NOT NULL,\n contact_person_id uuid,\n CONSTRAINT plg_conversations_channel_check CHECK ((channel = ANY (ARRAY['sms'::text, 'whatsapp'::text, 'instagram'::text, 'email'::text, 'webchat'::text]))),\n CONSTRAINT plg_conversations_status_check CHECK ((status = ANY (ARRAY['open'::text, 'snoozed'::text, 'closed'::text])))\n);\n\nCREATE TABLE public.plg_conversations_channels (\n id uuid DEFAULT gen_random_uuid() NOT NULL,\n tenant_id uuid,\n channel text DEFAULT 'whatsapp'::text NOT NULL,\n provider text NOT NULL,\n provider_number_id text NOT NULL,\n phone_e164 text,\n kind text DEFAULT 'dedicated'::text NOT NULL,\n status text DEFAULT 'requested'::text NOT NULL,\n display_name text,\n metadata jsonb DEFAULT '{}'::jsonb NOT NULL,\n created_at timestamp with time zone DEFAULT now() NOT NULL,\n updated_at timestamp with time zone DEFAULT now() NOT NULL,\n CONSTRAINT plg_conversations_channels_channel_check CHECK ((channel = ANY (ARRAY['sms'::text, 'whatsapp'::text, 'instagram'::text, 'email'::text, 'webchat'::text]))),\n CONSTRAINT plg_conversations_channels_kind_check CHECK ((kind = ANY (ARRAY['fallback'::text, 'dedicated'::text]))),\n CONSTRAINT plg_conversations_channels_status_check CHECK ((status = ANY (ARRAY['requested'::text, 'provisioning'::text, 'provisioned'::text, 'verifying'::text, 'active'::text, 'failed'::text, 'released'::text, 'disconnected'::text])))\n);\n\nCREATE TABLE public.plg_conversations_optouts (\n id uuid DEFAULT gen_random_uuid() NOT NULL,\n channel text DEFAULT 'whatsapp'::text NOT NULL,\n phone_e164 text NOT NULL,\n tenant_id uuid,\n provider text NOT NULL,\n reason text,\n created_at timestamp with time zone DEFAULT now() NOT NULL,\n CONSTRAINT plg_conversations_optouts_channel_check CHECK ((channel = ANY (ARRAY['sms'::text, 'whatsapp'::text, 'instagram'::text, 'email'::text, 'webchat'::text])))\n);\n\nCREATE TABLE public.plg_conversations_payment_requests (\n id uuid DEFAULT gen_random_uuid() NOT NULL,\n tenant_id uuid NOT NULL,\n conversation_id uuid,\n message_id uuid,\n subject_type text,\n subject_id text,\n amount_cents integer NOT NULL,\n currency text DEFAULT 'BRL'::text NOT NULL,\n description text,\n provider text NOT NULL,\n status text DEFAULT 'created'::text NOT NULL,\n provider_payment_id text,\n payment_link_url text,\n pix_copy_paste text,\n link_delivered_at timestamp with time zone,\n settled_notified_at timestamp with time zone,\n settled_at timestamp with time zone,\n created_at timestamp with time zone DEFAULT now() NOT NULL,\n updated_at timestamp with time zone DEFAULT now() NOT NULL,\n CONSTRAINT plg_conversations_payment_requests_amount_cents_check CHECK ((amount_cents > 0)),\n CONSTRAINT plg_conversations_payment_requests_status_check CHECK ((status = ANY (ARRAY['created'::text, 'link_generated'::text, 'approval_requested'::text, 'approved'::text, 'paid'::text, 'failed'::text, 'expired'::text, 'cancelled'::text])))\n);\n\nCREATE TABLE public.plg_conversations_webhook_events (\n id uuid DEFAULT gen_random_uuid() NOT NULL,\n provider text NOT NULL,\n event_id text NOT NULL,\n event_type text NOT NULL,\n received_at timestamp with time zone DEFAULT now() NOT NULL\n);\n\n\n-- ── function ────────────────────────────────────────────────────────────\n\nCREATE FUNCTION public.plg_conversations_match_by_phone(p_channel text, p_last10 text, p_last8 text, p_tenant_id uuid DEFAULT NULL::uuid) RETURNS TABLE(id uuid, tenant_id uuid, contact_name text, contact_handle text, contact_person_id uuid)\n LANGUAGE sql STABLE SECURITY DEFINER\n SET search_path TO 'public'\n AS $$\n WITH candidate AS (\n SELECT c.id, c.tenant_id, c.contact_name, c.contact_handle, c.contact_person_id,\n c.last_message_at,\n regexp_replace(COALESCE(c.contact_handle, ''), '[^0-9]', '', 'g') AS digits\n FROM public.plg_conversations c\n WHERE c.channel = p_channel\n AND (p_tenant_id IS NULL OR c.tenant_id = p_tenant_id)\n )\n SELECT id, tenant_id, contact_name, contact_handle, contact_person_id\n FROM candidate\n WHERE length(digits) >= 8\n AND ((p_last10 IS NOT NULL AND right(digits, 10) = p_last10)\n OR (p_last8 IS NOT NULL AND right(digits, 8) = p_last8))\n -- The 10-digit match wins over an 8-digit one even when the 8-digit thread\n -- is newer: a wrong area code is a different person, and a newer wrong\n -- answer is still wrong.\n ORDER BY (p_last10 IS NOT NULL AND right(digits, 10) = p_last10) DESC,\n last_message_at DESC NULLS LAST\n LIMIT 1;\n$$;\n\n\n-- ── constraint ──────────────────────────────────────────────────────────\n\nALTER TABLE ONLY public.plg_conversation_messages\n ADD CONSTRAINT plg_conversation_messages_pkey PRIMARY KEY (id);\n\nALTER TABLE ONLY public.plg_conversations_channels\n ADD CONSTRAINT plg_conversations_channels_pkey PRIMARY KEY (id);\n\nALTER TABLE ONLY public.plg_conversations_optouts\n ADD CONSTRAINT plg_conversations_optouts_pkey PRIMARY KEY (id);\n\nALTER TABLE ONLY public.plg_conversations_payment_requests\n ADD CONSTRAINT plg_conversations_payment_requests_pkey PRIMARY KEY (id);\n\nALTER TABLE ONLY public.plg_conversations\n ADD CONSTRAINT plg_conversations_pkey PRIMARY KEY (id);\n\nALTER TABLE ONLY public.plg_conversations_webhook_events\n ADD CONSTRAINT plg_conversations_webhook_events_pkey PRIMARY KEY (id);\n\n\n-- ── index ───────────────────────────────────────────────────────────────\n\nCREATE INDEX idx_plg_conversation_messages_by_subject ON public.plg_conversation_messages USING btree (tenant_id, subject_type, subject_id) WHERE (subject_id IS NOT NULL);\n\nCREATE INDEX idx_plg_conversation_messages_subject ON public.plg_conversation_messages USING btree (conversation_id, at DESC) WHERE (subject_id IS NOT NULL);\n\nCREATE INDEX idx_plg_conversation_messages_thread ON public.plg_conversation_messages USING btree (conversation_id, at);\n\nCREATE INDEX idx_plg_conversations_channels_tenant ON public.plg_conversations_channels USING btree (tenant_id, channel, status) WHERE (tenant_id IS NOT NULL);\n\nCREATE INDEX idx_plg_conversations_handle_suffix ON public.plg_conversations USING btree (channel, \"right\"(regexp_replace(COALESCE(contact_handle, ''::text), '[^0-9]'::text, ''::text, 'g'::text), 8), last_message_at DESC);\n\nCREATE INDEX idx_plg_conversations_optouts_lookup ON public.plg_conversations_optouts USING btree (channel, phone_e164);\n\nCREATE INDEX idx_plg_conversations_payment_requests_subject ON public.plg_conversations_payment_requests USING btree (tenant_id, subject_type, subject_id) WHERE (subject_id IS NOT NULL);\n\nCREATE INDEX idx_plg_conversations_payment_requests_tenant ON public.plg_conversations_payment_requests USING btree (tenant_id, created_at DESC);\n\nCREATE INDEX idx_plg_conversations_person ON public.plg_conversations USING btree (tenant_id, contact_person_id) WHERE (contact_person_id IS NOT NULL);\n\nCREATE INDEX idx_plg_conversations_tenant ON public.plg_conversations USING btree (tenant_id);\n\nCREATE INDEX idx_plg_conversations_tenant_recent ON public.plg_conversations USING btree (tenant_id, last_message_at DESC);\n\nCREATE INDEX idx_plg_conversations_webhook_events_received ON public.plg_conversations_webhook_events USING btree (received_at);\n\nCREATE UNIQUE INDEX uq_plg_conversation_messages_provider_message ON public.plg_conversation_messages USING btree (provider_message_id) WHERE (provider_message_id IS NOT NULL);\n\nCREATE UNIQUE INDEX uq_plg_conversations_channels_fallback ON public.plg_conversations_channels USING btree (channel, provider) WHERE ((tenant_id IS NULL) AND (kind = 'fallback'::text));\n\nCREATE UNIQUE INDEX uq_plg_conversations_channels_number ON public.plg_conversations_channels USING btree (provider, provider_number_id);\n\nCREATE UNIQUE INDEX uq_plg_conversations_optouts_contact ON public.plg_conversations_optouts USING btree (channel, phone_e164, COALESCE(tenant_id, '00000000-0000-0000-0000-000000000000'::uuid));\n\nCREATE UNIQUE INDEX uq_plg_conversations_payment_requests_open_subject ON public.plg_conversations_payment_requests USING btree (tenant_id, subject_type, subject_id) WHERE ((subject_id IS NOT NULL) AND (status = ANY (ARRAY['created'::text, 'link_generated'::text, 'approval_requested'::text, 'approved'::text])));\n\nCREATE UNIQUE INDEX uq_plg_conversations_payment_requests_provider ON public.plg_conversations_payment_requests USING btree (provider, provider_payment_id) WHERE (provider_payment_id IS NOT NULL);\n\nCREATE UNIQUE INDEX uq_plg_conversations_webhook_events_event ON public.plg_conversations_webhook_events USING btree (provider, event_id);\n\n\n-- ── fk constraint ───────────────────────────────────────────────────────\n\nALTER TABLE ONLY public.plg_conversation_messages\n ADD CONSTRAINT plg_conversation_messages_conversation_id_fkey FOREIGN KEY (conversation_id) REFERENCES public.plg_conversations(id) ON DELETE CASCADE;\n\nALTER TABLE ONLY public.plg_conversation_messages\n ADD CONSTRAINT plg_conversation_messages_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE;\n\nALTER TABLE ONLY public.plg_conversations_channels\n ADD CONSTRAINT plg_conversations_channels_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE;\n\nALTER TABLE ONLY public.plg_conversations\n ADD CONSTRAINT plg_conversations_contact_person_id_fkey FOREIGN KEY (contact_person_id) REFERENCES public.people(id) ON DELETE SET NULL;\n\nALTER TABLE ONLY public.plg_conversations_optouts\n ADD CONSTRAINT plg_conversations_optouts_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE;\n\nALTER TABLE ONLY public.plg_conversations_payment_requests\n ADD CONSTRAINT plg_conversations_payment_requests_conversation_id_fkey FOREIGN KEY (conversation_id) REFERENCES public.plg_conversations(id) ON DELETE SET NULL;\n\nALTER TABLE ONLY public.plg_conversations_payment_requests\n ADD CONSTRAINT plg_conversations_payment_requests_message_id_fkey FOREIGN KEY (message_id) REFERENCES public.plg_conversation_messages(id) ON DELETE SET NULL;\n\nALTER TABLE ONLY public.plg_conversations_payment_requests\n ADD CONSTRAINT plg_conversations_payment_requests_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE;\n\nALTER TABLE ONLY public.plg_conversations\n ADD CONSTRAINT plg_conversations_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE;\n\n\n-- ── row security ────────────────────────────────────────────────────────\n\nALTER TABLE public.plg_conversation_messages ENABLE ROW LEVEL SECURITY;\n\nALTER TABLE public.plg_conversations ENABLE ROW LEVEL SECURITY;\n\nALTER TABLE public.plg_conversations_channels ENABLE ROW LEVEL SECURITY;\n\nALTER TABLE public.plg_conversations_optouts ENABLE ROW LEVEL SECURITY;\n\nALTER TABLE public.plg_conversations_payment_requests ENABLE ROW LEVEL SECURITY;\n\nALTER TABLE public.plg_conversations_webhook_events ENABLE ROW LEVEL SECURITY;\n\n\n-- ── policy ──────────────────────────────────────────────────────────────\n\nCREATE POLICY plg_conversation_messages_delete ON public.plg_conversation_messages FOR DELETE TO authenticated USING ((tenant_id IN ( SELECT public.user_tenant_ids() AS user_tenant_ids)));\n\nCREATE POLICY plg_conversation_messages_insert ON public.plg_conversation_messages FOR INSERT TO authenticated WITH CHECK ((tenant_id IN ( SELECT public.user_tenant_ids() AS user_tenant_ids)));\n\nCREATE POLICY plg_conversation_messages_select ON public.plg_conversation_messages FOR SELECT TO authenticated USING ((tenant_id IN ( SELECT public.user_tenant_ids() AS user_tenant_ids)));\n\nCREATE POLICY plg_conversation_messages_update ON public.plg_conversation_messages FOR UPDATE TO authenticated USING ((tenant_id IN ( SELECT public.user_tenant_ids() AS user_tenant_ids)));\n\nCREATE POLICY plg_conversations_channels_select ON public.plg_conversations_channels FOR SELECT TO authenticated USING (((tenant_id IS NULL) OR (tenant_id IN ( SELECT public.user_tenant_ids() AS user_tenant_ids))));\n\nCREATE POLICY plg_conversations_delete ON public.plg_conversations FOR DELETE TO authenticated USING ((tenant_id IN ( SELECT public.user_tenant_ids() AS user_tenant_ids)));\n\nCREATE POLICY plg_conversations_insert ON public.plg_conversations FOR INSERT TO authenticated WITH CHECK ((tenant_id IN ( SELECT public.user_tenant_ids() AS user_tenant_ids)));\n\nCREATE POLICY plg_conversations_optouts_select ON public.plg_conversations_optouts FOR SELECT TO authenticated USING (((tenant_id IS NULL) OR (tenant_id IN ( SELECT public.user_tenant_ids() AS user_tenant_ids))));\n\nCREATE POLICY plg_conversations_payment_requests_select ON public.plg_conversations_payment_requests FOR SELECT TO authenticated USING ((tenant_id IN ( SELECT public.user_tenant_ids() AS user_tenant_ids)));\n\nCREATE POLICY plg_conversations_select ON public.plg_conversations FOR SELECT TO authenticated USING ((tenant_id IN ( SELECT public.user_tenant_ids() AS user_tenant_ids)));\n\nCREATE POLICY plg_conversations_update ON public.plg_conversations FOR UPDATE TO authenticated USING ((tenant_id IN ( SELECT public.user_tenant_ids() AS user_tenant_ids)));\n\n\n-- ── acl ─────────────────────────────────────────────────────────────────\n\nREVOKE ALL ON FUNCTION public.plg_conversations_match_by_phone(p_channel text, p_last10 text, p_last8 text, p_tenant_id uuid) FROM PUBLIC;\nGRANT ALL ON FUNCTION public.plg_conversations_match_by_phone(p_channel text, p_last10 text, p_last8 text, p_tenant_id uuid) TO service_role;\n\nGRANT MAINTAIN ON TABLE public.plg_conversation_messages TO anon;\nGRANT ALL ON TABLE public.plg_conversation_messages TO authenticated;\nGRANT ALL ON TABLE public.plg_conversation_messages TO service_role;\n\nGRANT MAINTAIN ON TABLE public.plg_conversations TO anon;\nGRANT ALL ON TABLE public.plg_conversations TO authenticated;\nGRANT ALL ON TABLE public.plg_conversations TO service_role;\n\nGRANT MAINTAIN ON TABLE public.plg_conversations_channels TO anon;\nGRANT ALL ON TABLE public.plg_conversations_channels TO authenticated;\nGRANT ALL ON TABLE public.plg_conversations_channels TO service_role;\n\nGRANT MAINTAIN ON TABLE public.plg_conversations_optouts TO anon;\nGRANT ALL ON TABLE public.plg_conversations_optouts TO authenticated;\nGRANT ALL ON TABLE public.plg_conversations_optouts TO service_role;\n\nGRANT MAINTAIN ON TABLE public.plg_conversations_payment_requests TO anon;\nGRANT ALL ON TABLE public.plg_conversations_payment_requests TO authenticated;\nGRANT ALL ON TABLE public.plg_conversations_payment_requests TO service_role;\n\nGRANT MAINTAIN ON TABLE public.plg_conversations_webhook_events TO anon;\nGRANT ALL ON TABLE public.plg_conversations_webhook_events TO authenticated;\nGRANT ALL ON TABLE public.plg_conversations_webhook_events TO service_role;\n\n\n-- ── default privileges: restore ─────────────────────────────────────────\n-- Put back, so the NEXT thing installed inherits what the chain had.\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO postgres$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO postgres$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO anon$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO anon$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO authenticated$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO authenticated$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO service_role$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO service_role$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO postgres$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO postgres$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO anon$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO anon$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO authenticated$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO authenticated$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO service_role$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO service_role$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO postgres$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO postgres$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO authenticated$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO authenticated$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO service_role$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO service_role$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO postgres$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO postgres$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO anon$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO anon$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO authenticated$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO authenticated$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO service_role$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO service_role$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO postgres$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO postgres$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT MAINTAIN ON TABLES TO anon$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT MAINTAIN ON TABLES TO anon$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO authenticated$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO authenticated$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO service_role$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO service_role$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO postgres$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO postgres$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO anon$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO anon$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO authenticated$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO authenticated$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO service_role$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO service_role$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$--\n-- PostgreSQL database dump complete\n--$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$--\n-- PostgreSQL database dump complete\n--$stmt$;\nEND $dp$;\n`\n\nexport const MIGRATION_001_A_CAIXA_SABE_QUEM_FALOU_POR_ULTIMO = `-- ---------------------------------------------------------------------------\n-- 001_a_caixa_sabe_quem_falou_por_ultimo.sql\n--\n-- A conversa guarda o TEXTO e a HORA da última mensagem, e não guarda de quem\n-- ela foi. Sem isso, \"quem está esperando resposta\" — que é a pergunta com que\n-- este produto abre o dia — não é respondível a partir da lista: seria preciso\n-- carregar as mensagens de cada thread para descobrir quem falou por último.\n--\n-- O painel respondia com \\`unread_count > 0\\`, que é a aproximação honesta que o\n-- shape permitia e não é a resposta certa. Ela erra exatamente no caso que mais\n-- dói: a thread que alguém ABRIU, leu, decidiu responder depois e esqueceu. O\n-- não-lida zera na leitura; o cliente continua esperando.\n--\n-- ── Por que TRIGGER e não mais uma coluna que cada escritor preenche ───────\n--\n-- \\`last_message_preview\\` e \\`last_message_at\\` são escritos hoje em CINCO lugares:\n-- o provider do navegador (criar conversa, enviar), a função \\`messaging-send\\` e\n-- o \\`tyxter-webhook\\` (duas vezes, contando o handler de pagamento). Uma sexta\n-- coluna com a mesma disciplina é uma sexta chance de alguém esquecer — e o\n-- modo de falhar é silencioso: a fila simplesmente para de listar alguém.\n--\n-- O gatilho tem UM escritor e não pode ser esquecido pelo próximo chamador. Ele\n-- é deliberadamente estreito: cuida só de \\`last_message_direction\\`. Trazer o\n-- preview e a hora para cá seria a correção certa e é uma mudança maior, com\n-- cinco chamadores para reconciliar — fica anotado, não feito aqui.\n-- ---------------------------------------------------------------------------\n\nALTER TABLE public.plg_conversations\n ADD COLUMN IF NOT EXISTS last_message_direction text;\n\nDO $$\nBEGIN\n ALTER TABLE public.plg_conversations\n ADD CONSTRAINT plg_conversations_last_message_direction_check\n CHECK (last_message_direction IS NULL\n OR last_message_direction = ANY (ARRAY['inbound'::text, 'outbound'::text]));\nEXCEPTION WHEN duplicate_object THEN\n NULL;\nEND $$;\n\nCOMMENT ON COLUMN public.plg_conversations.last_message_direction IS\n 'Quem falou por último: inbound = o cliente, outbound = a casa (001). Mantida por gatilho em plg_conversation_messages, nunca escrita à mão. NULL = thread anterior a esta migration cujo backfill não achou mensagem nenhuma.';\n\n-- ── o único escritor ───────────────────────────────────────────────────────\n--\n-- AFTER INSERT: a mensagem já existe quando a conversa é atualizada, então uma\n-- falha aqui não pode desfazer o recebimento — e recebimento perdido é pior que\n-- fila desatualizada.\n--\n-- Sem cláusula de ordem: a mensagem que acabou de entrar É a última. Comparar\n-- com \\`last_message_at\\` para decidir seria correto e traria uma corrida com os\n-- cinco escritores daquela coluna, que é justamente o problema que o gatilho\n-- existe para não ter.\nCREATE OR REPLACE FUNCTION public.plg_conversations_stamp_direction()\nRETURNS trigger\nLANGUAGE plpgsql\nSECURITY DEFINER\nSET search_path TO ''\nAS $function$\nBEGIN\n IF NEW.direction IS NULL THEN\n RETURN NULL;\n END IF;\n\n UPDATE public.plg_conversations\n SET last_message_direction = NEW.direction\n WHERE id = NEW.conversation_id;\n\n RETURN NULL;\nEND $function$;\n\nCOMMENT ON FUNCTION public.plg_conversations_stamp_direction() IS\n 'Carimba plg_conversations.last_message_direction a cada mensagem inserida (001). O único escritor da coluna.';\n\nDROP TRIGGER IF EXISTS plg_conversation_messages_stamp_direction ON public.plg_conversation_messages;\nCREATE TRIGGER plg_conversation_messages_stamp_direction\n AFTER INSERT ON public.plg_conversation_messages\n FOR EACH ROW\n EXECUTE FUNCTION public.plg_conversations_stamp_direction();\n\n-- ── backfill ───────────────────────────────────────────────────────────────\n--\n-- DISTINCT ON pega uma linha por conversa, a mais recente. \\`at DESC NULLS LAST\\`\n-- e depois \\`id DESC\\` porque \\`at\\` tem default e não NOT NULL: duas mensagens no\n-- mesmo instante (ou ambas sem hora) precisam de um desempate estável, ou o\n-- backfill devolve resultado diferente a cada execução.\nUPDATE public.plg_conversations c\n SET last_message_direction = m.direction\n FROM (\n SELECT DISTINCT ON (conversation_id) conversation_id, direction\n FROM public.plg_conversation_messages\n WHERE direction IS NOT NULL\n ORDER BY conversation_id, at DESC NULLS LAST, id DESC\n ) m\n WHERE m.conversation_id = c.id\n AND c.last_message_direction IS DISTINCT FROM m.direction;\n\n-- Só as threads que interessam à fila, e só quando o cliente falou por último:\n-- é o índice que a pergunta \"quem está esperando\" faz, e ele fica pequeno\n-- porque a maioria das conversas de um tenant está encerrada.\nCREATE INDEX IF NOT EXISTS idx_plg_conversations_waiting\n ON public.plg_conversations (tenant_id, last_message_at DESC)\n WHERE status = 'open' AND last_message_direction = 'inbound';\n`\n\nexport const MIGRATION_002_A_CASA_ESCOLHE_POR_ONDE_O_WHATSAPP_SAI = `-- ---------------------------------------------------------------------------\n-- 002_a_casa_escolhe_por_onde_o_whatsapp_sai.sql — dois provedores de WhatsApp,\n-- a credencial de cada um, e a escolha de qual está valendo.\n--\n-- ── por que a credencial precisa morar aqui ─────────────────────────────\n--\n-- O \\`messaging-send\\` (WP3) pega a chave assim:\n--\n-- const credential = await redeemConnectorCredential({ connectorSlug: 'tyxter', … })\n--\n-- Quinze linhas que chamam a PLATAFORMA Fayz. Funciona para o Tyxter porque a\n-- chave é uma só, da FayaLabs, e a plataforma a serve para qualquer inquilino\n-- do projeto. Não funciona para o Evolution, e não vai funcionar nunca: o\n-- Evolution é auto-hospedado, e cada casa tem a própria URL e a própria chave.\n-- Credencial POR INQUILINO não é atalho, é requisito do segundo provedor.\n--\n-- Um app que não está ligado a projeto Fayz nenhum — como o FullControl hoje —\n-- também não resgata nada. Aqui a chave tem onde ficar nos dois casos.\n--\n-- ── os dois provedores não são a mesma coisa, e a tabela diz isso ───────\n--\n-- \\`tyxter\\` API oficial (Cloud API, via revenda). Iniciar conversa EXIGE\n-- template aprovado pela Meta; texto livre só dentro de 24h da\n-- última mensagem do cliente. Em troca: entrega confiável, sem\n-- risco de banimento, e a Meta responde por número e qualidade.\n-- \\`evolution\\` WhatsApp Web, auto-hospedado. Não tem template nem janela de\n-- 24h — manda o que quiser. Em troca: a sessão morre, o número\n-- pode ser banido, e ninguém responde por isso além de você.\n--\n-- \\`capabilities\\` guarda essa diferença como DADO e não como \\`if\\` espalhado pela\n-- tela. Um botão de \"submeter template\" num provedor que não tem template não\n-- pode existir; a tela lê daqui para desabilitar em vez de fingir.\n--\n-- ── o segredo não passa pelo navegador ──────────────────────────────────\n--\n-- A tabela nega SELECT a \\`authenticated\\`. Quem lê é o \\`service_role\\`, do lado\n-- do servidor. A tela sabe se EXISTE credencial e quais são os campos não\n-- secretos (a URL do Evolution, o nome da instância) por uma view; o segredo\n-- ela nunca vê, nem para reexibir mascarado.\n-- ---------------------------------------------------------------------------\n\n-- ── quem está valendo, por inquilino ───────────────────────────────────────\nCREATE TABLE IF NOT EXISTS public.plg_conversations_providers (\n tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,\n provider text NOT NULL,\n -- Não secreto: a URL base do Evolution, o nome da instância, o id do número.\n -- É isto que a tela mostra e deixa editar.\n config jsonb NOT NULL DEFAULT '{}'::jsonb,\n -- Secreto. Nenhuma política de leitura alcança esta coluna.\n secrets jsonb NOT NULL DEFAULT '{}'::jsonb,\n -- O que este provedor SABE fazer. Lido pela tela para desabilitar o que não\n -- existe, em vez de oferecer um botão que erra.\n capabilities jsonb NOT NULL DEFAULT '{}'::jsonb,\n -- Um só por inquilino fica ativo. O outro continua configurado, pronto para\n -- a troca — que é o pedido: poder alternar sem reconfigurar.\n is_active boolean NOT NULL DEFAULT false,\n status text NOT NULL DEFAULT 'unconfigured',\n status_detail text,\n checked_at timestamptz,\n created_at timestamptz NOT NULL DEFAULT now(),\n updated_at timestamptz NOT NULL DEFAULT now(),\n PRIMARY KEY (tenant_id, provider),\n CONSTRAINT plg_conversations_providers_provider\n CHECK (provider IN ('tyxter', 'evolution')),\n CONSTRAINT plg_conversations_providers_status\n CHECK (status IN ('unconfigured', 'connecting', 'connected', 'error', 'disconnected'))\n);\n\n-- UM ativo por inquilino, garantido pelo banco e não pela tela. Duas telas\n-- abertas em abas diferentes são o caso comum, e \"o último clique vence\" só é\n-- verdade se alguém impedir o empate.\nCREATE UNIQUE INDEX IF NOT EXISTS plg_conversations_providers_one_active\n ON public.plg_conversations_providers (tenant_id)\n WHERE is_active;\n\nALTER TABLE public.plg_conversations_providers ENABLE ROW LEVEL SECURITY;\nALTER TABLE public.plg_conversations_providers FORCE ROW LEVEL SECURITY;\n\n-- Nenhuma política para \\`authenticated\\`: o segredo mora numa coluna desta\n-- tabela, e uma política de SELECT aqui entregaria a chave ao navegador.\n--\n-- E o REVOKE explícito, porque a ausência de política NÃO é a única linha de\n-- defesa. O schema \\`public\\` deste cluster carrega grants amplos de fábrica —\n-- medido nesta própria tabela logo após criá-la: sete privilégios já\n-- concedidos a \\`authenticated\\` sem ninguém ter pedido. A RLS forçada barra\n-- assim mesmo, mas uma política escrita errada num dia ruim é tudo o que\n-- separa a chave do navegador. Duas fechaduras.\nREVOKE ALL ON public.plg_conversations_providers FROM PUBLIC, anon, authenticated;\nGRANT SELECT, INSERT, UPDATE, DELETE ON public.plg_conversations_providers TO service_role;\n\n-- ── o que a tela pode ver ──────────────────────────────────────────────────\n--\n-- Tudo menos o segredo. \\`has_credential\\` responde a única pergunta que a tela\n-- precisa fazer sobre ele: já foi posto?\n-- ── A ÚNICA VIEW DESTE REPO QUE NÃO É \\`security_invoker\\` ──────────────────\n--\n-- A regra da casa é \\`security_invoker = true\\`: a view enxerga o que o leitor\n-- enxerga, e a RLS da tabela continua valendo. Aqui isso se autoderrota.\n--\n-- \\`authenticated\\` NÃO pode ler \\`plg_conversations_providers\\` — é onde o segredo\n-- mora, e o REVOKE logo acima é deliberado. Uma view invoker sobre uma tabela\n-- que o chamador não alcança devolve \\`42501 permission denied\\`, que foi\n-- exatamente o que a tela recebeu no primeiro teste com um JWT de verdade.\n--\n-- Então esta view é definer POR PROJETO, e paga o preço sendo ela mesma a\n-- fronteira: o \\`WHERE tenant_id IN (user_tenant_ids())\\` abaixo não é\n-- decoração, é a RLS desta view. E a coluna do segredo não está na lista do\n-- SELECT — o que ela não seleciona não existe para quem lê.\nDROP VIEW IF EXISTS public.v_conversations_providers;\nCREATE VIEW public.v_conversations_providers AS\n SELECT p.tenant_id,\n p.provider,\n p.config,\n p.capabilities,\n p.is_active,\n p.status,\n p.status_detail,\n p.checked_at,\n -- Nunca o valor. Só se existe.\n (p.secrets ? 'api_key') AS has_credential,\n p.created_at,\n p.updated_at\n FROM public.plg_conversations_providers p\n WHERE p.tenant_id IN (SELECT public.user_tenant_ids());\n\nGRANT SELECT ON public.v_conversations_providers TO authenticated;\n\nCOMMENT ON VIEW public.v_conversations_providers IS\n 'Os provedores de WhatsApp da casa, SEM o segredo. \\`has_credential\\` diz se a chave foi posta; o valor não sai daqui (002).';\n\n-- ── pôr a credencial ───────────────────────────────────────────────────────\n--\n-- SECURITY DEFINER porque a tabela nega escrita a \\`authenticated\\`: é a única\n-- porta, e ela confere o inquilino antes de abrir. \\`search_path\\` vazio porque\n-- uma função definer sem isso é uma função que o chamador pode redirecionar.\nCREATE OR REPLACE FUNCTION public.conversations_set_provider(\n p_tenant uuid,\n p_provider text,\n p_config jsonb DEFAULT '{}'::jsonb,\n p_api_key text DEFAULT NULL,\n p_capabilities jsonb DEFAULT NULL\n) RETURNS void\nLANGUAGE plpgsql SECURITY DEFINER SET search_path TO ''\nAS $$\nDECLARE v_caps jsonb;\nBEGIN\n IF p_tenant IS NULL OR NOT (p_tenant IN (SELECT public.user_tenant_ids())) THEN\n RAISE EXCEPTION 'conversations_set_provider: não é uma casa sua';\n END IF;\n\n -- O que cada provedor sabe fazer é conhecimento do produto, não do usuário.\n -- Quem chama pode sobrescrever (um Evolution atrás de proxy pode ganhar\n -- capacidades), mas o padrão descreve a verdade de cada um.\n v_caps := coalesce(p_capabilities, CASE p_provider\n WHEN 'tyxter' THEN jsonb_build_object(\n 'templates', true, -- e são OBRIGATÓRIOS para iniciar conversa\n 'requires_template', true,\n 'session_window_hours', 24, -- texto livre só dentro dela\n 'delivery_receipts', true,\n 'qr_pairing', false,\n 'ban_risk', 'low')\n WHEN 'evolution' THEN jsonb_build_object(\n 'templates', false, -- não existem: manda texto direto\n 'requires_template', false,\n 'session_window_hours', null,\n 'delivery_receipts', true,\n 'qr_pairing', true, -- parear é ler um QR, não cadastrar na Meta\n 'ban_risk', 'high') -- e é por isso que o aquecimento importa\n ELSE '{}'::jsonb END);\n\n INSERT INTO public.plg_conversations_providers AS t\n (tenant_id, provider, config, capabilities, status,\n secrets)\n VALUES\n (p_tenant, p_provider, coalesce(p_config, '{}'::jsonb), v_caps,\n CASE WHEN p_api_key IS NULL THEN 'unconfigured' ELSE 'connecting' END,\n CASE WHEN p_api_key IS NULL THEN '{}'::jsonb\n ELSE jsonb_build_object('api_key', p_api_key) END)\n ON CONFLICT (tenant_id, provider) DO UPDATE\n SET config = coalesce(p_config, t.config),\n capabilities = v_caps,\n -- Chave nula é \"não mexe\", não \"apaga\". Salvar a URL do Evolution sem\n -- redigitar a chave é o gesto comum, e apagá-la aqui seria a surpresa.\n secrets = CASE WHEN p_api_key IS NULL THEN t.secrets\n ELSE jsonb_set(t.secrets, '{api_key}', to_jsonb(p_api_key)) END,\n status = CASE WHEN p_api_key IS NULL THEN t.status ELSE 'connecting' END,\n updated_at = now();\nEND $$;\n\nREVOKE ALL ON FUNCTION public.conversations_set_provider(uuid, text, jsonb, text, jsonb) FROM PUBLIC, anon;\nGRANT EXECUTE ON FUNCTION public.conversations_set_provider(uuid, text, jsonb, text, jsonb) TO authenticated, service_role;\n\n-- ── trocar qual está valendo ───────────────────────────────────────────────\nCREATE OR REPLACE FUNCTION public.conversations_activate_provider(\n p_tenant uuid, p_provider text\n) RETURNS void\nLANGUAGE plpgsql SECURITY DEFINER SET search_path TO ''\nAS $$\nBEGIN\n IF p_tenant IS NULL OR NOT (p_tenant IN (SELECT public.user_tenant_ids())) THEN\n RAISE EXCEPTION 'conversations_activate_provider: não é uma casa sua';\n END IF;\n\n -- Desligar ANTES de ligar: o índice único parcial recusaria os dois ativos,\n -- e numa transação só a ordem é o que decide entre trocar e falhar.\n UPDATE public.plg_conversations_providers\n SET is_active = false, updated_at = now()\n WHERE tenant_id = p_tenant AND is_active;\n\n UPDATE public.plg_conversations_providers\n SET is_active = true, updated_at = now()\n WHERE tenant_id = p_tenant AND provider = p_provider;\n\n IF NOT FOUND THEN\n RAISE EXCEPTION 'conversations_activate_provider: % não está configurado nesta casa', p_provider;\n END IF;\nEND $$;\n\nREVOKE ALL ON FUNCTION public.conversations_activate_provider(uuid, text) FROM PUBLIC, anon;\nGRANT EXECUTE ON FUNCTION public.conversations_activate_provider(uuid, text) TO authenticated, service_role;\n\n-- ── o canal aprende de que provedor ele é ──────────────────────────────────\n--\n-- \\`plg_conversations_channels.provider\\` já existe e já guarda 'tyxter'. O que\n-- falta é a ligação com a credencial: um número do Evolution precisa saber de\n-- QUAL instância ele saiu, porque a mesma casa pode ter mais de uma.\nALTER TABLE public.plg_conversations_channels\n ADD COLUMN IF NOT EXISTS instance_key text;\n\nCOMMENT ON COLUMN public.plg_conversations_channels.instance_key IS\n 'A instância do provedor de onde este número sai. Nulo no Tyxter (uma conta por projeto); no Evolution é o nome da instância (002).';\n`\n\nexport const MIGRATION_003_A_CAMPANHA_SAI_NO_RITMO_QUE_O_NUMERO_AGUENTA = `-- ---------------------------------------------------------------------------\n-- 003_a_campanha_sai_no_ritmo_que_o_numero_aguenta.sql — template, campanha,\n-- log de envio e o ritmo que impede o número de ser banido.\n--\n-- O desenho não é invenção: saiu de ler Mautic, EspoCRM, Odoo e Twenty, e de\n-- ler a documentação da Meta em vez do folclore que circula sobre ela. Onde os\n-- quatro discordam, este arquivo diz qual escolheu e por quê.\n--\n-- ── três coisas que a Meta documenta e que viram REGRA, não ajuste ──────\n--\n-- · 131050 (o usuário saiu do marketing) NUNCA se repete. Reenviar é outra\n-- infração, não outra tentativa.\n-- · 131048 (bloqueado/marcado como spam) é parada dura. É o alarme de\n-- qualidade, não um erro de rede.\n-- · A janela de 24h só abre por mensagem OU chamada DO CLIENTE. Template\n-- nosso não abre janela nenhuma — e é o erro que mais se vê em CRM.\n--\n-- O resto — a rampa de aquecimento, a faixa de atraso entre mensagens — é\n-- prática de comunidade, não documentação. Vira PADRÃO EDITÁVEL: quem opera o\n-- número sabe mais do que uma tabela escrita hoje.\n--\n-- ── por que o log de envio tem \\`rotation\\` e \\`claim_token\\` ───────────────\n--\n-- \\`rotation\\` é do Mautic: a chave única é (campanha, contato, rotação), e a\n-- rotação sobe quando alguém REINICIA a campanha de propósito. Sem ela, a única\n-- forma de reenviar é apagar o histórico — e aí ninguém consegue provar o que\n-- foi mandado.\n--\n-- \\`claim_token\\` + \\`claim_expires_at\\` é do Twenty: quem vai enviar ARRENDA a\n-- linha por cinco minutos. Duas instâncias do disparador não mandam a mesma\n-- mensagem duas vezes, e um processo que morre no meio devolve a linha sozinho\n-- quando o arrendamento vence. Os quatro CRMs resolvem isso com índice único\n-- mais arrendamento; nenhum usa advisory lock, e nenhum usa outbox.\n-- ---------------------------------------------------------------------------\n\n-- ── quem nunca mais deve ser contatado ─────────────────────────────────────\n--\n-- A tabela já existe e já é chaveada pelo NÚMERO e não pelo contato — que é o\n-- acerto do Odoo e do Twenty, e o erro do Mautic: número é reatribuído, contato\n-- é duplicado, e a supressão precisa sobreviver aos dois.\n--\n-- O que falta é não apagar nunca. O EspoCRM apaga a linha ao reinscrever e\n-- perde a prova de que alguém um dia pediu para sair; o Odoo desliga um\n-- booleano. Seguimos o Odoo.\nALTER TABLE public.plg_conversations_optouts\n ADD COLUMN IF NOT EXISTS active boolean NOT NULL DEFAULT true,\n ADD COLUMN IF NOT EXISTS revoked_at timestamptz,\n ADD COLUMN IF NOT EXISTS source text;\n\nCOMMENT ON COLUMN public.plg_conversations_optouts.active IS\n 'Desligar em vez de apagar: a linha é a prova de que a pessoa pediu para sair, e apagá-la é perder a prova (003).';\n\n-- ── o texto que se manda ───────────────────────────────────────────────────\n--\n-- Um template aqui NÃO é um template da Meta. É o texto da casa, com o espelho\n-- do que o provedor respondeu sobre ele. Assim o mesmo registro serve aos dois:\n-- no Tyxter ele carrega o nome e o status da aprovação; no Evolution, que não\n-- tem o conceito, ele é só o texto — e a campanha não precisa saber a diferença.\nCREATE TABLE IF NOT EXISTS public.plg_conversations_templates (\n id uuid PRIMARY KEY DEFAULT gen_random_uuid(),\n tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,\n name text NOT NULL,\n language text NOT NULL DEFAULT 'pt_BR',\n category text NOT NULL DEFAULT 'MARKETING',\n body text NOT NULL,\n -- Os nomes das variáveis, na ordem em que {{1}}, {{2}} aparecem. A Meta só\n -- entende posição; gente entende nome. Guardar os dois é o que deixa a tela\n -- pedir \"cargo\" em vez de \"variável 3\".\n variables text[] NOT NULL DEFAULT '{}',\n provider text,\n provider_template_id text,\n -- \\`draft\\` enquanto é nosso; depois é o que o provedor respondeu, verbatim.\n status text NOT NULL DEFAULT 'draft',\n status_detail text,\n submitted_at timestamptz,\n approved_at timestamptz,\n created_at timestamptz NOT NULL DEFAULT now(),\n updated_at timestamptz NOT NULL DEFAULT now()\n);\n\nCREATE UNIQUE INDEX IF NOT EXISTS plg_conversations_templates_name_uq\n ON public.plg_conversations_templates (tenant_id, name, language);\n\nALTER TABLE public.plg_conversations_templates ENABLE ROW LEVEL SECURITY;\nALTER TABLE public.plg_conversations_templates FORCE ROW LEVEL SECURITY;\nDROP POLICY IF EXISTS plg_conversations_templates_rw ON public.plg_conversations_templates;\nCREATE POLICY plg_conversations_templates_rw ON public.plg_conversations_templates\n FOR ALL TO authenticated\n USING (tenant_id IN (SELECT public.user_tenant_ids()))\n WITH CHECK (tenant_id IN (SELECT public.user_tenant_ids()));\nGRANT SELECT, INSERT, UPDATE, DELETE ON public.plg_conversations_templates TO authenticated, service_role;\n\n-- ── a campanha ─────────────────────────────────────────────────────────────\nCREATE TABLE IF NOT EXISTS public.plg_conversations_campaigns (\n id uuid PRIMARY KEY DEFAULT gen_random_uuid(),\n tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,\n name text NOT NULL,\n template_id uuid REFERENCES public.plg_conversations_templates(id) ON DELETE SET NULL,\n channel_id uuid REFERENCES public.plg_conversations_channels(id) ON DELETE SET NULL,\n provider text,\n status text NOT NULL DEFAULT 'draft',\n -- O ritmo. Nasce do padrão do provedor e é editável: a rampa é prática de\n -- comunidade, e quem opera o número sabe mais que esta tabela.\n pacing jsonb NOT NULL DEFAULT '{}'::jsonb,\n -- Por que parou, quando parou sozinha. Ver \\`crm\\` nenhum: é o alarme de\n -- qualidade da Meta chegando na tela de quem disparou.\n paused_reason text,\n started_at timestamptz,\n finished_at timestamptz,\n created_by uuid,\n created_at timestamptz NOT NULL DEFAULT now(),\n updated_at timestamptz NOT NULL DEFAULT now(),\n CONSTRAINT plg_conversations_campaigns_status\n CHECK (status IN ('draft', 'scheduled', 'running', 'paused', 'done', 'cancelled'))\n);\n\nALTER TABLE public.plg_conversations_campaigns ENABLE ROW LEVEL SECURITY;\nALTER TABLE public.plg_conversations_campaigns FORCE ROW LEVEL SECURITY;\nDROP POLICY IF EXISTS plg_conversations_campaigns_rw ON public.plg_conversations_campaigns;\nCREATE POLICY plg_conversations_campaigns_rw ON public.plg_conversations_campaigns\n FOR ALL TO authenticated\n USING (tenant_id IN (SELECT public.user_tenant_ids()))\n WITH CHECK (tenant_id IN (SELECT public.user_tenant_ids()));\nGRANT SELECT, INSERT, UPDATE, DELETE ON public.plg_conversations_campaigns TO authenticated, service_role;\n\n-- ── uma linha por destinatário, para sempre ────────────────────────────────\n--\n-- É o log de envio e a fila ao mesmo tempo. Vale para os dois provedores, e é\n-- o único lugar onde se pode PROVAR o que foi mandado, para quem, quando, e\n-- por que alguém NÃO foi contatado.\nCREATE TABLE IF NOT EXISTS public.plg_conversations_campaign_targets (\n id uuid PRIMARY KEY DEFAULT gen_random_uuid(),\n tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,\n campaign_id uuid NOT NULL REFERENCES public.plg_conversations_campaigns(id) ON DELETE CASCADE,\n -- A pessoa do CRM. \\`SET NULL\\` e não \\`CASCADE\\`: apagar a pessoa não pode\n -- apagar a prova de que uma mensagem foi enviada a ela.\n person_id uuid,\n phone_e164 text NOT NULL,\n -- Ver o cabeçalho: sobe quando alguém REINICIA a campanha de propósito.\n rotation integer NOT NULL DEFAULT 1,\n state text NOT NULL DEFAULT 'queued',\n skip_reason text,\n failure_code text,\n failure_detail text,\n -- O arrendamento. Só quem segura o token manda.\n claim_token uuid,\n claim_expires_at timestamptz,\n provider_message_id text,\n variables jsonb NOT NULL DEFAULT '{}'::jsonb,\n queued_at timestamptz NOT NULL DEFAULT now(),\n sent_at timestamptz,\n delivered_at timestamptz,\n read_at timestamptz,\n replied_at timestamptz,\n failed_at timestamptz,\n CONSTRAINT plg_conversations_campaign_targets_state\n CHECK (state IN ('queued', 'sending', 'sent', 'delivered', 'read', 'replied', 'failed', 'skipped')),\n -- O arrendamento existe se e só se está enviando. Duas verificações porque\n -- um token sem prazo é um token que nunca volta.\n CONSTRAINT plg_conversations_campaign_targets_lease\n CHECK (state <> 'sending' OR claim_expires_at IS NOT NULL),\n CONSTRAINT plg_conversations_campaign_targets_lease_pair\n CHECK ((claim_token IS NULL) = (claim_expires_at IS NULL))\n);\n\n-- Uma vez por campanha, por pessoa, por rodada. É esta linha que impede o\n-- disparo duplo — não um \\`if\\` no código do disparador.\nCREATE UNIQUE INDEX IF NOT EXISTS plg_conversations_campaign_targets_once\n ON public.plg_conversations_campaign_targets (campaign_id, phone_e164, rotation);\n\n-- O webhook do provedor chega mais de uma vez, de propósito (o Evolution tenta\n-- 10 vezes em ~50 min). A idempotência do recebimento é este índice.\nCREATE UNIQUE INDEX IF NOT EXISTS plg_conversations_campaign_targets_provider_msg\n ON public.plg_conversations_campaign_targets (tenant_id, provider_message_id)\n WHERE provider_message_id IS NOT NULL;\n\nCREATE INDEX IF NOT EXISTS plg_conversations_campaign_targets_pending\n ON public.plg_conversations_campaign_targets (campaign_id)\n WHERE state IN ('queued', 'sending');\n\n-- O ceifeiro: linhas cujo arrendamento venceu voltam para a fila.\nCREATE INDEX IF NOT EXISTS plg_conversations_campaign_targets_reaper\n ON public.plg_conversations_campaign_targets (claim_expires_at)\n WHERE state = 'sending';\n\nALTER TABLE public.plg_conversations_campaign_targets ENABLE ROW LEVEL SECURITY;\nALTER TABLE public.plg_conversations_campaign_targets FORCE ROW LEVEL SECURITY;\nDROP POLICY IF EXISTS plg_conversations_campaign_targets_rw ON public.plg_conversations_campaign_targets;\nCREATE POLICY plg_conversations_campaign_targets_rw ON public.plg_conversations_campaign_targets\n FOR ALL TO authenticated\n USING (tenant_id IN (SELECT public.user_tenant_ids()))\n WITH CHECK (tenant_id IN (SELECT public.user_tenant_ids()));\nGRANT SELECT, INSERT, UPDATE, DELETE ON public.plg_conversations_campaign_targets TO authenticated, service_role;\n\n-- ── o ritmo que o número aguenta ───────────────────────────────────────────\n--\n-- Mora no canal e não na campanha: o limite é do NÚMERO, e duas campanhas no\n-- mesmo número somam. Guardar na campanha deixaria cada uma achar que tem a\n-- cota inteira.\nALTER TABLE public.plg_conversations_channels\n ADD COLUMN IF NOT EXISTS pacing jsonb NOT NULL DEFAULT '{}'::jsonb,\n ADD COLUMN IF NOT EXISTS warmup_day integer,\n ADD COLUMN IF NOT EXISTS warmup_started_at timestamptz,\n ADD COLUMN IF NOT EXISTS quality text,\n ADD COLUMN IF NOT EXISTS quality_at timestamptz;\n\nCOMMENT ON COLUMN public.plg_conversations_channels.pacing IS\n 'Ritmo deste número: por minuto, por hora, por dia, faixa de atraso e janela de horário. Padrão por provedor, editável — a rampa é prática de comunidade, não documentação da Meta (003).';\n\n-- Os padrões, por provedor. Números da pesquisa; o comentário diz a\n-- procedência de cada um, porque \"por que 20?\" é a pergunta que sempre volta.\nCREATE OR REPLACE FUNCTION public.conversations_default_pacing(p_provider text)\nRETURNS jsonb LANGUAGE sql IMMUTABLE AS $$\n SELECT CASE p_provider\n -- Oficial. O teto real é o tier da Meta (250/2.000/10.000/100.000), que a\n -- própria API informa por número — então aqui só entra o que a Meta NÃO\n -- controla: o ritmo instantâneo. 1 msg / 6 s para o MESMO destinatário é\n -- documentado; 60/min é 75% do teto de 80/s por segurança.\n WHEN 'tyxter' THEN jsonb_build_object(\n 'per_minute', 60, 'per_hour', 1000, 'per_day', null,\n 'delay_seconds', jsonb_build_array(1, 3),\n 'same_recipient_seconds', 6,\n 'hours', jsonb_build_object('start', '09:00', 'end', '18:00', 'weekdays_only', true))\n -- Não oficial. Aqui não há teto do provedor — há banimento. Os números são\n -- a moda de treze rampas brasileiras publicadas: 10-30/dia na primeira\n -- semana, 200-300/dia no regime. Atraso ALEATÓRIO entre 15 e 45 s porque\n -- intervalo fixo é assinatura de robô.\n WHEN 'evolution' THEN jsonb_build_object(\n 'per_minute', 12, 'per_hour', 300, 'per_day', 250,\n 'delay_seconds', jsonb_build_array(15, 45),\n 'same_recipient_seconds', 6,\n 'batch_size', 50, 'batch_cooldown_minutes', 12,\n 'hours', jsonb_build_object('start', '09:00', 'end', '18:00', 'weekdays_only', true))\n ELSE '{}'::jsonb END\n$$;\n\nCOMMENT ON FUNCTION public.conversations_default_pacing(text) IS\n 'O ritmo inicial de cada provedor. Tyxter: o teto é da Meta, aqui só o ritmo instantâneo. Evolution: não há teto, há banimento (003).';\n\n-- A rampa de aquecimento, em dias. Prática de comunidade, editável — e a\n-- função existe para que o padrão seja UM lugar e não uma constante copiada.\nCREATE OR REPLACE FUNCTION public.conversations_warmup_allowance(p_day integer)\nRETURNS integer LANGUAGE sql IMMUTABLE AS $$\n SELECT CASE\n WHEN p_day IS NULL THEN NULL -- número sem aquecimento declarado: sem teto extra\n WHEN p_day <= 3 THEN 20\n WHEN p_day <= 7 THEN 50\n WHEN p_day <= 14 THEN 100\n WHEN p_day <= 21 THEN 200\n ELSE 300\n END\n$$;\n\nCOMMENT ON FUNCTION public.conversations_warmup_allowance(integer) IS\n 'Quantos destinatários NOVOS por dia, por dia de aquecimento. Moda de treze rampas brasileiras publicadas — comunidade, não Meta. Padrão editável (003).';\n\n-- ── o que a tela do disparo precisa ver ────────────────────────────────────\nDROP VIEW IF EXISTS public.v_conversations_campaign_progress;\nCREATE VIEW public.v_conversations_campaign_progress\nWITH (security_invoker = true) AS\n SELECT c.id AS campaign_id,\n c.tenant_id,\n c.name,\n c.status,\n c.paused_reason,\n c.provider,\n count(t.id) AS total,\n count(*) FILTER (WHERE t.state = 'queued') AS queued,\n count(*) FILTER (WHERE t.state = 'sending') AS sending,\n count(*) FILTER (WHERE t.state IN ('sent','delivered','read','replied')) AS sent,\n count(*) FILTER (WHERE t.state = 'delivered') AS delivered,\n count(*) FILTER (WHERE t.state = 'read') AS read,\n count(*) FILTER (WHERE t.state = 'replied') AS replied,\n count(*) FILTER (WHERE t.state = 'failed') AS failed,\n count(*) FILTER (WHERE t.state = 'skipped') AS skipped,\n -- A taxa de resposta é o sinal que decide se o número sobrevive: a\n -- prática diz que abaixo de 30% o risco de banimento sobe. Fica na\n -- view porque é o número que precisa estar na tela, não num relatório.\n CASE WHEN count(*) FILTER (WHERE t.state IN ('sent','delivered','read','replied')) > 0\n THEN round(100.0 * count(*) FILTER (WHERE t.state = 'replied')\n / count(*) FILTER (WHERE t.state IN ('sent','delivered','read','replied')), 1)\n END AS reply_rate,\n min(t.sent_at) AS first_sent_at,\n max(t.sent_at) AS last_sent_at\n FROM public.plg_conversations_campaigns c\n LEFT JOIN public.plg_conversations_campaign_targets t ON t.campaign_id = c.id\n WHERE c.tenant_id IN (SELECT public.user_tenant_ids())\n GROUP BY c.id, c.tenant_id, c.name, c.status, c.paused_reason, c.provider;\n\nGRANT SELECT ON public.v_conversations_campaign_progress TO authenticated;\n\n-- ── arrendar o próximo lote ────────────────────────────────────────────────\n--\n-- \\`FOR UPDATE SKIP LOCKED\\` é o que deixa dois disparadores trabalharem a mesma\n-- campanha sem pisar um no outro, e sem advisory lock. O arrendamento de cinco\n-- minutos é o que devolve a linha quando um deles morre no meio.\n--\n-- A supressão é conferida AQUI, na mesma transação que arrenda. Conferir antes\n-- e mandar depois deixa uma janela em que alguém pede para sair e recebe assim\n-- mesmo — e é justamente essa mensagem que vira denúncia.\nCREATE OR REPLACE FUNCTION public.conversations_claim_targets(\n p_campaign uuid, p_limit integer DEFAULT 10\n) RETURNS TABLE (\n id uuid, phone_e164 text, person_id uuid, variables jsonb, claim_token uuid\n)\nLANGUAGE plpgsql SECURITY DEFINER SET search_path TO ''\nAS $$\nDECLARE v_token uuid := gen_random_uuid(); v_tenant uuid;\nBEGIN\n SELECT c.tenant_id INTO v_tenant\n FROM public.plg_conversations_campaigns c WHERE c.id = p_campaign;\n IF v_tenant IS NULL THEN RAISE EXCEPTION 'campanha não encontrada'; END IF;\n\n -- TRÊS COMANDOS, E NÃO UM. A primeira versão fazia o ceifeiro, a supressão e\n -- o arrendamento em CTEs do mesmo UPDATE, e isso é uma armadilha: dentro de\n -- um comando só, todas as CTEs enxergam o MESMO instantâneo da tabela, e uma\n -- linha atualizada por duas delas recebe só a primeira — em silêncio. O\n -- suprimido vinha marcado E arrendado, e qual dos dois vencia dependia do\n -- plano que o Postgres escolhesse naquele dia.\n --\n -- Separado, cada passo enxerga o resultado do anterior. É mais lento por um\n -- par de milissegundos e é a diferença entre uma regra e uma coincidência.\n\n -- 1. O ceifeiro. Quem venceu o arrendamento volta para a fila, senão uma\n -- campanha inteira fica presa num processo que morreu.\n UPDATE public.plg_conversations_campaign_targets t\n SET state = 'queued', claim_token = NULL, claim_expires_at = NULL\n WHERE t.campaign_id = p_campaign AND t.state = 'sending'\n AND t.claim_expires_at < now();\n\n -- 2. Quem pediu para sair NUNCA entra num lote. A linha fica marcada em vez\n -- de sumir: é assim que se prova que a decisão foi deliberada.\n UPDATE public.plg_conversations_campaign_targets t\n SET state = 'skipped', skip_reason = 'suppressed'\n WHERE t.campaign_id = p_campaign AND t.state = 'queued'\n AND EXISTS (SELECT 1 FROM public.plg_conversations_optouts o\n WHERE o.tenant_id = t.tenant_id\n AND o.phone_e164 = t.phone_e164\n AND o.active);\n\n -- 3. Só então arrenda. \\`SKIP LOCKED\\` deixa dois disparadores trabalharem a\n -- mesma campanha sem pisar um no outro, e sem advisory lock.\n RETURN QUERY\n WITH escolhidos AS (\n SELECT t.id FROM public.plg_conversations_campaign_targets t\n WHERE t.campaign_id = p_campaign AND t.state = 'queued'\n ORDER BY t.queued_at\n LIMIT greatest(1, least(coalesce(p_limit, 10), 100))\n FOR UPDATE SKIP LOCKED\n )\n UPDATE public.plg_conversations_campaign_targets t\n SET state = 'sending', claim_token = v_token,\n claim_expires_at = now() + interval '5 minutes'\n FROM escolhidos e\n WHERE t.id = e.id\n RETURNING t.id, t.phone_e164, t.person_id, t.variables, t.claim_token;\nEND $$;\n\nREVOKE ALL ON FUNCTION public.conversations_claim_targets(uuid, integer) FROM PUBLIC, anon;\nGRANT EXECUTE ON FUNCTION public.conversations_claim_targets(uuid, integer) TO service_role;\n`\n\nexport const MIGRATION_004_A_TELA_SABE_SE_PODE_FALAR_ANTES_DE_DEIXAR_ESCREVER = `-- ---------------------------------------------------------------------------\n-- 004_a_tela_sabe_se_pode_falar_antes_de_deixar_escrever.sql — a janela de 24h,\n-- como DADO, para a tela responder antes de alguém digitar.\n--\n-- ── o defeito que isto conserta ─────────────────────────────────────────\n--\n-- Na API oficial do WhatsApp, texto livre só sai dentro de 24h contadas da\n-- última mensagem DO CLIENTE. Fora dela a Meta recusa, e a recusa chega como um\n-- código no log — não na tela de quem escreveu. O atendente digita, aperta\n-- enviar, vê a mensagem aparecer na conversa, e ela nunca chega.\n--\n-- Isso é pior que um erro: é uma tela que mente. E some inteiro num provedor\n-- não oficial, onde não existe janela nenhuma — então o MESMO campo de texto\n-- funciona ou falha calado dependendo de qual provedor está ativo.\n--\n-- A correção não é um aviso depois. É a tela saber ANTES: janela aberta, texto\n-- livre; janela fechada, só template, e o campo diz isso em vez de aceitar.\n--\n-- ── quem abre a janela ──────────────────────────────────────────────────\n--\n-- O CLIENTE, e só ele. Mensagem nossa não abre nada — nem template aprovado.\n-- É o erro mais comum de quem implementa isso, e é por isso que o cálculo\n-- abaixo olha exclusivamente \\`direction = 'inbound'\\`.\n--\n-- (Origem: developers.facebook.com/documentation/business-messaging/whatsapp —\n-- \"The user messages you about the product. This opens a 24 hour customer\n-- service window.\" Cliques em anúncio Click-to-WhatsApp abrem 72h; não\n-- distinguimos ainda, e 24h é o limite conservador dos dois.)\n-- ---------------------------------------------------------------------------\n\nDROP VIEW IF EXISTS public.v_conversations_window;\nCREATE VIEW public.v_conversations_window\nWITH (security_invoker = true) AS\n SELECT c.id AS conversation_id,\n c.tenant_id,\n c.channel,\n c.contact_handle,\n c.contact_person_id,\n u.last_inbound_at,\n -- Quando fecha. Nulo = nunca abriu, que é o caso de todo lead que\n -- ainda não respondeu — a maioria de uma lista de feira.\n (u.last_inbound_at + interval '24 hours') AS window_expires_at,\n (u.last_inbound_at IS NOT NULL\n AND u.last_inbound_at > now() - interval '24 hours') AS window_open,\n -- Quanto falta, em minutos. A tela mostra \"faltam 3h\" e não um\n -- carimbo de data: quem está respondendo quer saber se dá tempo.\n CASE WHEN u.last_inbound_at IS NOT NULL\n THEN greatest(0, floor(extract(epoch FROM\n (u.last_inbound_at + interval '24 hours') - now()) / 60))::int\n END AS minutes_left,\n -- Quem pediu para não ser mais contatado. Some da tela antes de\n -- qualquer janela: não importa se está aberta.\n EXISTS (SELECT 1 FROM public.plg_conversations_optouts o\n WHERE o.tenant_id = c.tenant_id\n AND o.phone_e164 = regexp_replace(coalesce(c.contact_handle,''), '\\\\D', '', 'g')\n AND o.active) AS opted_out\n FROM public.plg_conversations c\n LEFT JOIN LATERAL (\n SELECT max(m.at) AS last_inbound_at\n FROM public.plg_conversation_messages m\n WHERE m.conversation_id = c.id\n AND m.direction = 'inbound'\n ) u ON true\n WHERE c.tenant_id IN (SELECT public.user_tenant_ids());\n\nGRANT SELECT ON public.v_conversations_window TO authenticated;\n\nCOMMENT ON VIEW public.v_conversations_window IS\n 'Se dá para mandar texto livre para esta conversa agora. A janela de 24h só abre por mensagem DO CLIENTE — template nosso não abre nada (004).';\n\n-- O índice que a LATERAL usa. Sem ele, cada linha da caixa varre as mensagens\n-- da conversa inteira, e a caixa é justamente a tela que abre o dia todo.\nCREATE INDEX IF NOT EXISTS plg_conversation_messages_inbound_at\n ON public.plg_conversation_messages (conversation_id, at DESC)\n WHERE direction = 'inbound';\n`\n\nexport const MIGRATIONS: Array<{ id: string; sql: string }> = [\n { id: \"000_baseline\", sql: MIGRATION_000_BASELINE },\n { id: \"001_a_caixa_sabe_quem_falou_por_ultimo\", sql: MIGRATION_001_A_CAIXA_SABE_QUEM_FALOU_POR_ULTIMO },\n { id: \"002_a_casa_escolhe_por_onde_o_whatsapp_sai\", sql: MIGRATION_002_A_CASA_ESCOLHE_POR_ONDE_O_WHATSAPP_SAI },\n { id: \"003_a_campanha_sai_no_ritmo_que_o_numero_aguenta\", sql: MIGRATION_003_A_CAMPANHA_SAI_NO_RITMO_QUE_O_NUMERO_AGUENTA },\n { id: \"004_a_tela_sabe_se_pode_falar_antes_de_deixar_escrever\", sql: MIGRATION_004_A_TELA_SABE_SE_PODE_FALAR_ANTES_DE_DEIXAR_ESCREVER },\n]\n","import React from 'react'\nimport { CampaignsView } from './views/CampaignsView'\nimport { TemplatesView } from './views/TemplatesView'\nimport type { EntityDef, PluginManifest, PluginScope, VerticalId } from '@fayz-ai/core'\nimport { getActiveTenantId, getSupabaseClientOptional, registerTranslations } from '@fayz-ai/core'\nimport type { EntityLookup } from '@fayz-ai/admin'\nimport { ConversationsPage } from './views/ConversationsPage'\nimport type { ResolvedConversationsConfig } from './context'\nimport type { ConversationsProvider } from './data/types'\nimport { createMockConversationsProvider } from './data/mock'\nimport { createSupabaseConversationsProvider } from './data/supabase'\nimport { createConversationsStore } from './store'\nimport { conversationsLocales } from './locales'\nimport { createConversationsDashboardWidgets } from './views/dashboardWidgets'\nimport { ConversationsSettingsTab } from './settings/ConversationsSettings'\nimport { buildConversationsOnboarding } from './lib/onboarding'\nimport { tyxterConnectorDef } from './integrations/tyxter/connectorDef'\nimport { MIGRATIONS } from './migrations'\n\n// ---------------------------------------------------------------------------\n// @fayz-ai/plugin-conversations — the GoHighLevel \"Conversations\" equivalent:\n// one omni-channel inbox (SMS / WhatsApp / Instagram / Email / Web chat).\n// Universal plugin — reusable by any vertical (beauty, resto, agency…).\n//\n// Supabase-backed (plg_conversations / plg_conversation_messages, tenant-scoped\n// via RLS) when a client is registered; a persisted mock inbox otherwise.\n// WhatsApp arrives through the Tyxter connector declared below; the other\n// channels still deliver inbound rows out-of-band, and this plugin stays the\n// read/compose surface for all of them.\n// ---------------------------------------------------------------------------\n\nexport interface ConversationsPluginOptions {\n navPosition?: number\n navSection?: 'main' | 'secondary' | 'settings'\n navLabel?: string\n scope?: PluginScope\n verticalId?: VerticalId\n dataProvider?: ConversationsProvider\n\n /**\n * `people.kind` for contacts created from the compose modal's picker. Each\n * vertical names its people differently (beauty 'client', agency 'lead'),\n * hence a knob rather than a hardcoded value. Default: 'contact'.\n */\n contactKind?: string\n /** Custom search source for the picker. Defaults to the person archetype lookup. */\n contactLookup?: EntityLookup\n /**\n * The app's contact entity, same definition its registry page uses. \"Create\"\n * in the picker opens THAT form. Without it the picker searches only.\n */\n contactEntityDef?: EntityDef\n}\n\nfunction createSafeProvider(): ConversationsProvider {\n // Real data when a Supabase client is registered; mock inbox otherwise.\n // Resolution is LAZY (per call): createConversationsPlugin runs at module\n // scope in the app config, BEFORE the Supabase client is registered — an\n // eager check here would capture the mock forever even on live backends.\n let real: ConversationsProvider | null = null\n let mock: ConversationsProvider | null = null\n const resolve = (): ConversationsProvider => {\n if (getSupabaseClientOptional()) {\n real ??= createSupabaseConversationsProvider({ tenantId: () => getActiveTenantId() })\n return real\n }\n mock ??= createMockConversationsProvider({ tenantId: () => getActiveTenantId() })\n return mock\n }\n return {\n listConversations: (...a) => resolve().listConversations(...a),\n getMessages: (...a) => resolve().getMessages(...a),\n sendMessage: (...a) => resolve().sendMessage(...a),\n markRead: (...a) => resolve().markRead(...a),\n setStatus: (...a) => resolve().setStatus(...a),\n createConversation: (...a) => resolve().createConversation(...a),\n }\n}\n\nexport function createConversationsPlugin(options?: ConversationsPluginOptions): PluginManifest {\n registerTranslations(conversationsLocales)\n const provider = options?.dataProvider ?? createSafeProvider()\n const store = createConversationsStore(provider)\n\n const config: ResolvedConversationsConfig = {\n contactKind: options?.contactKind ?? 'contact',\n contactLookup: options?.contactLookup,\n contactEntityDef: options?.contactEntityDef,\n }\n\n const dashboardWidgets = createConversationsDashboardWidgets({ store, config })\n\n const PageComponent: React.ComponentType<unknown> = () =>\n React.createElement(ConversationsPage, { store, config })\n PageComponent.displayName = 'ConversationsPage'\n\n // As campanhas moram numa rota irmã e não numa aba da caixa: a caixa é o\n // trabalho de quem atende e a campanha é o de quem dispara — duas pessoas,\n // dois momentos. Empilhar as duas faria a caixa carregar contadores que\n // ninguém naquele momento está olhando.\n const CampaignsPageComponent: React.ComponentType<unknown> = () =>\n React.createElement(CampaignsView)\n CampaignsPageComponent.displayName = 'ConversationsCampaignsPage'\n\n const TemplatesPageComponent: React.ComponentType<unknown> = () =>\n React.createElement(TemplatesView)\n TemplatesPageComponent.displayName = 'ConversationsTemplatesPage'\n\n return {\n id: 'conversations',\n defaultAgentRole: 'frontdesk',\n name: options?.navLabel ?? 'Conversations',\n icon: 'MessageCircle',\n version: '1.0.0',\n scope: options?.scope ?? 'universal',\n verticalId: options?.verticalId,\n defaultEnabled: true,\n dependencies: [],\n declaredFeatures: [{ id: 'conversations', label: 'Conversations', group: 'Engage' }],\n // Recurring monthly quota — counts conversation threads created this month.\n declaredLimits: [\n { key: 'conversations_month', label: 'Conversations this month', table: 'plg_conversations', period: 'month' },\n ],\n navigation: [\n {\n section: options?.navSection ?? 'main',\n position: options?.navPosition ?? 1,\n label: options?.navLabel ?? 'Conversations',\n route: '/conversations',\n icon: 'MessageCircle',\n permission: { feature: 'conversations', action: 'read' as const },\n },\n {\n section: options?.navSection ?? 'main',\n position: (options?.navPosition ?? 1) + 1,\n label: 'Campanhas',\n route: '/conversations/campaigns',\n icon: 'Send',\n permission: { feature: 'conversations', action: 'read' as const },\n },\n {\n section: options?.navSection ?? 'main',\n position: (options?.navPosition ?? 1) + 2,\n label: 'Templates',\n route: '/conversations/templates',\n icon: 'FileText',\n permission: { feature: 'conversations', action: 'read' as const },\n },\n ],\n routes: [\n {\n path: '/conversations/templates',\n component: TemplatesPageComponent,\n title: 'Templates',\n parentRoute: '/conversations',\n permission: { feature: 'conversations', action: 'read' as const },\n },\n {\n // Antes da rota-pai: a shell ordena por especificidade, mas declarar\n // a filha primeiro é o que deixa isso legível para quem ler depois.\n path: '/conversations/campaigns',\n component: CampaignsPageComponent,\n title: 'Campanhas',\n parentRoute: '/conversations',\n permission: { feature: 'conversations', action: 'read' as const },\n },\n {\n path: '/conversations',\n component: PageComponent,\n fullBleed: true,\n permission: { feature: 'conversations', action: 'read' as const },\n },\n ],\n widgets: [],\n dashboardWidgets,\n events: [\n { name: 'conversations.message.received', description: 'An inbound message arrived on any channel' },\n { name: 'conversations.message.sent', description: 'An outbound message was sent' },\n {\n // Emitted server-side by plg_emit_event, from the tyxter-webhook's\n // payment handler — the browser never sees this write, which is the\n // whole reason packages/db/migrations/027 exists.\n name: 'conversations.payment_received',\n title: 'Pagamento recebido na conversa',\n description: 'O cliente pagou uma cobrança enviada dentro da conversa',\n trigger: 'Quando o provedor confirma o pagamento de uma cobrança aberta no chat',\n icon: 'BadgeDollarSign',\n fields: [\n { name: 'amount', label: 'Valor', type: 'money', example: 'R$ 120,00' },\n { name: 'amount_cents', label: 'Valor em centavos', type: 'number', example: '12000' },\n { name: 'currency', label: 'Moeda', type: 'string', example: 'BRL' },\n { name: 'description', label: 'Descrição da cobrança', type: 'string', example: 'Corte + Escova' },\n { name: 'channel', label: 'Canal', type: 'string', example: 'whatsapp' },\n { name: 'provider', label: 'Provedor', type: 'string', example: 'tyxter' },\n // The subject is the BOOKING, not the charge: an automation reacting\n // to this needs the thing that was paid for, and the charge id is the\n // audit trail beside it.\n { name: 'subject_type', label: 'Tipo do registro', type: 'string', example: 'booking' },\n { name: 'subject_id', label: 'Registro', type: 'id', example: '9f1c…' },\n { name: 'payment_request_id', label: 'Cobrança', type: 'id', example: '3ab2…' },\n ],\n },\n ],\n aiTools: [\n {\n id: 'conversations.list-threads',\n name: 'listConversations',\n description: 'Lists open conversations across all channels, optionally filtered by channel.',\n icon: 'MessageCircle',\n mode: 'read' as const,\n category: 'Conversations',\n parameters: {\n type: 'object' as const,\n properties: {\n channel: {\n type: 'string' as const,\n enum: ['all', 'sms', 'whatsapp', 'instagram', 'email', 'webchat'],\n },\n },\n },\n suggestions: [\n { label: 'Show unread conversations' },\n { label: 'Any new WhatsApp messages?' },\n ],\n permission: { feature: 'conversations', action: 'read' as const },\n },\n {\n id: 'conversations.send-message',\n name: 'sendMessage',\n description: 'Sends a reply in a conversation thread.',\n icon: 'Send',\n mode: 'persist' as const,\n category: 'Conversations',\n parameters: {\n type: 'object' as const,\n properties: {\n conversationId: { type: 'string' as const, description: 'Conversation id' },\n body: { type: 'string' as const, description: 'Message body' },\n },\n required: ['conversationId', 'body'],\n },\n permission: { feature: 'conversations', action: 'create' as const },\n },\n ],\n // One baseline per unit since #338 — read from the generated barrel\n // rather than listed here, so regenerating the SQL never needs a\n // manifest edit to match.\n migrations: MIGRATIONS.map((entry) => ({\n id: `conversations-${entry.id}`,\n version: '1.0.0',\n sql: entry.sql,\n description:\n 'The whole schema this plugin provisions, as one file: every table, view, function, policy and grant, plus the reference rows it seeds. Replaced the per-change chain in the 1.0.0 squash (#338); the files it collapsed are kept, unapplied, in src/migrations.archive/.',\n })),\n functions: [\n {\n slug: 'tyxter-webhook',\n // PUBLIC, and the only function in this plugin that is. The caller is\n // Tyxter's infrastructure, which has no Supabase session and never\n // will — so the gateway check is off and the HMAC signature over\n // `${timestamp}.${rawBody}` IS the authentication.\n ingress: 'none',\n verifyJwt: false,\n // NAMES only. TYXTER_WEBHOOK_SECRET is the endpoint signing secret,\n // returned exactly ONCE by POST /v1/webhook-endpoints and stored as a\n // pool secret by the provisioning script — it is per-endpoint, which is\n // why it can live in the pool while the API key cannot.\n secrets: [\n 'TYXTER_WEBHOOK_SECRET',\n 'TYXTER_API_BASE_URL',\n 'FAYZ_API_URL',\n 'FAYZ_PROJECT_ID',\n 'CONNECTOR_REDEMPTION_SECRET',\n ],\n },\n {\n slug: 'tyxter-template-sync',\n // Two callers, neither of them the outside world: an operator with a\n // Supabase JWT, and an ops script with TYXTER_SYNC_SECRET. The second\n // cannot present a JWT, so the gateway check has to be off and the\n // function authenticates itself.\n ingress: 'none',\n verifyJwt: false,\n // NAMES only. The Tyxter key is NOT here and could not be: a Supabase\n // secret is per-POOL and the key belongs to the platform's project — it\n // is redeemed per run from the vault, which is what the last three are for.\n secrets: [\n 'TYXTER_API_BASE_URL',\n 'TYXTER_SYNC_SECRET',\n 'FAYZ_API_URL',\n 'FAYZ_PROJECT_ID',\n 'CONNECTOR_REDEMPTION_SECRET',\n ],\n },\n {\n slug: 'tyxter-payment-preflight',\n // verify_jwt TRUE — the only function in this wave with a human caller\n // and nothing else. An operator looking at the connector panel has a\n // Supabase session; there is no clock and no ops script here, so the\n // gateway's own check is left on and membership is checked on top of it.\n ingress: 'none',\n verifyJwt: true,\n // NAMES only. The browser cannot ask Tyxter anything — the key is the\n // platform's — which is the whole reason this function exists.\n secrets: [\n 'TYXTER_API_BASE_URL',\n 'FAYZ_API_URL',\n 'FAYZ_PROJECT_ID',\n 'CONNECTOR_REDEMPTION_SECRET',\n ],\n },\n ],\n // The settings screen exists so the connector below can be REACHED: the\n // Integrations tab is drawn by PluginSettingsPanel, so a plugin with a\n // connector and no settings route ships a connector nobody can open.\n settings: [\n {\n id: 'conversations',\n label: 'Conversas',\n icon: 'MessageCircle',\n component: ConversationsSettingsTab as React.ComponentType<unknown>,\n order: 30,\n permission: { feature: 'conversations', action: 'read' as const },\n },\n ],\n onboarding: { program: buildConversationsOnboarding() },\n // WhatsApp through Tyxter. Declared here because this plugin IS the inbox\n // the messages land in — the connector configures inside Conversas.\n connectors: [tyxterConnectorDef],\n locales: conversationsLocales,\n }\n}\n\nexport type { ConversationsProvider } from './data/types'\nexport type { Conversation, Message, Channel, MessageDirection, CreateConversationInput } from './types'\nexport { isWaitingOnUs } from './types'\nexport { createMockConversationsProvider, type MockConversationsConfig } from './data/mock'\nexport {\n createSupabaseConversationsProvider,\n type SupabaseConversationsConfig,\n} from './data/supabase'\n// The automations template editor gates its `pay` action on this, so it has to\n// leave the plugin — see PaymentReadiness for why `ready` alone is not the test.\nexport {\n readPaymentReadiness,\n openPaymentSetupSession,\n canOfferPayment,\n PAYMENT_PREFLIGHT_FUNCTION,\n type PaymentReadiness,\n type PaymentActiveMode,\n type PaymentSetupSession,\n} from './data/payment-preflight'\nexport {\n listMessagingChannels,\n type MessagingChannelAccount,\n type MessagingChannelKind,\n type MessagingChannelStatus,\n type ListMessagingChannelsOptions,\n} from './data/channels'\n// Exported so a bespoke admin can render the connector without mounting the\n// whole plugin — the same reason plugin-shop exports its definitions.\nexport {\n tyxterConnectorDef,\n TYXTER_CONNECTOR_ID,\n TYXTER_LATENCY_BUDGET_MS,\n} from './integrations/tyxter/connectorDef'\n\n// A campanha e os provedores, exportados para que outro plugin possa criar uma\n// campanha sem montar a caixa de entrada inteira. É o que deixa a lista de\n// leads do CRM oferecer \"Enviar WhatsApp\" carregando este módulo sob demanda:\n// um import estático faria a lista quebrar num app onde este plugin não está.\nexport { campaignsApi } from './data/campaigns'\nexport type { CampaignProgress, CampaignTemplate, DispatchResult } from './data/campaigns'\nexport { providersApi } from './data/providers'\nexport type { ProviderId, ProviderRow, ProviderNumber, ProviderTemplate, ProviderCapabilities } from './data/providers'\nexport { CampaignsView } from './views/CampaignsView'\nexport { TemplatesView } from './views/TemplatesView'\nexport { WhatsAppProviders } from './settings/WhatsAppProviders'\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/data/campaigns.ts","../src/data/providers.ts","../src/views/CampaignsView.tsx","../src/views/TemplatesView.tsx","../src/context.tsx","../src/types.ts","../src/lib/channel.ts","../src/views/shared.tsx","../src/views/ConversationList.tsx","../src/views/SendWindow.tsx","../src/views/MessageInspector.tsx","../src/views/MessageThread.tsx","../src/views/LeadCard.tsx","../src/views/ContactPanel.tsx","../src/views/NewConversationPanel.tsx","../src/views/InboxView.tsx","../src/views/ConversationsPage.tsx","../src/data/accents.ts","../src/data/mock.ts","../src/data/tables.ts","../src/data/supabase.ts","../src/store.ts","../src/locales/en.ts","../src/locales/pt-BR.ts","../src/locales/index.ts","../src/views/dashboardWidgets.tsx","../src/settings/WhatsAppProviders.tsx","../src/data/channels.ts","../src/settings/ConversationsSettings.tsx","../src/lib/onboarding.ts","../src/integrations/tyxter/data.ts","../src/data/payment-preflight.ts","../src/integrations/tyxter/connectorDef.tsx","../src/migrations/index.ts","../src/index.ts"],"names":["client","getSupabaseClientOptional","getActiveTenantId","React","AlertTriangle","toast","jsxs","jsx","Fragment","Button","Loader2","Send","store","MessageSquare","cn","Input","InboxIcon","Clock","useTranslation","X","CHANNELS","Check","RefreshCw","React14","SettingsGroup","invoke","tenant","ExternalLink","useState","useEffect"],"mappings":"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AAA;AAAA;AAAA;AAAA;AAAA;;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;AAAA;;;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA;AAogB/B,IAAM,gDAAA,GAAmD,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA;AAyGzD,IAAM,oDAAA,GAAuD,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA,CAAA;AAyO7D,IAAM,0DAAA,GAA6D,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA,CAAA;AA0WnE,IAAM,gEAAA,GAAmE,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;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","file":"index.js","sourcesContent":["// ---------------------------------------------------------------------------\n// Campanhas, do lado do navegador.\n//\n// A leitura é direta (RLS por inquilino); o DISPARO passa pela função de borda,\n// porque é lá que a credencial mora e é lá que o ritmo é respeitado. O navegador\n// nunca fala com o Tyxter nem com o Evolution.\n// ---------------------------------------------------------------------------\nimport { getSupabaseClientOptional, getActiveTenantId } from '@fayz-ai/core'\nimport type { ProviderId } from './providers'\n\nexport interface CampaignTemplate {\n id: string\n name: string\n language: string\n category: string\n body: string\n /** Os nomes das variáveis, na ordem de {{1}}, {{2}}. A Meta entende posição;\n * gente entende nome — e a tela pede \"cargo\", não \"variável 3\". */\n variables: string[]\n provider: string | null\n status: string\n statusDetail: string | null\n}\n\nexport interface CampaignProgress {\n campaignId: string\n name: string\n status: 'draft' | 'scheduled' | 'running' | 'paused' | 'done' | 'cancelled'\n pausedReason: string | null\n provider: string | null\n total: number\n queued: number\n sending: number\n sent: number\n delivered: number\n read: number\n replied: number\n failed: number\n skipped: number\n /** Abaixo de 30% a prática diz que o risco de banimento sobe. Fica na tela. */\n replyRate: number | null\n firstSentAt: string | null\n lastSentAt: string | null\n}\n\nexport interface DispatchResult {\n campaignId: string\n provider?: ProviderId\n sent: number\n remaining: number | null\n failures?: Array<{ to: string; code: string; message: string }>\n paused?: string\n skipped?: boolean\n reason?: string\n}\n\nfunction client() {\n const c = getSupabaseClientOptional() as any\n if (!c) throw new Error('Supabase não inicializado')\n return c\n}\n\nfunction tenant(): string {\n const t = getActiveTenantId()\n if (!t) throw new Error('Nenhuma casa ativa')\n return t\n}\n\nexport const campaignsApi = {\n async templates(): Promise<CampaignTemplate[]> {\n const { data, error } = await client()\n .from('plg_conversations_templates')\n .select('id, name, language, category, body, variables, provider, status, status_detail')\n .eq('tenant_id', tenant()).order('name')\n if (error) throw new Error(error.message)\n return ((data ?? []) as any[]).map((r) => ({\n id: r.id, name: r.name, language: r.language, category: r.category,\n body: r.body, variables: r.variables ?? [], provider: r.provider,\n status: r.status, statusDetail: r.status_detail ?? null,\n }))\n },\n\n async saveTemplate(t: { id?: string; name: string; body: string; variables?: string[]; language?: string; category?: string }) {\n const row = {\n tenant_id: tenant(), name: t.name, body: t.body,\n variables: t.variables ?? [], language: t.language ?? 'pt_BR',\n category: t.category ?? 'MARKETING', updated_at: new Date().toISOString(),\n }\n const q = t.id\n ? client().from('plg_conversations_templates').update(row).eq('id', t.id)\n : client().from('plg_conversations_templates').insert(row)\n const { error } = await q\n if (error) throw new Error(error.message)\n },\n\n async list(): Promise<CampaignProgress[]> {\n const { data, error } = await client()\n .from('v_conversations_campaign_progress')\n .select('*').eq('tenant_id', tenant())\n if (error) throw new Error(error.message)\n return ((data ?? []) as any[]).map((r) => ({\n campaignId: r.campaign_id, name: r.name, status: r.status,\n pausedReason: r.paused_reason ?? null, provider: r.provider,\n total: Number(r.total ?? 0), queued: Number(r.queued ?? 0),\n sending: Number(r.sending ?? 0), sent: Number(r.sent ?? 0),\n delivered: Number(r.delivered ?? 0), read: Number(r.read ?? 0),\n replied: Number(r.replied ?? 0), failed: Number(r.failed ?? 0),\n skipped: Number(r.skipped ?? 0),\n replyRate: r.reply_rate == null ? null : Number(r.reply_rate),\n firstSentAt: r.first_sent_at ?? null, lastSentAt: r.last_sent_at ?? null,\n }))\n },\n\n /** Cria a campanha e enfileira os destinatários.\n *\n * `ON CONFLICT DO NOTHING` via upsert: o índice único (campanha, telefone,\n * rodada) é quem impede o disparo duplo, e não uma checagem aqui. Dois\n * cliques no botão enfileiram uma vez. */\n async create(input: {\n name: string\n templateId?: string\n provider?: ProviderId\n channelId?: string\n targets: Array<{ phone: string; personId?: string; variables?: Record<string, string> | string[] }>\n }): Promise<string> {\n const tenantId = tenant()\n const { data: camp, error } = await client()\n .from('plg_conversations_campaigns')\n .insert({\n tenant_id: tenantId, name: input.name,\n template_id: input.templateId ?? null,\n provider: input.provider ?? null,\n channel_id: input.channelId ?? null,\n status: 'draft',\n })\n .select('id').single()\n if (error) throw new Error(error.message)\n\n const rows = input.targets\n .map((t) => ({\n tenant_id: tenantId, campaign_id: camp.id,\n phone_e164: String(t.phone).replace(/\\D/g, ''),\n person_id: t.personId ?? null,\n variables: t.variables ?? {},\n }))\n .filter((r) => r.phone_e164.length >= 10)\n\n // Em blocos: o PostgREST tem teto por requisição, e uma lista de 300 leads\n // numa inserção só volta truncada sem dizer que truncou.\n for (let i = 0; i < rows.length; i += 200) {\n const { error: e } = await client()\n .from('plg_conversations_campaign_targets')\n .upsert(rows.slice(i, i + 200), { onConflict: 'campaign_id,phone_e164,rotation', ignoreDuplicates: true })\n if (e) throw new Error(e.message)\n }\n return camp.id as string\n },\n\n async setStatus(campaignId: string, status: CampaignProgress['status']) {\n const patch: Record<string, unknown> = { status, updated_at: new Date().toISOString() }\n if (status === 'running') { patch.started_at = new Date().toISOString(); patch.paused_reason = null }\n const { error } = await client()\n .from('plg_conversations_campaigns').update(patch).eq('id', campaignId)\n if (error) throw new Error(error.message)\n },\n\n /** Um lote. Quem chama repete até `remaining` chegar a zero — o arrendamento\n * de cinco minutos é o que impede duas chamadas de mandarem o mesmo. */\n async dispatch(campaignId: string, limit?: number): Promise<DispatchResult> {\n const { data, error } = await client().functions.invoke('conversations-provider', {\n body: { tenantId: tenant(), action: 'dispatch', campaignId, limit },\n })\n if (error) {\n const detail = (await error.context?.json?.().catch(() => null)) as { message?: string } | null\n throw new Error(detail?.message ?? error.message)\n }\n if ((data as any)?.error) throw new Error((data as any).message ?? (data as any).error)\n return data as DispatchResult\n },\n}\n","// ---------------------------------------------------------------------------\n// Os dois provedores de WhatsApp, do lado do navegador.\n//\n// Este arquivo NUNCA vê uma chave. A leitura vem de `v_conversations_providers`,\n// que expõe tudo menos o segredo; a escrita vai por uma função `SECURITY\n// DEFINER`; e qualquer coisa que precise realmente falar com o Tyxter ou com o\n// Evolution passa pela função de borda, onde a credencial mora.\n//\n// É por isso que `hasCredential` é um booleano e não uma string mascarada:\n// `tx_live_••••` ainda é a chave inteira viajando até aqui para ser escondida\n// na renderização. O que não sai do servidor não vaza no devtools.\n// ---------------------------------------------------------------------------\nimport { getSupabaseClientOptional, getActiveTenantId } from '@fayz-ai/core'\n\nexport type ProviderId = 'tyxter' | 'evolution'\n\n/** O que cada provedor sabe fazer. Vem do banco para a tela DESABILITAR o que\n * não existe, em vez de oferecer um botão que erra. */\nexport interface ProviderCapabilities {\n templates: boolean\n /** Verdadeiro no Tyxter: iniciar conversa exige template aprovado pela Meta. */\n requires_template: boolean\n /** 24 no Tyxter, nulo no Evolution — que não tem janela nenhuma. */\n session_window_hours: number | null\n delivery_receipts: boolean\n /** Verdadeiro no Evolution: parear é ler um QR com o celular. */\n qr_pairing: boolean\n ban_risk: 'low' | 'high' | string\n}\n\nexport interface ProviderRow {\n provider: ProviderId\n config: Record<string, unknown>\n capabilities: Partial<ProviderCapabilities>\n isActive: boolean\n status: 'unconfigured' | 'connecting' | 'connected' | 'error' | 'disconnected'\n statusDetail: string | null\n checkedAt: string | null\n /** Se a chave já foi posta. O valor não chega aqui, nem mascarado. */\n hasCredential: boolean\n}\n\n/** Um número por onde a mensagem pode sair. */\nexport interface ProviderNumber {\n id: string\n phone: string | null\n displayName: string | null\n status: string\n /** Só o Tyxter tem: é a Meta quem avalia. Nulo no Evolution, e a tela mostra\n * \"—\" em vez de inventar um \"bom\" que ninguém mediu. */\n quality: string | null\n tier: string | null\n used24h: number | null\n allowance: number | null\n /** Se pode carregar um envio AGORA. */\n ready: boolean\n}\n\nexport interface ProviderTemplate {\n id: string\n name: string\n language: string\n category: string\n status: string\n body: string\n variables: number\n rejectionReason: string | null\n approvedAt: string | null\n}\n\nconst FN = 'conversations-provider'\n\nfunction client() {\n const c = getSupabaseClientOptional() as any\n if (!c) throw new Error('Supabase não inicializado')\n return c\n}\n\n/** A porta de borda. Tudo que fala com um provedor de verdade passa por aqui. */\nasync function callFn<T>(action: string, body: Record<string, unknown> = {}): Promise<T> {\n const tenantId = getActiveTenantId()\n if (!tenantId) throw new Error('Nenhuma casa ativa')\n const { data, error } = await client().functions.invoke(FN, {\n body: { tenantId, action, ...body },\n })\n if (error) {\n // O corpo do erro carrega a mensagem útil (\"o Tyxter só inicia conversa por\n // template\"); o `error.message` do SDK carrega \"Edge Function returned a\n // non-2xx status code\", que não ajuda ninguém.\n const detail = (await error.context?.json?.().catch(() => null)) as { message?: string } | null\n throw new Error(detail?.message ?? error.message)\n }\n if ((data as any)?.error) throw new Error((data as any).message ?? (data as any).error)\n return data as T\n}\n\nexport const providersApi = {\n /** O que está configurado nesta casa. Sem segredo. */\n async list(): Promise<ProviderRow[]> {\n const tenantId = getActiveTenantId()\n if (!tenantId) return []\n const { data, error } = await client()\n .from('v_conversations_providers')\n .select('provider, config, capabilities, is_active, status, status_detail, checked_at, has_credential')\n .eq('tenant_id', tenantId)\n if (error) throw new Error(error.message)\n return ((data ?? []) as any[]).map((r) => ({\n provider: r.provider,\n config: r.config ?? {},\n capabilities: r.capabilities ?? {},\n isActive: Boolean(r.is_active),\n status: r.status,\n statusDetail: r.status_detail ?? null,\n checkedAt: r.checked_at ?? null,\n hasCredential: Boolean(r.has_credential),\n }))\n },\n\n /** Guarda config e, quando vier, a chave. Chave ausente é \"não mexe\": salvar\n * a URL do Evolution sem redigitar a chave é o gesto comum. */\n async save(provider: ProviderId, config: Record<string, unknown>, apiKey?: string): Promise<void> {\n const tenantId = getActiveTenantId()\n if (!tenantId) throw new Error('Nenhuma casa ativa')\n const { error } = await client().rpc('conversations_set_provider', {\n p_tenant: tenantId,\n p_provider: provider,\n p_config: config,\n p_api_key: apiKey && apiKey.trim() ? apiKey.trim() : null,\n })\n if (error) throw new Error(error.message)\n },\n\n /** A troca. Um ativo por casa, garantido por índice único no banco. */\n async activate(provider: ProviderId): Promise<void> {\n const tenantId = getActiveTenantId()\n if (!tenantId) throw new Error('Nenhuma casa ativa')\n const { error } = await client().rpc('conversations_activate_provider', {\n p_tenant: tenantId,\n p_provider: provider,\n })\n if (error) throw new Error(error.message)\n },\n\n health: (provider?: ProviderId) =>\n callFn<{ provider: ProviderId; ok: boolean; detail: string | null; capabilities: ProviderCapabilities }>('health', { provider }),\n\n numbers: (provider?: ProviderId) =>\n callFn<{ provider: ProviderId; numbers: ProviderNumber[] }>('numbers', { provider }),\n\n /** O QR do Evolution. O Tyxter recusa com `unsupported`. */\n pair: (provider?: ProviderId) =>\n callFn<{ provider: ProviderId; qr: string | null; code: string | null }>('pair', { provider }),\n\n templates: (provider?: ProviderId) =>\n callFn<{ provider: ProviderId; templates: ProviderTemplate[]; capabilities: ProviderCapabilities }>('templates', { provider }),\n\n createTemplate: (template: { name: string; body: string; language?: string; category?: string; example?: string[] }, provider?: ProviderId) =>\n callFn<{ provider: ProviderId; result: unknown }>('create_template', { template, provider }),\n\n send: (input: {\n to: string\n text?: string\n template?: { name: string; language?: string; body?: string; variables?: string[] }\n senderId?: string\n provider?: ProviderId\n }) =>\n callFn<{ provider: ProviderId; to: string; providerMessageId: string | null; status: string; skipped?: boolean; reason?: string }>('send', input),\n}\n","import React from 'react'\nimport { Loader2, Pause, Play, Send, AlertTriangle, MessageSquare } from 'lucide-react'\nimport { Button, toast } from '@fayz-ai/ui'\nimport { campaignsApi, type CampaignProgress, type CampaignTemplate } from '../data/campaigns'\nimport { providersApi, type ProviderRow } from '../data/providers'\n\n/**\n * As campanhas, e o botão que as faz andar.\n *\n * ── por que o disparo é um laço na tela e não um botão que \"manda tudo\" ──\n *\n * Uma função de borda tem teto de tempo, e trezentos contatos no ritmo seguro\n * do Evolution — 12 por minuto, com 15 a 45 s aleatórios entre um e outro —\n * levam horas. Nenhuma requisição sobrevive a isso.\n *\n * Então cada clique manda UM LOTE e devolve quantos faltam. A tela repete\n * enquanto a aba estiver aberta, e quem fechar a aba pausa o disparo em vez de\n * perdê-lo: o arrendamento de cinco minutos devolve as linhas para a fila\n * sozinho, e o próximo clique continua de onde parou.\n *\n * É honesto sobre o que é: um disparador que depende de alguém olhando. Um\n * agendador de verdade mora no servidor e é outro trabalho — mas isto aqui\n * manda mensagem hoje, e não mente sobre o que faz quando a aba fecha.\n */\n\nconst STATE_COPY: Record<CampaignProgress['status'], { label: string; cls: string }> = {\n draft: { label: 'rascunho', cls: 'bg-muted text-muted-foreground' },\n scheduled: { label: 'agendada', cls: 'bg-info-soft text-info-soft-foreground' },\n running: { label: 'disparando', cls: 'bg-success-soft text-success-soft-foreground' },\n paused: { label: 'pausada', cls: 'bg-warning-soft text-warning-soft-foreground' },\n done: { label: 'concluída', cls: 'bg-muted text-muted-foreground' },\n cancelled: { label: 'cancelada', cls: 'bg-muted text-muted-foreground' },\n}\n\nfunction Bar({ c }: { c: CampaignProgress }) {\n const total = Math.max(1, c.total)\n const seg = (n: number, cls: string, title: string) =>\n n > 0 ? <div className={cls} style={{ width: `${(n / total) * 100}%` }} title={`${title}: ${n}`} /> : null\n return (\n <div className=\"flex h-1.5 w-full overflow-hidden rounded-full bg-muted\">\n {seg(c.replied, 'bg-success', 'responderam')}\n {seg(c.sent - c.replied, 'bg-primary', 'enviadas')}\n {seg(c.failed, 'bg-destructive', 'falharam')}\n {seg(c.skipped, 'bg-muted-foreground/40', 'puladas')}\n </div>\n )\n}\n\nexport function CampaignsView() {\n const [rows, setRows] = React.useState<CampaignProgress[] | null>(null)\n const [templates, setTemplates] = React.useState<CampaignTemplate[]>([])\n const [providers, setProviders] = React.useState<ProviderRow[]>([])\n const [running, setRunning] = React.useState<string | null>(null)\n // Um sinal de parada que o laço lê a cada volta. Sem ele, \"Pausar\" só\n // mudaria o banco e o lote seguinte sairia assim mesmo.\n const stop = React.useRef(false)\n\n const load = React.useCallback(async () => {\n try {\n const [c, t, p] = await Promise.all([\n campaignsApi.list(), campaignsApi.templates(), providersApi.list(),\n ])\n setRows(c); setTemplates(t); setProviders(p)\n } catch (e: any) {\n toast.error(e?.message ?? 'Não consegui carregar')\n setRows([])\n }\n }, [])\n React.useEffect(() => { void load() }, [load])\n\n const active = providers.find((p) => p.isActive)\n\n async function run(c: CampaignProgress) {\n stop.current = false\n setRunning(c.campaignId)\n try {\n if (c.status !== 'running') await campaignsApi.setStatus(c.campaignId, 'running')\n // O laço. Cada volta é um lote; entre lotes o servidor já esperou o\n // atraso aleatório, então aqui não há sleep — só a próxima chamada.\n for (;;) {\n if (stop.current) { await campaignsApi.setStatus(c.campaignId, 'paused'); break }\n const r = await campaignsApi.dispatch(c.campaignId)\n await load()\n if (r.paused) { toast.error(`Pausado: ${r.paused}`); break }\n if (r.skipped) { toast.error(r.reason ?? 'Campanha não está disparando'); break }\n if (r.failures?.length) {\n for (const f of r.failures.slice(0, 3)) toast.error(`${f.to}: ${f.message}`)\n }\n if (!r.remaining) { toast.success('Campanha concluída'); break }\n }\n } catch (e: any) {\n toast.error(e?.message ?? 'Falhou')\n } finally {\n setRunning(null); void load()\n }\n }\n\n return (\n <div className=\"space-y-4\">\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <div>\n <h2 className=\"text-lg font-semibold\">Campanhas</h2>\n <p className=\"text-xs text-muted-foreground\">\n {active\n ? <>saindo por <span className=\"font-medium text-foreground\">{active.provider}</span>\n {active.capabilities?.requires_template && ' · exige template aprovado'}</>\n : 'nenhum provedor de WhatsApp ativo — configure em Ajustes'}\n </p>\n </div>\n </div>\n\n {rows === null && (\n <div className=\"flex items-center gap-2 py-8 text-sm text-muted-foreground\">\n <Loader2 className=\"h-4 w-4 animate-spin\" /> carregando…\n </div>\n )}\n\n {rows?.length === 0 && (\n <div className=\"rounded-card border-2 border-dashed py-10 text-center\">\n <MessageSquare className=\"mx-auto h-8 w-8 text-muted-foreground/30\" />\n <p className=\"mt-2 text-sm text-muted-foreground\">Nenhuma campanha ainda.</p>\n <p className=\"text-xs text-muted-foreground\">\n Crie uma a partir da lista de leads: filtre, selecione e escolha \"Enviar WhatsApp\".\n </p>\n </div>\n )}\n\n {rows?.map((c) => {\n const st = STATE_COPY[c.status] ?? STATE_COPY.draft\n const busy = running === c.campaignId\n return (\n <div key={c.campaignId} className=\"rounded-card border bg-card p-4\" data-testid={`campaign-${c.campaignId}`}>\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"text-sm font-semibold\">{c.name}</span>\n <span className={`rounded-full px-2 py-0.5 text-[10px] font-medium ${st.cls}`}>{st.label}</span>\n {c.provider && <span className=\"text-[10px] text-muted-foreground\">{c.provider}</span>}\n {/* Por que parou sozinha. É o alarme de qualidade da Meta\n chegando em quem disparou, e não num log que ninguém abre. */}\n {c.pausedReason && (\n <span className=\"inline-flex items-center gap-1 text-[10px] text-warning\">\n <AlertTriangle className=\"h-3 w-3\" /> {c.pausedReason}\n </span>\n )}\n </div>\n\n <div className=\"mt-2\"><Bar c={c} /></div>\n\n <div className=\"mt-2 flex flex-wrap gap-x-4 gap-y-1 text-xs text-muted-foreground\">\n <span>{c.total} destinatários</span>\n <span>enviadas: <span className=\"text-foreground tabular-nums\">{c.sent}</span></span>\n <span>responderam: <span className=\"text-foreground tabular-nums\">{c.replied}</span></span>\n {c.failed > 0 && <span className=\"text-destructive\">falharam: {c.failed}</span>}\n {c.skipped > 0 && <span>puladas: {c.skipped}</span>}\n {c.queued > 0 && <span>na fila: <span className=\"text-foreground tabular-nums\">{c.queued}</span></span>}\n {/* Abaixo de 30% a prática diz que o risco de banimento sobe.\n Por isso a taxa fica ao lado dos números e não num relatório. */}\n {c.replyRate != null && (\n <span className={c.replyRate < 30 ? 'text-warning' : 'text-success'}>\n resposta: {c.replyRate}%\n </span>\n )}\n </div>\n\n <div className=\"mt-3 flex gap-2\">\n {busy ? (\n <Button size=\"sm\" variant=\"outline\" onClick={() => { stop.current = true }}>\n <Pause className=\"mr-1 h-3 w-3\" /> Pausar\n </Button>\n ) : (\n <Button\n size=\"sm\"\n disabled={!active || c.queued === 0 || c.status === 'done' || c.status === 'cancelled'}\n onClick={() => void run(c)}\n >\n {c.status === 'paused' ? <><Play className=\"mr-1 h-3 w-3\" /> Continuar</> : <><Send className=\"mr-1 h-3 w-3\" /> Disparar</>}\n </Button>\n )}\n {busy && <span className=\"flex items-center gap-1 text-xs text-muted-foreground\">\n <Loader2 className=\"h-3 w-3 animate-spin\" /> disparando — mantenha esta aba aberta\n </span>}\n </div>\n </div>\n )\n })}\n\n {templates.length > 0 && (\n <div className=\"rounded-card border bg-card p-4\">\n <p className=\"text-xs font-medium text-muted-foreground mb-2\">Templates ({templates.length})</p>\n {templates.map((t) => (\n <div key={t.id} className=\"flex flex-wrap items-baseline gap-2 py-1 text-xs\">\n <span className=\"font-medium\">{t.name}</span>\n <span className={t.status === 'approved' ? 'text-success' : 'text-muted-foreground'}>{t.status}</span>\n <span className=\"truncate text-muted-foreground\">{t.body.slice(0, 70)}</span>\n </div>\n ))}\n </div>\n )}\n </div>\n )\n}\n","import React from 'react'\nimport { Loader2, Plus, RefreshCw, Send, AlertTriangle, CheckCircle2, Clock } from 'lucide-react'\nimport { Button, Input, toast } from '@fayz-ai/ui'\nimport { providersApi, type ProviderTemplate, type ProviderRow } from '../data/providers'\nimport { campaignsApi, type CampaignTemplate } from '../data/campaigns'\n\n/**\n * Os templates: escrever, submeter à Meta, e saber o que ela respondeu.\n *\n * ── por que esta tela precisa existir ───────────────────────────────────\n *\n * Na API oficial, um template aprovado é a ÚNICA porta para iniciar conversa\n * com quem nunca respondeu — que é o caso de toda lista de feira. Sem esta\n * tela, a única forma de criar um era um comando no terminal, e o estado da\n * aprovação só existia na conta do provedor.\n *\n * ── as duas listas, e por que não são uma ───────────────────────────────\n *\n * `plg_conversations_templates` é o texto DA CASA: existe mesmo sem provedor,\n * serve ao Evolution (que não tem o conceito e manda o texto direto), e\n * sobrevive a uma troca de provedor.\n *\n * A lista do provedor é o que a Meta aprovou. Um texto nosso sem par lá é um\n * texto que não pode iniciar conversa; um par lá sem texto aqui é um template\n * que alguém criou por fora. A tela mostra os dois lados e a diferença, em vez\n * de fingir que são a mesma coisa.\n *\n * ── o limite de caracteres não é enfeite ────────────────────────────────\n *\n * A Meta recusa corpo acima de 1024 caracteres, e a recusa chega um dia depois\n * — porque a fila de aprovação é dela. Contar aqui custa nada e economiza 24h.\n */\n\nconst STATUS_UI: Record<string, { icon: typeof Clock; cls: string; label: string }> = {\n approved: { icon: CheckCircle2, cls: 'text-success', label: 'aprovado' },\n submitted: { icon: Clock, cls: 'text-warning', label: 'em análise' },\n pending: { icon: Clock, cls: 'text-warning', label: 'em análise' },\n rejected: { icon: AlertTriangle, cls: 'text-destructive', label: 'recusado' },\n paused: { icon: AlertTriangle, cls: 'text-warning', label: 'pausado' },\n disabled: { icon: AlertTriangle, cls: 'text-destructive', label: 'desativado' },\n draft: { icon: Clock, cls: 'text-muted-foreground', label: 'rascunho' },\n}\n\n/** `{{1}}`, `{{2}}`… na ordem em que aparecem. A Meta só entende posição. */\nfunction slots(body: string): number {\n const found = new Set((body.match(/\\{\\{(\\d+)\\}\\}/g) ?? []))\n return found.size\n}\n\nexport function TemplatesView() {\n const [mine, setMine] = React.useState<CampaignTemplate[] | null>(null)\n const [theirs, setTheirs] = React.useState<ProviderTemplate[]>([])\n const [providers, setProviders] = React.useState<ProviderRow[]>([])\n const [busy, setBusy] = React.useState<string | null>(null)\n const [draft, setDraft] = React.useState({ name: '', body: '', variables: '' })\n\n const active = providers.find((p) => p.isActive)\n const supportsTemplates = active?.capabilities?.templates !== false\n\n const load = React.useCallback(async () => {\n try {\n const [m, p] = await Promise.all([campaignsApi.templates(), providersApi.list()])\n setMine(m); setProviders(p)\n } catch (e: any) { toast.error(e?.message ?? 'Não consegui carregar'); setMine([]) }\n }, [])\n React.useEffect(() => { void load() }, [load])\n\n const sync = async () => {\n setBusy('sync')\n try {\n const d = await providersApi.templates()\n setTheirs(d.templates)\n toast.success(`${d.templates.length} template(s) no provedor`)\n } catch (e: any) { toast.error(e?.message ?? 'Falhou') } finally { setBusy(null) }\n }\n\n const create = async () => {\n const name = draft.name.trim().toLowerCase().replace(/[^a-z0-9_]/g, '_')\n if (!name || !draft.body.trim()) { toast.error('Nome e corpo são obrigatórios'); return }\n // A Meta recusa acima de 1024 e responde no dia seguinte. Contar aqui\n // custa nada e economiza 24 horas de espera por um erro de digitação.\n if (draft.body.length > 1024) { toast.error(`Corpo com ${draft.body.length} caracteres — a Meta aceita até 1024`); return }\n setBusy('create')\n try {\n const vars = draft.variables.split(',').map((v) => v.trim()).filter(Boolean)\n await campaignsApi.saveTemplate({ name, body: draft.body, variables: vars })\n if (supportsTemplates) {\n // O exemplo é OBRIGATÓRIO quando há variáveis: sem ele a Meta recusa\n // sem dizer por quê, e a recusa volta um dia depois.\n await providersApi.createTemplate({\n name, body: draft.body,\n example: vars.length ? vars.map((v) => `exemplo ${v}`) : undefined,\n })\n toast.success('Submetido — a Meta costuma responder em ~24h')\n } else {\n toast.success('Guardado. Este provedor não usa templates: o texto sai direto.')\n }\n setDraft({ name: '', body: '', variables: '' })\n await load(); await sync()\n } catch (e: any) { toast.error(e?.message ?? 'Falhou') } finally { setBusy(null) }\n }\n\n const byName = new Map(theirs.map((t) => [t.name, t]))\n\n return (\n <div className=\"space-y-4\">\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <div>\n <h2 className=\"text-lg font-semibold\">Templates</h2>\n <p className=\"text-xs text-muted-foreground\">\n {supportsTemplates\n ? <>No <span className=\"font-medium text-foreground\">{active?.provider ?? 'provedor'}</span>, um template aprovado é a única porta para falar com quem nunca respondeu.</>\n : <>O <span className=\"font-medium text-foreground\">{active?.provider}</span> não usa templates — o texto sai direto. O que você escrever aqui vira o corpo da campanha.</>}\n </p>\n </div>\n <Button size=\"sm\" variant=\"outline\" disabled={busy === 'sync' || !active} onClick={() => void sync()}>\n {busy === 'sync' ? <Loader2 className=\"h-3 w-3 animate-spin\" /> : <><RefreshCw className=\"mr-1 h-3 w-3\" /> Buscar no provedor</>}\n </Button>\n </div>\n\n {/* ── escrever ── */}\n <div className=\"rounded-card border bg-card p-4\">\n <p className=\"mb-2 flex items-center gap-1.5 text-sm font-medium\"><Plus className=\"h-3.5 w-3.5\" /> Novo template</p>\n <div className=\"grid gap-2 sm:grid-cols-[1fr_1fr]\">\n <label className=\"block\">\n <span className=\"text-[11px] text-muted-foreground\">Nome (sem espaços)</span>\n <Input className=\"mt-0.5 h-8 text-sm\" placeholder=\"primeiro_contato_feira\"\n value={draft.name} onChange={(e) => setDraft({ ...draft, name: e.target.value })} />\n </label>\n <label className=\"block\">\n <span className=\"text-[11px] text-muted-foreground\">Variáveis, em ordem, separadas por vírgula</span>\n <Input className=\"mt-0.5 h-8 text-sm\" placeholder=\"nome, vendedor, dor\"\n value={draft.variables} onChange={(e) => setDraft({ ...draft, variables: e.target.value })} />\n </label>\n </div>\n <label className=\"mt-2 block\">\n <span className=\"text-[11px] text-muted-foreground\">\n Corpo — use {'{{1}}'}, {'{{2}}'} na ordem das variáveis\n </span>\n <textarea\n rows={5}\n className=\"mt-0.5 w-full resize-y rounded-md border border-border bg-background px-3 py-2 text-sm outline-none focus:border-primary\"\n placeholder={'Oi {{1}}! Aqui é {{2}}, do ChefControl 👋\\n\\nA gente se falou no Salão Abrasel...'}\n value={draft.body}\n onChange={(e) => setDraft({ ...draft, body: e.target.value })}\n />\n </label>\n <div className=\"mt-1 flex flex-wrap items-center gap-3 text-[11px]\">\n <span className={draft.body.length > 1024 ? 'font-medium text-destructive' : 'text-muted-foreground'}>\n {draft.body.length}/1024 caracteres\n </span>\n <span className=\"text-muted-foreground\">{slots(draft.body)} variável(is) no corpo</span>\n {/* O descasamento é o erro mais comum e o mais caro: a Meta recusa e\n você só descobre no dia seguinte. */}\n {slots(draft.body) !== draft.variables.split(',').filter((v) => v.trim()).length && draft.body && (\n <span className=\"text-warning\">\n ⚠ o corpo tem {slots(draft.body)} e você nomeou {draft.variables.split(',').filter((v) => v.trim()).length}\n </span>\n )}\n </div>\n <Button size=\"sm\" className=\"mt-2\" disabled={busy === 'create'} onClick={() => void create()}>\n {busy === 'create' ? <Loader2 className=\"h-3 w-3 animate-spin\" /> : <><Send className=\"mr-1 h-3 w-3\" /> {supportsTemplates ? 'Guardar e submeter' : 'Guardar'}</>}\n </Button>\n </div>\n\n {/* ── nossos ── */}\n <div className=\"rounded-card border bg-card\">\n <p className=\"border-b px-4 py-2 text-xs font-medium text-muted-foreground\">\n Desta casa {mine ? `(${mine.length})` : ''}\n </p>\n {mine === null && <p className=\"px-4 py-3 text-xs text-muted-foreground\">carregando…</p>}\n {mine?.length === 0 && <p className=\"px-4 py-3 text-xs text-muted-foreground\">Nenhum template ainda.</p>}\n {mine?.map((t) => {\n const remote = byName.get(t.name)\n const st = STATUS_UI[remote?.status ?? t.status] ?? STATUS_UI.draft\n const Icon = st.icon\n return (\n <div key={t.id} className=\"border-b px-4 py-2.5 last:border-b-0\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"text-sm font-medium\">{t.name}</span>\n <span className={`inline-flex items-center gap-1 text-[10px] ${st.cls}`}>\n <Icon className=\"h-3 w-3\" /> {st.label}\n </span>\n {t.variables.length > 0 && (\n <span className=\"text-[10px] text-muted-foreground\">{t.variables.join(' · ')}</span>\n )}\n {/* Existe aqui e não lá: não pode iniciar conversa. É a\n diferença que decide se dá para usar numa campanha. */}\n {supportsTemplates && !remote && (\n <span className=\"text-[10px] text-warning\">não está no provedor</span>\n )}\n </div>\n <p className=\"mt-0.5 whitespace-pre-wrap text-xs text-muted-foreground\">{t.body}</p>\n {remote?.rejectionReason && (\n <p className=\"mt-1 text-[11px] text-destructive\">Recusado: {remote.rejectionReason}</p>\n )}\n </div>\n )\n })}\n </div>\n\n {/* ── os do provedor que não temos ── */}\n {theirs.filter((t) => !(mine ?? []).some((m) => m.name === t.name)).length > 0 && (\n <div className=\"rounded-card border bg-card\">\n <p className=\"border-b px-4 py-2 text-xs font-medium text-muted-foreground\">\n No provedor, sem par nesta casa — criados por fora\n </p>\n {theirs.filter((t) => !(mine ?? []).some((m) => m.name === t.name)).map((t) => {\n const st = STATUS_UI[t.status] ?? STATUS_UI.draft\n const Icon = st.icon\n return (\n <div key={t.id} className=\"flex flex-wrap items-baseline gap-2 border-b px-4 py-2 text-xs last:border-b-0\">\n <span className=\"font-medium\">{t.name}</span>\n <span className={`inline-flex items-center gap-1 text-[10px] ${st.cls}`}><Icon className=\"h-3 w-3\" /> {st.label}</span>\n <span className=\"truncate text-muted-foreground\">{t.body.slice(0, 80)}</span>\n </div>\n )\n })}\n </div>\n )}\n </div>\n )\n}\n","import React from 'react'\nimport { useStore, type StoreApi } from 'zustand'\nimport type { EntityLookup } from '@fayz-ai/admin'\nimport type { EntityDef } from '@fayz-ai/core'\nimport type { ConversationsUIState } from './store'\n\n/**\n * App-tunable knobs the inbox UI reads. Kept tiny on purpose: everything here\n * describes the APP's data shape (which people are \"contacts\" in this vertical),\n * never product policy.\n */\nexport interface ResolvedConversationsConfig {\n /** `people.kind` the picker searches. */\n contactKind: string\n /** Search source for the contact picker. Defaults to the person archetype. */\n contactLookup?: EntityLookup\n /** The app's own contact entity — \"Create\" opens ITS form. Agenda's twin is `clientEntityDef`. */\n contactEntityDef?: EntityDef\n}\n\nexport const DEFAULT_CONVERSATIONS_CONFIG: ResolvedConversationsConfig = {\n contactKind: 'contact',\n}\n\nconst StoreContext = React.createContext<StoreApi<ConversationsUIState> | null>(null)\nconst ConfigContext = React.createContext<ResolvedConversationsConfig>(DEFAULT_CONVERSATIONS_CONFIG)\n\nexport function ConversationsContextProvider({\n store,\n config = DEFAULT_CONVERSATIONS_CONFIG,\n children,\n}: {\n store: StoreApi<ConversationsUIState>\n config?: ResolvedConversationsConfig\n children?: React.ReactNode\n}) {\n return (\n <StoreContext.Provider value={store}>\n <ConfigContext.Provider value={config}>{children}</ConfigContext.Provider>\n </StoreContext.Provider>\n )\n}\n\nexport function useConversationsStore<T>(selector: (state: ConversationsUIState) => T): T {\n const store = React.useContext(StoreContext)\n if (!store) throw new Error('useConversationsStore must be used within ConversationsPage')\n return useStore(store, selector)\n}\n\nexport function useConversationsConfig(): ResolvedConversationsConfig {\n return React.useContext(ConfigContext)\n}\n","// ---------------------------------------------------------------------------\n// @fayz-ai/plugin-conversations — domain types for the unified omni-channel\n// inbox (the GoHighLevel \"Conversations\" equivalent). Provider-agnostic.\n// ---------------------------------------------------------------------------\n\nexport type Channel = 'sms' | 'whatsapp' | 'instagram' | 'email' | 'webchat'\n\nexport type ConversationStatus = 'open' | 'snoozed' | 'closed'\n\nexport type MessageDirection = 'inbound' | 'outbound'\n\nexport interface Conversation {\n id: string\n contactName: string\n /**\n * `public.people` id when the thread is tied to a real contact record (the\n * compose modal resolves one via the shared ContactPicker). Absent for legacy\n * threads and for inbound messages from an unknown handle.\n */\n contactPersonId?: string\n /** Phone, @handle, or email depending on channel */\n contactHandle: string\n channel: Channel\n lastMessagePreview: string\n lastMessageAt: string\n /**\n * Who spoke last: `inbound` = the customer, `outbound` = the house.\n *\n * The field the queue is built on — \"waiting on us\" is `status === 'open'`\n * plus this being `inbound`. `unreadCount` is the older approximation and\n * gets the painful case wrong: a thread somebody opened, read, meant to\n * answer later and forgot has zero unread and a customer still waiting.\n *\n * Optional because a thread predating the column's backfill (or a provider\n * that does not report direction) genuinely does not know — see\n * `isWaitingOnUs`, which falls back rather than guessing.\n */\n lastMessageDirection?: MessageDirection\n unreadCount: number\n status: ConversationStatus\n assignedTo?: string\n /** Tailwind-ish accent for the avatar bubble */\n accent: string\n tags: string[]\n // Optional context shown in the contact panel.\n location?: string\n note?: string\n}\n\nexport interface Message {\n id: string\n conversationId: string\n channel: Channel\n direction: MessageDirection\n body: string\n at: string\n author: string\n /** O que aconteceu com ela depois que saiu. Nulo na entrada — ela chegou,\n * não foi entregue por nós. `failed` é o caso que a tela precisa gritar:\n * uma mensagem que nunca saiu não pode parecer uma mensagem enviada. */\n deliveryStatus?: string | null\n /** O id no provedor. Sem ele, não saiu — e é a chave para buscar o JSON cru. */\n providerMessageId?: string | null\n}\n\nexport interface ListConversationsQuery {\n channel?: Channel | 'all'\n status?: ConversationStatus | 'all'\n search?: string\n}\n\nexport interface SendMessageInput {\n conversationId: string\n body: string\n}\n\nexport interface CreateConversationInput {\n contactName: string\n /** `public.people` id, when the contact was resolved/created by the picker. */\n contactPersonId?: string\n /** Phone, @handle, or email depending on channel. */\n contactHandle?: string\n channel: Channel\n /** Optional first outbound message; stamps preview + last_message_at. */\n firstMessage?: string\n /** Optional free-text note surfaced in the contact panel. */\n note?: string\n}\n\nexport const CHANNEL_LABELS: Record<Channel, string> = {\n sms: 'SMS',\n whatsapp: 'WhatsApp',\n instagram: 'Instagram',\n email: 'Email',\n webchat: 'Web Chat',\n}\n\n/**\n * Is this thread waiting on US?\n *\n * One definition, in one place, because the KPI, the queue table and anything\n * that later filters the inbox have to agree — a dashboard that says three and\n * a list that shows one is worse than neither.\n *\n * `lastMessageDirection` is the real answer. `unreadCount` is the fallback for\n * a thread that genuinely does not know its direction (older than migration\n * 001, or a provider that does not report one) — it under-reports rather than\n * over-reports, which is the right way round: a queue that invents work loses\n * trust faster than one that misses an old row.\n */\nexport function isWaitingOnUs(conversation: Conversation): boolean {\n if (conversation.status !== 'open') return false\n if (conversation.lastMessageDirection) return conversation.lastMessageDirection === 'inbound'\n return conversation.unreadCount > 0\n}\n","import React from 'react'\nimport { MessageSquare, Phone, Instagram, Mail, Globe } from 'lucide-react'\nimport { CHANNEL_LABELS, type Channel } from '../types'\n\n// ---------------------------------------------------------------------------\n// Shared channel visuals — one source of truth for the icon + accent color of\n// each channel, reused by the conversation list, thread header and contact\n// panel. Keeps the omni-channel inbox visually consistent.\n// ---------------------------------------------------------------------------\n\nexport const CHANNEL_ICON: Record<Channel, React.ComponentType<{ className?: string }>> = {\n sms: Phone,\n whatsapp: MessageSquare,\n instagram: Instagram,\n email: Mail,\n webchat: Globe,\n}\n\nexport interface ChannelAccent {\n /** Solid brand color (hex) — bubbles, dots, header chips. */\n color: string\n /** Soft tinted background classes for badges. */\n badge: string\n}\n\nexport const CHANNEL_ACCENT: Record<Channel, ChannelAccent> = {\n whatsapp: { color: '#22c55e', badge: 'bg-[#22c55e]/12 text-[#15803d] dark:text-[#4ade80]' },\n sms: { color: '#6366f1', badge: 'bg-[#6366f1]/12 text-[#4338ca] dark:text-[#a5b4fc]' },\n instagram: { color: '#ec4899', badge: 'bg-[#ec4899]/12 text-[#be185d] dark:text-[#f9a8d4]' },\n email: { color: '#0ea5e9', badge: 'bg-[#0ea5e9]/12 text-[#0369a1] dark:text-[#7dd3fc]' },\n webchat: { color: '#f59e0b', badge: 'bg-[#f59e0b]/12 text-[#b45309] dark:text-[#fcd34d]' },\n}\n\nexport { CHANNEL_LABELS }\n","import React from 'react'\nimport { cn } from '@fayz-ai/ui'\nimport { CHANNEL_ACCENT, CHANNEL_ICON, CHANNEL_LABELS } from '../lib/channel'\nimport type { Channel } from '../types'\n\n// ---------------------------------------------------------------------------\n// Small presentational helpers shared across the inbox panes.\n// ---------------------------------------------------------------------------\n\n/** Reactive media-query match — drives the responsive list/thread/panel layout. */\nexport function useMediaQuery(query: string): boolean {\n const [matches, setMatches] = React.useState(\n () => typeof window !== 'undefined' && window.matchMedia(query).matches,\n )\n React.useEffect(() => {\n if (typeof window === 'undefined') return\n const mql = window.matchMedia(query)\n const onChange = () => setMatches(mql.matches)\n onChange()\n mql.addEventListener('change', onChange)\n return () => mql.removeEventListener('change', onChange)\n }, [query])\n return matches\n}\n\nexport function initialsOf(name: string): string {\n return name.replace('@', '').split(/\\s+/).filter(Boolean).map((w) => w[0]).slice(0, 2).join('').toUpperCase()\n}\n\nexport function Avatar({ name, accent, size = 'md', channel }: {\n name: string\n accent: string\n size?: 'sm' | 'md' | 'lg'\n /** When set, renders a small channel glyph badge over the avatar. */\n channel?: Channel\n}) {\n const dims = size === 'lg' ? 'h-14 w-14 text-lg' : size === 'sm' ? 'h-9 w-9 text-xs' : 'h-10 w-10 text-sm'\n const Icon = channel ? CHANNEL_ICON[channel] : null\n return (\n <div className=\"relative shrink-0\">\n <div\n className={cn('flex items-center justify-center rounded-full font-semibold text-white', dims)}\n style={{ backgroundColor: accent }}\n >\n {initialsOf(name)}\n </div>\n {Icon && (\n <span\n className=\"absolute -bottom-0.5 -right-0.5 flex h-4 w-4 items-center justify-center rounded-full ring-2 ring-card\"\n style={{ backgroundColor: CHANNEL_ACCENT[channel!].color }}\n >\n <Icon className=\"h-2.5 w-2.5 text-white\" />\n </span>\n )}\n </div>\n )\n}\n\nexport function ChannelBadge({ channel, className }: { channel: Channel; className?: string }) {\n const Icon = CHANNEL_ICON[channel]\n return (\n <span\n className={cn(\n 'inline-flex items-center gap-1 rounded-full px-1.5 py-0.5 text-[10px] font-medium',\n CHANNEL_ACCENT[channel].badge,\n className,\n )}\n >\n <Icon className=\"h-2.5 w-2.5\" />\n {CHANNEL_LABELS[channel]}\n </span>\n )\n}\n\nexport function relativeTime(iso: string): string {\n const diff = Date.now() - new Date(iso).getTime()\n const mins = Math.round(diff / 60_000)\n if (mins < 1) return 'now'\n if (mins < 60) return `${mins}m`\n const hours = Math.round(mins / 60)\n if (hours < 24) return `${hours}h`\n const days = Math.round(hours / 24)\n if (days < 7) return `${days}d`\n return `${Math.round(days / 7)}w`\n}\n\n/** Day label for the thread date separators. */\nexport function dayLabel(iso: string): string {\n const d = new Date(iso)\n const now = new Date()\n const startOf = (x: Date) => new Date(x.getFullYear(), x.getMonth(), x.getDate()).getTime()\n const dayDiff = Math.round((startOf(now) - startOf(d)) / 86_400_000)\n if (dayDiff <= 0) return 'Today'\n if (dayDiff === 1) return 'Yesterday'\n if (dayDiff < 7) return d.toLocaleDateString([], { weekday: 'long' })\n return d.toLocaleDateString([], { month: 'short', day: 'numeric', year: now.getFullYear() === d.getFullYear() ? undefined : 'numeric' })\n}\n","import React from 'react'\nimport { Search, Inbox as InboxIcon } from 'lucide-react'\nimport { Input, Skeleton, cn } from '@fayz-ai/ui'\nimport { useTranslation } from '@fayz-ai/core'\nimport { useConversationsStore } from '../context'\nimport type { Channel } from '../types'\nimport { Avatar, ChannelBadge, relativeTime } from './shared'\n\nconst FILTERS: Array<Channel | 'all'> = ['all', 'whatsapp', 'sms', 'instagram', 'email', 'webchat']\n\nexport function ConversationList({ className }: { className?: string }) {\n const t = useTranslation()\n const {\n conversations, selectedId, channelFilter, search, loading,\n select, setChannelFilter, setSearch,\n } = useConversationsStore((s) => s)\n\n return (\n <aside className={cn('w-full shrink-0 flex-col border-r border-border bg-card lg:w-[320px]', className)}>\n {/* No title: the shell header already names the page 24px away. Starting\n a conversation moved there too — it is the PAGE's action, not the list's. */}\n <div className=\"border-b border-border px-3 py-3\">\n <div className=\"relative\">\n <Search className=\"pointer-events-none absolute left-2.5 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground\" />\n <Input\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n placeholder={t('conversations.list.search')}\n className=\"pl-8\"\n />\n </div>\n <div className=\"mt-2 flex flex-wrap gap-1\">\n {FILTERS.map((id) => (\n <button\n key={id}\n onClick={() => setChannelFilter(id)}\n className={cn(\n 'rounded-full px-2.5 py-1 text-xs font-medium transition-colors',\n channelFilter === id\n ? 'bg-primary text-primary-foreground'\n : 'bg-muted text-muted-foreground hover:bg-muted/70',\n )}\n >\n {t(`conversations.filter.${id}`)}\n </button>\n ))}\n </div>\n </div>\n\n <div className=\"min-h-0 flex-1 overflow-y-auto\">\n {loading && conversations.length === 0 &&\n Array.from({ length: 6 }, (_, i) => (\n <div key={i} className=\"flex w-full items-start gap-3 border-b border-border/50 px-3 py-3\">\n <Skeleton className=\"h-10 w-10 shrink-0 rounded-full\" />\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center justify-between gap-2\">\n <Skeleton className=\"h-4 w-28\" />\n <Skeleton className=\"h-3 w-8\" />\n </div>\n <Skeleton className=\"mt-1.5 h-4 w-16 rounded-full\" />\n <Skeleton className=\"mt-1.5 h-3 w-3/4\" />\n </div>\n </div>\n ))}\n {!loading && conversations.length === 0 && (\n <div className=\"flex flex-col items-center gap-2 p-8 text-center text-muted-foreground\">\n <InboxIcon className=\"h-6 w-6\" />\n <p className=\"text-sm\">{t('conversations.list.empty')}</p>\n </div>\n )}\n {conversations.map((c) => {\n const active = c.id === selectedId\n const unread = c.unreadCount > 0\n return (\n <button\n key={c.id}\n onClick={() => select(c.id)}\n className={cn(\n 'flex w-full items-start gap-3 border-b border-border/50 px-3 py-3 text-left transition-colors',\n // Um TOM da cor de marca, e não `bg-accent`.\n //\n // `bg-accent` resolve para quase preto neste tema, e a linha da\n // conversa aberta ficava escura sobre escura. Trocar junto o\n // `text-accent-foreground` deixava legível, mas pintava de\n // preto a única linha que se está a ler — chamava mais atenção\n // que a conversa.\n //\n // `bg-primary/10` é um véu: o texto continua com as cores dele,\n // a seleção se lê como seleção e não como bloco, e funciona nos\n // dois temas porque não presume nada sobre o fundo.\n active ? 'bg-primary/10' : 'hover:bg-muted/50',\n )}\n >\n <Avatar name={c.contactName} accent={c.accent} channel={c.channel} />\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center justify-between gap-2\">\n <span className={cn('truncate text-sm text-foreground', unread ? 'font-semibold' : 'font-medium')}>\n {c.contactName}\n </span>\n <span className={cn('shrink-0 text-[11px]', unread ? 'font-semibold text-primary' : 'text-muted-foreground')}>\n {relativeTime(c.lastMessageAt)}\n </span>\n </div>\n <div className=\"mt-1 flex items-center gap-2\">\n <ChannelBadge channel={c.channel} />\n {c.status !== 'open' && (\n <span className=\"text-[10px] uppercase tracking-wide text-muted-foreground/70\">\n {t(`conversations.status.${c.status}`)}\n </span>\n )}\n </div>\n <div className=\"mt-1 flex items-center justify-between gap-2\">\n <span className={cn('truncate text-xs', unread ? 'text-foreground' : 'text-muted-foreground')}>\n {c.lastMessagePreview}\n </span>\n {unread && (\n <span className=\"flex h-[18px] min-w-[18px] shrink-0 items-center justify-center rounded-full bg-primary px-1.5 text-[10px] font-semibold text-primary-foreground\">\n {c.unreadCount}\n </span>\n )}\n </div>\n </div>\n </button>\n )\n })}\n </div>\n </aside>\n )\n}\n","import React from 'react'\nimport { getSupabaseClientOptional, getActiveTenantId } from '@fayz-ai/core'\nimport { providersApi, type ProviderRow } from '../data/providers'\n\n/**\n * Se dá para falar com esta pessoa AGORA, e por qual caminho.\n *\n * ── o defeito que isto existe para matar ────────────────────────────────\n *\n * Na API oficial do WhatsApp, texto livre só sai dentro de 24h contadas da\n * última mensagem DO CLIENTE. Fora dela a Meta recusa — e a recusa chega num\n * log, não na tela. O atendente digita, aperta enviar, vê a mensagem aparecer\n * na conversa, e ela nunca chega ao destinatário.\n *\n * Uma tela que aceita o que não vai entregar é pior que uma tela que recusa.\n *\n * ── e por que isso muda com o provedor ──────────────────────────────────\n *\n * No Evolution não existe janela nenhuma: manda sempre. Então o MESMO campo de\n * texto é seguro num provedor e mentiroso no outro, e qual está valendo é uma\n * configuração que muda numa tela distante daqui. Por isso este gancho lê as\n * duas coisas juntas — a janela da conversa E a capacidade do provedor ativo —\n * e devolve uma resposta só: pode escrever, ou não pode e por quê.\n */\n\nexport type SendMode =\n | { kind: 'free'; until: string | null; minutesLeft: number | null }\n | { kind: 'template'; reason: 'window_closed' | 'never_replied' }\n | { kind: 'blocked'; reason: 'no_provider' }\n\nexport interface WindowState {\n loading: boolean\n mode: SendMode\n provider: ProviderRow | undefined\n}\n\ninterface WindowRow {\n window_open: boolean\n window_expires_at: string | null\n minutes_left: number | null\n last_inbound_at: string | null\n}\n\nexport function useSendWindow(conversationId: string | undefined): WindowState {\n const [row, setRow] = React.useState<WindowRow | null>(null)\n const [providers, setProviders] = React.useState<ProviderRow[] | null>(null)\n\n React.useEffect(() => {\n let alive = true\n providersApi.list().then((p) => { if (alive) setProviders(p) }).catch(() => { if (alive) setProviders([]) })\n return () => { alive = false }\n }, [])\n\n React.useEffect(() => {\n let alive = true\n if (!conversationId) { setRow(null); return }\n const sb = getSupabaseClientOptional() as any\n if (!sb) { setRow(null); return }\n sb.from('v_conversations_window')\n .select('window_open, window_expires_at, minutes_left, last_inbound_at')\n .eq('conversation_id', conversationId).eq('tenant_id', getActiveTenantId())\n .maybeSingle()\n .then(({ data }: { data: WindowRow | null }) => { if (alive) setRow(data ?? null) })\n .catch(() => { if (alive) setRow(null) })\n return () => { alive = false }\n }, [conversationId])\n\n const provider = (providers ?? []).find((p) => p.isActive)\n\n const mode = React.useMemo<SendMode>(() => {\n if (!provider) return { kind: 'blocked', reason: 'no_provider' }\n // O provedor que não exige template não tem janela: manda sempre. Dizer\n // \"janela aberta\" aqui seria inventar um conceito que ele não tem.\n if (!provider.capabilities?.requires_template) {\n return { kind: 'free', until: null, minutesLeft: null }\n }\n if (row?.window_open) {\n return { kind: 'free', until: row.window_expires_at, minutesLeft: row.minutes_left }\n }\n return { kind: 'template', reason: row?.last_inbound_at ? 'window_closed' : 'never_replied' }\n }, [row, provider])\n\n return { loading: providers === null, mode, provider }\n}\n\n/** Quanto falta, em português de gente: \"faltam 3h\" e não um carimbo ISO. */\nexport function humanLeft(minutes: number | null | undefined): string {\n if (minutes == null) return ''\n if (minutes < 60) return `${minutes} min`\n const h = Math.floor(minutes / 60)\n return h < 24 ? `${h}h` : `${Math.floor(h / 24)}d`\n}\n\nexport function SendWindowBanner({ state }: { state: WindowState }) {\n const { mode, provider } = state\n if (mode.kind === 'free' && mode.minutesLeft == null) return null\n\n if (mode.kind === 'free') {\n // Aberta, mas fechando. O relógio importa: quem responde quer saber se dá\n // tempo de resolver agora ou se precisa de template.\n const urgent = (mode.minutesLeft ?? 0) < 120\n return (\n <div className={`flex items-center gap-2 px-4 py-1.5 text-[11px] ${urgent ? 'bg-warning-soft text-warning-soft-foreground' : 'text-muted-foreground'}`}>\n <span className=\"h-1.5 w-1.5 rounded-full bg-success\" />\n Janela aberta — texto livre por mais {humanLeft(mode.minutesLeft)}\n </div>\n )\n }\n\n if (mode.kind === 'blocked') {\n return (\n <div className=\"flex items-center gap-2 bg-destructive-soft px-4 py-2 text-[11px] text-destructive-soft-foreground\">\n <span className=\"h-1.5 w-1.5 rounded-full bg-destructive\" />\n Nenhum provedor de WhatsApp ativo — configure em Ajustes › Conversas.\n </div>\n )\n }\n\n return (\n <div className=\"flex flex-wrap items-center gap-x-2 gap-y-1 bg-warning-soft px-4 py-2 text-[11px] text-warning-soft-foreground\">\n <span className=\"h-1.5 w-1.5 shrink-0 rounded-full bg-warning\" />\n <span className=\"font-medium\">\n {mode.reason === 'never_replied'\n ? 'Esta pessoa nunca respondeu.'\n : 'A janela de 24 horas fechou.'}\n </span>\n <span>\n No {provider?.provider ?? 'provedor'}, texto livre só sai dentro de 24h da\n resposta do cliente. Para falar agora é preciso um template aprovado.\n </span>\n </div>\n )\n}\n","import React from 'react'\nimport { AlertTriangle, Check, CheckCheck, Clock, Loader2, MoreVertical, X } from 'lucide-react'\nimport { getSupabaseClientOptional, getActiveTenantId } from '@fayz-ai/core'\nimport type { Message } from '../types'\n\n/**\n * O que aconteceu com esta mensagem, e o JSON cru de quem devia tê-la entregue.\n *\n * ── por que isto existe ─────────────────────────────────────────────────\n *\n * Duas mensagens ficaram um dia inteiro na caixa com a mesma cara de qualquer\n * outra — balão verde, horário, tudo igual — e nunca chegaram a ninguém. Não\n * havia como saber olhando. A tela dizia \"enviada\" porque a linha existia no\n * banco, e existir no banco não é ter saído.\n *\n * Uma mensagem que falhou precisa parecer que falhou. E quando alguém\n * perguntar \"por quê?\", a resposta não pode ser \"olha o log do servidor\".\n *\n * ── o JSON vem AO VIVO, e não de uma cópia ──────────────────────────────\n *\n * O status de uma mensagem de WhatsApp muda DEPOIS do envio: aceita, enviada,\n * entregue, lida — às vezes horas depois. Uma cópia gravada no momento do envio\n * envelhece na primeira mudança e passa a mentir com precisão de carimbo.\n * Então o detalhe é buscado no provedor na hora em que alguém abre.\n */\n\nconst STATUS: Record<string, { icon: typeof Check; label: string; cls: string }> = {\n queued: { icon: Clock, label: 'na fila', cls: 'text-white/60' },\n sent: { icon: Check, label: 'enviada', cls: 'text-white/70' },\n delivered: { icon: CheckCheck, label: 'entregue', cls: 'text-white/80' },\n read: { icon: CheckCheck, label: 'lida', cls: 'text-sky-200' },\n failed: { icon: AlertTriangle, label: 'NÃO ENVIADA', cls: 'text-white' },\n expired: { icon: AlertTriangle, label: 'expirou', cls: 'text-white' },\n delivery_timeout: { icon: AlertTriangle, label: 'sem confirmação', cls: 'text-white' },\n cancelled: { icon: X, label: 'cancelada', cls: 'text-white' },\n opted_out: { icon: X, label: 'recusada', cls: 'text-white' },\n}\n\nconst BAD = new Set(['failed', 'expired', 'cancelled', 'opted_out', 'delivery_timeout'])\n\nexport function isFailed(m: Message): boolean {\n if (m.direction !== 'outbound') return false\n // O STATUS MANDA. Ele vem do provedor e é a resposta de quem entregou.\n if (m.deliveryStatus) return BAD.has(m.deliveryStatus)\n\n // Sem status, a ausência de id do provedor é o único sinal que resta — mas\n // ela também é o estado normal nos primeiros segundos, enquanto o envio\n // acontece. Pintar de vermelho nesse intervalo faz a tela acusar de falha\n // uma mensagem que está saindo, e foi exatamente o que aconteceu com uma\n // que já tinha sido entregue.\n //\n // Dois minutos: mais que qualquer envio e menos que a paciência de quem\n // está olhando.\n if (m.providerMessageId) return false\n const age = Date.now() - new Date(m.at).getTime()\n // Uma mensagem em fila para o disparador local pode esperar mais que dois\n // minutos sem que nada esteja errado — ele confere a cada volta e a sessão\n // do WhatsApp pode estar religando.\n return Number.isFinite(age) && age > 120_000\n}\n\n/** O carimbo ao lado da hora. Some nas de entrada: elas chegaram. */\nexport function DeliveryMark({ m }: { m: Message }) {\n if (m.direction !== 'outbound') return null\n const key = isFailed(m) ? 'failed' : (m.deliveryStatus ?? 'sent')\n const cfg = STATUS[key] ?? STATUS.sent\n const Icon = cfg.icon\n return (\n <span className={`inline-flex items-center gap-0.5 ${cfg.cls}`} title={cfg.label}>\n <Icon className=\"h-3 w-3\" />\n {BAD.has(key) && <span className=\"text-[9px] font-semibold uppercase\">{cfg.label}</span>}\n </span>\n )\n}\n\nexport function MessageMenu({ m }: { m: Message }) {\n const [open, setOpen] = React.useState(false)\n const [detail, setDetail] = React.useState<unknown>(null)\n const [loading, setLoading] = React.useState(false)\n const [err, setErr] = React.useState<string | null>(null)\n\n async function load() {\n setOpen(true)\n if (!m.providerMessageId) return\n setLoading(true); setErr(null)\n try {\n const sb = getSupabaseClientOptional() as any\n const { data, error } = await sb.functions.invoke('conversations-provider', {\n body: { tenantId: getActiveTenantId(), action: 'message_detail', providerMessageId: m.providerMessageId },\n })\n if (error) {\n const d = (await error.context?.json?.().catch(() => null)) as { message?: string } | null\n throw new Error(d?.message ?? error.message)\n }\n if ((data as any)?.error) throw new Error((data as any).message ?? (data as any).error)\n setDetail((data as any).detail)\n } catch (e: any) {\n setErr(e?.message ?? 'Não consegui buscar')\n } finally {\n setLoading(false)\n }\n }\n\n return (\n <>\n <button\n onClick={() => void load()}\n aria-label=\"Detalhes da mensagem\"\n className=\"opacity-0 transition-opacity group-hover:opacity-60 hover:!opacity-100\"\n >\n <MoreVertical className=\"h-3.5 w-3.5\" />\n </button>\n\n {open && (\n <div className=\"fixed inset-0 z-[120] flex items-center justify-center bg-black/50 p-4\" onClick={() => setOpen(false)}>\n <div className=\"max-h-[80vh] w-full max-w-2xl overflow-hidden rounded-card bg-card shadow-2xl\" onClick={(e) => e.stopPropagation()}>\n <div className=\"flex items-center justify-between border-b px-4 py-3\">\n <div>\n <p className=\"text-sm font-semibold\">Detalhes da mensagem</p>\n <p className=\"text-[11px] text-muted-foreground\">\n {new Date(m.at).toLocaleString()} · {m.direction === 'outbound' ? 'saída' : 'entrada'}\n {m.deliveryStatus ? ` · ${m.deliveryStatus}` : ''}\n </p>\n </div>\n <button onClick={() => setOpen(false)} aria-label=\"Fechar\"><X className=\"h-4 w-4\" /></button>\n </div>\n\n <div className=\"max-h-[60vh] overflow-auto p-4 text-xs\">\n <p className=\"mb-1 font-medium text-muted-foreground\">Nesta base</p>\n <pre className=\"mb-4 overflow-auto rounded bg-muted/40 p-3 text-[11px] leading-relaxed\">\n{JSON.stringify({ id: m.id, direction: m.direction, body: m.body, at: m.at,\n author: m.author, deliveryStatus: m.deliveryStatus,\n providerMessageId: m.providerMessageId }, null, 2)}\n </pre>\n\n <p className=\"mb-1 font-medium text-muted-foreground\">No provedor</p>\n {!m.providerMessageId && (\n // A ausência é a informação: sem id, ninguém do outro lado\n // chegou a aceitar esta mensagem. Ela não atrasou — ela não saiu.\n <p className=\"rounded bg-destructive-soft p-3 text-[11px] text-destructive-soft-foreground\">\n Esta mensagem não tem id no provedor — ela nunca chegou a ser aceita para envio.\n Não há o que buscar do outro lado.\n </p>\n )}\n {loading && <p className=\"flex items-center gap-2 p-3 text-muted-foreground\"><Loader2 className=\"h-3 w-3 animate-spin\" /> buscando…</p>}\n {err && <p className=\"rounded bg-destructive-soft p-3 text-[11px] text-destructive-soft-foreground\">{err}</p>}\n {detail != null && (\n <pre className=\"overflow-auto rounded bg-muted/40 p-3 text-[11px] leading-relaxed\">\n{JSON.stringify(detail, null, 2)}\n </pre>\n )}\n </div>\n </div>\n </div>\n )}\n </>\n )\n}\n","import React from 'react'\nimport { useSendWindow, SendWindowBanner } from './SendWindow'\nimport { DeliveryMark, MessageMenu, isFailed } from './MessageInspector'\nimport { Send, Clock, Archive, PanelRight, MessageSquare, ChevronLeft } from 'lucide-react'\nimport { Button, cn } from '@fayz-ai/ui'\nimport { useTranslation } from '@fayz-ai/core'\nimport { useConversationsStore } from '../context'\nimport { CHANNEL_ACCENT, CHANNEL_LABELS } from '../lib/channel'\nimport type { Conversation, Message } from '../types'\nimport { Avatar, ChannelBadge, dayLabel } from './shared'\n\n// Build a flat render list: day-separator rows interleaved with messages, and\n// each message flagged as the start of a new sender-run (WhatsApp grouping).\ntype Row =\n | { kind: 'day'; id: string; label: string }\n | { kind: 'msg'; id: string; message: Message; startsRun: boolean; endsRun: boolean }\n\nfunction buildRows(messages: Message[]): Row[] {\n const rows: Row[] = []\n let lastDay = ''\n messages.forEach((m, i) => {\n const day = new Date(m.at).toDateString()\n if (day !== lastDay) {\n rows.push({ kind: 'day', id: `day-${day}`, label: dayLabel(m.at) })\n lastDay = day\n }\n const prev = messages[i - 1]\n const next = messages[i + 1]\n const samePrev = prev && prev.direction === m.direction && new Date(prev.at).toDateString() === day\n const sameNext = next && next.direction === m.direction && new Date(next.at).toDateString() === day\n rows.push({ kind: 'msg', id: m.id, message: m, startsRun: !samePrev, endsRun: !sameNext })\n })\n return rows\n}\n\nexport function MessageThread({ selected, onTogglePanel, panelOpen, onBack, className }: {\n selected: Conversation\n onTogglePanel: () => void\n panelOpen: boolean\n onBack?: () => void\n className?: string\n}) {\n const t = useTranslation()\n const { messages, sending, send, setStatus, refreshMessages } = useConversationsStore((s) => s)\n const [draft, setDraft] = React.useState('')\n // Se dá para falar com esta pessoa AGORA, e por qual caminho. Lê a janela de\n // 24h da conversa E a capacidade do provedor ativo: o mesmo campo de texto é\n // seguro num provedor e mentiroso no outro.\n const win = useSendWindow(selected.id)\n const canType = win.mode.kind === 'free'\n const threadRef = React.useRef<HTMLDivElement>(null)\n\n React.useEffect(() => {\n threadRef.current?.scrollTo({ top: threadRef.current.scrollHeight, behavior: 'smooth' })\n }, [messages.length, selected.id])\n\n // O tique duplo chega pelo webhook, no banco, sem passar por aqui. Dez\n // segundos é o intervalo que faz \"entregue\" e \"lida\" aparecerem enquanto a\n // pessoa ainda olha a conversa, e é barato: uma consulta por thread aberta.\n //\n // Para quando a aba sai de foco. Uma caixa esquecida aberta a noite toda não\n // deve consultar 8.640 vezes por nada.\n React.useEffect(() => {\n if (!selected.id) return\n let timer: ReturnType<typeof setInterval> | null = null\n const start = () => { if (!timer) timer = setInterval(() => { void refreshMessages() }, 10_000) }\n const stop = () => { if (timer) { clearInterval(timer); timer = null } }\n const onVis = () => (document.visibilityState === 'visible' ? start() : stop())\n onVis()\n document.addEventListener('visibilitychange', onVis)\n return () => { stop(); document.removeEventListener('visibilitychange', onVis) }\n }, [selected.id, refreshMessages])\n\n async function handleSend() {\n if (!draft.trim()) return\n const body = draft\n setDraft('')\n await send(body)\n }\n\n const accent = CHANNEL_ACCENT[selected.channel]\n const rows = React.useMemo(() => buildRows(messages), [messages])\n\n return (\n <section className={cn('flex min-w-0 flex-1 flex-col bg-muted/20', className)}>\n {/* Header */}\n <div className=\"flex items-center justify-between gap-2 border-b border-border bg-card px-3 py-2.5 md:px-5\">\n <div className=\"flex min-w-0 items-center gap-2 md:gap-3\">\n {onBack && (\n <Button variant=\"ghost\" size=\"icon\" className=\"-ml-1 shrink-0 lg:hidden\" onClick={onBack} aria-label={t('conversations.thread.back')}>\n <ChevronLeft className=\"h-5 w-5\" />\n </Button>\n )}\n <Avatar name={selected.contactName} accent={selected.accent} size=\"sm\" channel={selected.channel} />\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-semibold text-foreground\">{selected.contactName}</p>\n <p className=\"flex items-center gap-1.5 text-xs text-muted-foreground\">\n <ChannelBadge channel={selected.channel} />\n <span className=\"truncate\">{selected.contactHandle}</span>\n </p>\n </div>\n </div>\n <div className=\"flex shrink-0 items-center gap-1.5\">\n {/* ABRIR O LEAD, no lugar de \"Adiar\" e \"Encerrar\".\n Aqueles dois são vocabulário de suporte — uma fila de chamados que\n se despacha. Aqui a conversa é uma VENDA em andamento: ninguém\n \"encerra\" um lead que respondeu, e adiar é o que a fila da rota já\n faz com data. O que faltava era o caminho da conversa para a\n ficha, e ele não existia em lugar nenhum da tela. */}\n <Button variant=\"outline\" size=\"sm\" onClick={() => setStatus('closed')} aria-label=\"Arquivar\">\n <Archive className=\"h-3.5 w-3.5 sm:mr-1\" /> <span className=\"hidden sm:inline\">Arquivar</span>\n </Button>\n <Button\n variant={panelOpen ? 'secondary' : 'ghost'}\n size=\"icon\"\n onClick={onTogglePanel}\n aria-label={t('conversations.thread.details')}\n >\n <PanelRight className=\"h-4 w-4\" />\n </Button>\n </div>\n </div>\n\n {/* Messages */}\n <div ref={threadRef} className=\"min-h-0 flex-1 overflow-y-auto px-5 py-4\">\n {rows.map((row) => {\n if (row.kind === 'day') {\n return (\n <div key={row.id} className=\"my-3 flex items-center justify-center\">\n <span className=\"rounded-full bg-muted px-3 py-0.5 text-[11px] font-medium text-muted-foreground\">\n {row.label}\n </span>\n </div>\n )\n }\n const m = row.message\n const outbound = m.direction === 'outbound'\n const failed = isFailed(m)\n return (\n <div\n key={row.id}\n className={cn('group flex items-center gap-1', outbound ? 'justify-end' : 'justify-start', row.startsRun ? 'mt-2.5' : 'mt-0.5')}\n >\n {/* O menu fica FORA do balão e do lado de dentro da conversa, para\n não empurrar o texto nem cobrir a hora. Aparece no hover. */}\n {outbound && <MessageMenu m={m} />}\n <div\n className={cn(\n 'max-w-[68%] px-3.5 py-2 text-sm shadow-sm',\n outbound ? 'rounded-2xl text-white' : 'rounded-2xl bg-card text-foreground',\n // Tail only on the last bubble of a run, on the sender's side.\n outbound && row.endsRun && 'rounded-br-sm',\n !outbound && row.endsRun && 'rounded-bl-sm',\n // UMA MENSAGEM QUE FALHOU PRECISA PARECER QUE FALHOU. Duas\n // ficaram um dia na caixa com a cara de qualquer outra e nunca\n // chegaram a ninguém — não havia como saber olhando.\n outbound && failed && 'ring-2 ring-destructive',\n )}\n style={outbound ? { backgroundColor: failed ? 'hsl(var(--destructive))' : accent.color } : undefined}\n >\n <p className=\"whitespace-pre-wrap break-words\">{m.body}</p>\n <div className={cn('mt-0.5 flex items-center justify-end gap-1 text-[10px]', outbound ? 'text-white/70' : 'text-muted-foreground')}>\n {new Date(m.at).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}\n <DeliveryMark m={m} />\n </div>\n </div>\n {!outbound && <MessageMenu m={m} />}\n </div>\n )\n })}\n {rows.length === 0 && (\n <div className=\"flex h-full flex-col items-center justify-center text-muted-foreground\">\n <MessageSquare className=\"h-7 w-7\" />\n <p className=\"mt-2 text-sm\">{t('conversations.thread.empty')}</p>\n </div>\n )}\n </div>\n\n {/* Composer */}\n {/* TODO(follow-up): wire real emoji picker + attachment upload (removed the\n dead placeholder buttons rather than shipping non-functional UI). */}\n <div className=\"border-t border-border bg-card\">\n <SendWindowBanner state={win} />\n <div className=\"flex items-end gap-2 px-4 py-3\">\n <textarea\n value={draft}\n onChange={(e) => setDraft(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault()\n void handleSend()\n }\n }}\n rows={1}\n disabled={!canType}\n placeholder={\n canType\n ? t('conversations.thread.reply', { channel: CHANNEL_LABELS[selected.channel] })\n : win.mode.kind === 'blocked'\n ? 'Envio bloqueado'\n : 'Fora da janela — envie um template'\n }\n className=\"max-h-32 min-h-[40px] flex-1 resize-none rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground outline-none focus:border-primary disabled:cursor-not-allowed disabled:bg-muted/40 disabled:text-muted-foreground\"\n />\n <Button onClick={() => void handleSend()} disabled={!canType || sending || !draft.trim()} aria-label={t('conversations.thread.send')}>\n <Send className=\"h-4 w-4\" />\n </Button>\n </div>\n </div>\n </section>\n )\n}\n","import React from 'react'\nimport { ExternalLink, Flame, Snowflake, Sun, Building2 } from 'lucide-react'\nimport { getSupabaseClientOptional, getActiveTenantId } from '@fayz-ai/core'\n\n/**\n * O lead, dentro da conversa.\n *\n * ── por que estes campos e não a ficha inteira ──────────────────────────\n *\n * Quem está respondendo tem uma pergunta só: \"com quem eu estou falando, e isto\n * é importante?\". A ficha inteira responde isso e mais quarenta coisas, e o\n * custo de ler quarenta é não ler nenhuma.\n *\n * Então: nome, empresa, temperatura, e os motivos que a produziram. O resto\n * está a um clique — e o clique existe, que é o outro defeito que isto conserta:\n * dava para ver o telefone do lead na conversa e não dava para chegar nele.\n *\n * ── sem import entre plugins ────────────────────────────────────────────\n *\n * Isto vive no plugin de conversas e lê tabelas do CRM. Não importa nada de\n * `plugin-crm`: as tabelas são do schema público com RLS por inquilino, e um\n * import amarraria a caixa de entrada a um plugin que pode não estar montado.\n * Mesmo princípio do `TalkToPerson`, que faz o caminho inverso.\n */\n\ninterface LeadBrief {\n id: string\n name: string\n company: string | null\n status: string | null\n sourceName: string | null\n temperature: 'hot' | 'warm' | 'cold' | null\n fitScore: number | null\n interestScore: number | null\n reasons: string[]\n}\n\nconst TEMP: Record<string, { icon: typeof Flame; cls: string; label: string }> = {\n hot: { icon: Flame, cls: 'bg-destructive-soft text-destructive-soft-foreground', label: 'Quente' },\n warm: { icon: Sun, cls: 'bg-warning-soft text-warning-soft-foreground', label: 'Morno' },\n cold: { icon: Snowflake, cls: 'bg-info-soft text-info-soft-foreground', label: 'Frio' },\n}\n\nexport function LeadCard({ personId, handle }: { personId?: string | null; handle?: string | null }) {\n const [lead, setLead] = React.useState<LeadBrief | null | undefined>(undefined)\n\n React.useEffect(() => {\n let alive = true\n const sb = getSupabaseClientOptional() as any\n const tenantId = getActiveTenantId()\n if (!sb || !tenantId || (!personId && !handle)) { setLead(null); return }\n\n ;(async () => {\n try {\n let q = sb.from('people')\n .select('id, name, metadata, phone')\n .eq('tenant_id', tenantId).eq('kind', 'lead').limit(1)\n // Pelo id quando a conversa já está vinculada; pelo telefone quando\n // não — e por DÍGITOS, porque o mesmo número aparece em três grafias.\n q = personId\n ? q.eq('id', personId)\n : q.like('phone', `%${String(handle).replace(/\\D/g, '').slice(-8)}`)\n const { data } = await q.maybeSingle()\n if (!alive) return\n if (!data) { setLead(null); return }\n\n const meta = (data.metadata ?? {}) as any\n let score: any = null\n try {\n const { data: s } = await sb.from('v_crm_lead_score')\n .select('temperature, fit_score, interest_score, reasons')\n .eq('lead_id', data.id).maybeSingle()\n score = s\n } catch { /* sem nota, o cartão mostra o resto */ }\n\n setLead({\n id: data.id, name: data.name,\n company: meta.company ?? null,\n status: meta.status ?? null,\n sourceName: meta.sourceName ?? null,\n temperature: score?.temperature ?? null,\n fitScore: score?.fit_score ?? null,\n interestScore: score?.interest_score ?? null,\n reasons: Array.isArray(score?.reasons) ? score.reasons : [],\n })\n } catch { if (alive) setLead(null) }\n })()\n return () => { alive = false }\n }, [personId, handle])\n\n if (lead === undefined) return <div className=\"px-4 py-3 text-xs text-muted-foreground\">carregando…</div>\n if (!lead) {\n return (\n <div className=\"border-t border-border px-4 py-3\">\n <p className=\"text-xs text-muted-foreground\">\n Este contato ainda não é um lead cadastrado.\n </p>\n </div>\n )\n }\n\n const t = lead.temperature ? TEMP[lead.temperature] : null\n const Icon = t?.icon\n\n return (\n <div className=\"border-t border-border px-4 py-3\">\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-semibold\">{lead.name}</p>\n {lead.company && (\n <p className=\"flex items-center gap-1 truncate text-xs text-muted-foreground\">\n <Building2 className=\"h-3 w-3 shrink-0\" /> {lead.company}\n </p>\n )}\n </div>\n {/* O caminho que faltava: da conversa para a ficha. */}\n <a\n href={`#/sales/leads/${lead.id}`}\n className=\"inline-flex shrink-0 items-center gap-1 rounded-button border bg-card px-2 py-1 text-[11px] font-medium hover:bg-muted\"\n >\n Abrir lead <ExternalLink className=\"h-3 w-3\" />\n </a>\n </div>\n\n <div className=\"mt-2 flex flex-wrap items-center gap-1.5\">\n {t && Icon && (\n <span className={`inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-[10px] font-medium ${t.cls}`}>\n <Icon className=\"h-3 w-3\" /> {t.label}\n {lead.interestScore != null && <span className=\"tabular-nums opacity-70\">{lead.interestScore}</span>}\n </span>\n )}\n {lead.status && (\n <span className=\"rounded-full bg-muted px-2 py-0.5 text-[10px] capitalize text-muted-foreground\">{lead.status}</span>\n )}\n {lead.sourceName && (\n <span className=\"rounded-full bg-muted px-2 py-0.5 text-[10px] text-muted-foreground\">{lead.sourceName}</span>\n )}\n </div>\n\n {/* O PORQUÊ da temperatura. Sem ele o chip é um número que ninguém\n confere, e quem está respondendo não sabe o que puxar na conversa. */}\n {lead.reasons.length > 0 && (\n <ul className=\"mt-2 space-y-0.5\">\n {lead.reasons.slice(0, 4).map((r, i) => (\n <li key={i} className=\"flex gap-1.5 text-[11px] text-muted-foreground\">\n <span className=\"text-muted-foreground/40\">•</span><span>{r}</span>\n </li>\n ))}\n </ul>\n )}\n </div>\n )\n}\n","import React from 'react'\nimport { LeadCard } from './LeadCard'\nimport { MapPin, User, Tag, Link2, StickyNote, X } from 'lucide-react'\nimport { Button, cn } from '@fayz-ai/ui'\nimport { useTranslation } from '@fayz-ai/core'\nimport { CHANNEL_LABELS } from '../lib/channel'\nimport type { Conversation } from '../types'\nimport { Avatar, ChannelBadge } from './shared'\n\nfunction Section({ icon: Icon, title, children }: {\n icon: React.ComponentType<{ className?: string }>\n title: string\n children: React.ReactNode\n}) {\n return (\n <div className=\"border-t border-border px-4 py-3\">\n <p className=\"mb-1.5 flex items-center gap-1.5 text-[11px] font-semibold uppercase tracking-wide text-muted-foreground\">\n <Icon className=\"h-3 w-3\" /> {title}\n </p>\n {children}\n </div>\n )\n}\n\nexport function ContactPanel({ contact, onClose, className }: {\n contact: Conversation\n onClose?: () => void\n className?: string\n}) {\n const t = useTranslation()\n return (\n <aside className={cn('flex shrink-0 flex-col overflow-y-auto border-l border-border bg-card', className)}>\n {onClose && (\n <div className=\"flex items-center justify-between border-b border-border px-3 py-2 xl:hidden\">\n <span className=\"text-sm font-semibold text-foreground\">{t('conversations.contact.details')}</span>\n <Button variant=\"ghost\" size=\"icon\" onClick={onClose} aria-label={t('conversations.contact.closeDetails')}>\n <X className=\"h-4 w-4\" />\n </Button>\n </div>\n )}\n <div className=\"flex flex-col items-center gap-2 px-4 py-5 text-center\">\n <Avatar name={contact.contactName} accent={contact.accent} size=\"lg\" channel={contact.channel} />\n <div>\n <p className=\"text-sm font-semibold text-foreground\">{contact.contactName}</p>\n <p className=\"text-xs text-muted-foreground\">{contact.contactHandle}</p>\n </div>\n <ChannelBadge channel={contact.channel} />\n </div>\n\n {/* O LEAD PRIMEIRO. \"Canal / Status / Responsável\" descrevem a\n conversa; quem está respondendo precisa saber de QUEM ela é — e\n precisa do caminho para a ficha, que não existia. */}\n <LeadCard personId={contact.contactPersonId} handle={contact.contactHandle} />\n\n <Section icon={User} title={t('conversations.contact.details')}>\n <dl className=\"space-y-1.5 text-sm\">\n <div className=\"flex items-center justify-between gap-2\">\n <dt className=\"text-muted-foreground\">{t('conversations.contact.channel')}</dt>\n <dd className=\"text-foreground\">{CHANNEL_LABELS[contact.channel]}</dd>\n </div>\n <div className=\"flex items-center justify-between gap-2\">\n <dt className=\"text-muted-foreground\">{t('conversations.contact.status')}</dt>\n <dd className=\"text-foreground\">{t(`conversations.status.${contact.status}`)}</dd>\n </div>\n {contact.assignedTo && (\n <div className=\"flex items-center justify-between gap-2\">\n <dt className=\"text-muted-foreground\">{t('conversations.contact.assignedTo')}</dt>\n <dd className=\"text-foreground\">{contact.assignedTo}</dd>\n </div>\n )}\n {contact.location && (\n <div className=\"flex items-center justify-between gap-2\">\n <dt className=\"flex items-center gap-1 text-muted-foreground\"><MapPin className=\"h-3 w-3\" /> {t('conversations.contact.location')}</dt>\n <dd className=\"text-foreground\">{contact.location}</dd>\n </div>\n )}\n </dl>\n </Section>\n\n {contact.tags.length > 0 && (\n <Section icon={Tag} title={t('conversations.contact.tags')}>\n <div className=\"flex flex-wrap gap-1.5\">\n {contact.tags.map((tag) => (\n <span key={tag} className=\"rounded-full bg-muted px-2 py-0.5 text-xs font-medium text-foreground\">\n {tag}\n </span>\n ))}\n </div>\n </Section>\n )}\n\n {contact.note && (\n <Section icon={StickyNote} title={t('conversations.contact.note')}>\n <p className=\"text-sm text-foreground\">{contact.note}</p>\n </Section>\n )}\n\n <Section icon={Link2} title={t('conversations.contact.linkedRecords')}>\n <p className=\"text-xs text-muted-foreground\">{t('conversations.contact.noLinkedRecords')}</p>\n </Section>\n </aside>\n )\n}\n","import { errorMessage } from '@fayz-ai/core'\nimport React from 'react'\nimport { Button, cn, toast } from '@fayz-ai/ui'\nimport { useTranslation } from '@fayz-ai/core'\nimport {\n useLimitGuard, invalidateLimit, ContactPicker, RightRailPage,\n type ContactPickerValue,\n} from '@fayz-ai/admin'\nimport { useConversationsStore, useConversationsConfig } from '../context'\nimport { CHANNEL_ACCENT, CHANNEL_ICON, CHANNEL_LABELS } from '../lib/channel'\nimport type { Channel } from '../types'\n\nconst CHANNELS: Channel[] = ['whatsapp', 'sms', 'instagram', 'email', 'webchat']\n\n/**\n * Which field of a person IS the handle on a given channel. Phone channels read\n * the phone, email reads the email; Instagram and web chat have no counterpart\n * on a person record, so they always have to be typed. Deliberately NOT\n * cross-filling (an email in a WhatsApp handle looked plausible on screen and\n * was simply wrong).\n */\nfunction personHandleFor(channel: Channel, contact: ContactPickerValue | null): string {\n if (!contact) return ''\n if (channel === 'email') return contact.email ?? ''\n if (channel === 'sms' || channel === 'whatsapp') return contact.phone ?? ''\n return ''\n}\n\nconst HANDLE_LABEL_KEY: Record<Channel, string> = {\n whatsapp: 'conversations.new.handleLabel.phone',\n sms: 'conversations.new.handleLabel.phone',\n email: 'conversations.new.handleLabel.email',\n instagram: 'conversations.new.handleLabel.instagram',\n webchat: 'conversations.new.handleLabel.webchat',\n}\n\n/**\n * Starting a conversation, in the right column.\n *\n * It was a dialog over the inbox — over the very list you read to decide\n * whether a thread already exists. Beside it, that list stays legible while the\n * contact is chosen.\n */\nexport function NewConversationPanel({\n open,\n onOpenChange,\n}: {\n open: boolean\n onOpenChange: (open: boolean) => void\n}) {\n const t = useTranslation()\n const create = useConversationsStore((s) => s.create)\n const config = useConversationsConfig()\n const guardConversations = useLimitGuard('conversations_month')\n\n const [channel, setChannel] = React.useState<Channel>('whatsapp')\n const [contact, setContact] = React.useState<ContactPickerValue | null>(null)\n // Handle the user typed themselves. The one taken FROM the contact is derived\n // (see personHandleFor) and shown on the picker's chip — same as the agenda,\n // which never had a second phone field.\n const [typedHandle, setTypedHandle] = React.useState('')\n // While the picker's inline create form is open it already asks for phone and\n // email, so showing our own handle field would ask for the phone twice.\n const [creatingContact, setCreatingContact] = React.useState(false)\n const [firstMessage, setFirstMessage] = React.useState('')\n const [submitting, setSubmitting] = React.useState(false)\n\n // Reset the form each time the modal opens. `pickerKey` remounts the picker so\n // its internal search text resets too.\n const [pickerKey, setPickerKey] = React.useState(0)\n React.useEffect(() => {\n if (open) {\n setChannel('whatsapp')\n setContact(null)\n setTypedHandle('')\n setCreatingContact(false)\n setFirstMessage('')\n setSubmitting(false)\n setPickerKey((k) => k + 1)\n }\n }, [open])\n\n // What we'd message on this channel: the contact's own datum when they have\n // one, otherwise whatever the user typed. Recomputed on every channel switch,\n // so flipping WhatsApp → Email swaps phone for email with no stale state.\n const derivedHandle = personHandleFor(channel, contact)\n const effectiveHandle = derivedHandle || typedHandle\n const handleLabel = t(HANDLE_LABEL_KEY[channel])\n\n const canSubmit = (contact?.name.trim().length ?? 0) > 0 && !submitting\n\n async function handleSubmit(e: React.FormEvent) {\n e.preventDefault()\n if (!canSubmit) return\n setSubmitting(true)\n try {\n // Plan quantity guard (client-side, before the store call): opens the\n // global UpgradeModal and aborts when the monthly cap is reached. Inside\n // the try on purpose — it hits the network to count usage, and a rejection\n // out here used to escape unhandled, leaving the modal open with no\n // feedback at all (indistinguishable from a dead button).\n if ((await guardConversations()) === 'blocked') return\n await create({\n channel,\n contactName: contact!.name.trim(),\n contactPersonId: contact?.id,\n contactHandle: effectiveHandle.trim() || undefined,\n firstMessage: firstMessage.trim() || undefined,\n })\n invalidateLimit('conversations_month')\n onOpenChange(false)\n } catch (err) {\n // A failed insert (unresolved tenant, RLS rejection, offline…) previously\n // threw past the `finally` — `onOpenChange(false)` never ran, so the modal\n // stayed open with no feedback and the thread silently vanished. Surface\n // the failure and keep the form open so the user can retry.\n const message = errorMessage(err)\n toast.error(t('conversations.new.createFailed'), { description: message })\n } finally {\n setSubmitting(false)\n }\n }\n\n return (\n <RightRailPage\n title={t('conversations.new.title')}\n open={open}\n onClose={() => onOpenChange(false)}\n width=\"max-w-md\"\n >\n <form onSubmit={handleSubmit} className=\"flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto px-5 py-4\">\n {/* Channel picker */}\n <div>\n <label className=\"mb-1.5 block text-xs font-medium text-muted-foreground\">\n {t('conversations.new.channel')}\n </label>\n <div className=\"flex flex-wrap gap-1.5\">\n {CHANNELS.map((ch) => {\n const Icon = CHANNEL_ICON[ch]\n const active = channel === ch\n return (\n <button\n key={ch}\n type=\"button\"\n onClick={() => setChannel(ch)}\n aria-pressed={active}\n className={cn(\n 'inline-flex items-center gap-1.5 rounded-full px-3 py-1.5 text-xs font-medium transition-colors',\n active\n ? 'text-white'\n : 'bg-muted text-muted-foreground hover:bg-muted/70',\n )}\n style={active ? { backgroundColor: CHANNEL_ACCENT[ch].color } : undefined}\n >\n <Icon className=\"h-3 w-3\" />\n {CHANNEL_LABELS[ch]}\n </button>\n )\n })}\n </div>\n </div>\n\n {/* Contact — shared find-or-create flow (@fayz-ai/admin), the same one\n the agenda uses to pick a client. The picker owns its label, the\n search field, the chip and the channel handle, so this composer and\n the appointment modal render the SAME thing from the same code. */}\n <ContactPicker\n key={pickerKey}\n value={contact}\n onChange={setContact}\n kind={config.contactKind}\n lookup={config.contactLookup}\n createEntityDef={config.contactEntityDef}\n allowFreeText\n onCreatingChange={setCreatingContact}\n autoFocus\n label={t('conversations.new.contactName')}\n placeholder={t('conversations.new.contactNamePlaceholder')}\n secondaryText={derivedHandle || undefined}\n handleField={{\n label: handleLabel,\n derived: derivedHandle || undefined,\n value: typedHandle,\n onChange: setTypedHandle,\n fieldLabel: t('conversations.new.handle'),\n placeholder: t('conversations.new.handlePlaceholder'),\n }}\n />\n\n {/* First message */}\n <div>\n <label htmlFor=\"conv-first-message\" className=\"mb-1.5 block text-xs font-medium text-muted-foreground\">\n {t('conversations.new.firstMessage')}\n </label>\n <textarea\n id=\"conv-first-message\"\n value={firstMessage}\n onChange={(e) => setFirstMessage(e.target.value)}\n rows={3}\n placeholder={t('conversations.new.firstMessagePlaceholder')}\n className=\"max-h-40 min-h-[64px] w-full resize-none rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground outline-none focus:border-primary\"\n />\n </div>\n\n {/* No Cancel: the column header already holds the way out. */}\n <div className=\"mt-auto pt-2\">\n <Button type=\"submit\" disabled={!canSubmit} className=\"w-full\">\n {submitting ? t('conversations.new.creating') : t('conversations.new.create')}\n </Button>\n </div>\n </form>\n </RightRailPage>\n )\n}\n","import React from 'react'\nimport { MessageSquare, SquarePen } from 'lucide-react'\nimport { Button, PageHeaderActions, cn } from '@fayz-ai/ui'\nimport { PermissionGate } from '@fayz-ai/admin'\nimport { useTranslation } from '@fayz-ai/core'\nimport { useConversationsStore } from '../context'\nimport { ConversationList } from './ConversationList'\nimport { MessageThread } from './MessageThread'\nimport { ContactPanel } from './ContactPanel'\nimport { NewConversationPanel } from './NewConversationPanel'\nimport { useMediaQuery } from './shared'\n\nexport function InboxView() {\n const t = useTranslation()\n const { conversations, selectedId, deselect } = useConversationsStore((s) => s)\n // xl+ shows the contact panel inline (three panes); below that it's an\n // on-demand slide-over so the list + thread keep room to breathe.\n const isWidePanel = useMediaQuery('(min-width: 1280px)')\n const [panelOpen, setPanelOpen] = React.useState(false)\n const [newOpen, setNewOpen] = React.useState(false)\n const selected = conversations.find((c) => c.id === selectedId) ?? null\n\n // Open the panel by default only when it fits inline; collapse it otherwise.\n React.useEffect(() => {\n setPanelOpen(isWidePanel)\n }, [isWidePanel])\n\n return (\n <>\n {/* Outside the inbox row: with a header slot this portals and takes no\n space; without one it falls in as a block above the screen, rather\n than a column wedged between the list and the thread. */}\n <PermissionGate feature=\"conversations\" action=\"create\">\n <PageHeaderActions>\n <Button\n size=\"sm\"\n onClick={() => setNewOpen(true)}\n aria-label={t('conversations.list.new')}\n data-testid=\"conversations-new\"\n >\n <SquarePen className=\"h-3.5 w-3.5 sm:mr-1.5\" />\n <span className=\"hidden sm:inline\">{t('conversations.list.new')}</span>\n </Button>\n </PageHeaderActions>\n </PermissionGate>\n\n <NewConversationPanel open={newOpen} onOpenChange={setNewOpen} />\n\n <div className=\"relative flex h-full min-h-0 overflow-hidden bg-background\">\n {/* Conversation list — single-pane below lg (full width), fixed rail at lg+.\n Below lg it yields to the thread once a conversation is open. */}\n <ConversationList className={cn(selected ? 'hidden lg:flex' : 'flex')} />\n\n {/* Thread — hidden below lg until a conversation is selected. */}\n {selected ? (\n <MessageThread\n selected={selected}\n panelOpen={panelOpen}\n onTogglePanel={() => setPanelOpen((v) => !v)}\n onBack={deselect}\n className=\"flex\"\n />\n ) : (\n <section className=\"hidden min-w-0 flex-1 flex-col items-center justify-center bg-muted/20 text-muted-foreground lg:flex\">\n <MessageSquare className=\"h-9 w-9\" />\n <p className=\"mt-2 text-sm\">{t('conversations.empty.select')}</p>\n </section>\n )}\n\n {/* Contact panel — inline column at xl, slide-over overlay below xl. */}\n {selected && panelOpen && (\n <>\n <button\n className=\"absolute inset-0 z-10 bg-black/40 xl:hidden\"\n aria-label=\"Close details\"\n onClick={() => setPanelOpen(false)}\n />\n <ContactPanel\n contact={selected}\n onClose={() => setPanelOpen(false)}\n className=\"absolute inset-y-0 right-0 z-20 w-[340px] max-w-[88%] shadow-2xl xl:static xl:z-auto xl:w-[300px] xl:max-w-none xl:shadow-none\"\n />\n </>\n )}\n </div>\n </>\n )\n}\n","import React from 'react'\nimport type { StoreApi } from 'zustand/vanilla'\nimport { ConversationsContextProvider, type ResolvedConversationsConfig } from '../context'\nimport type { ConversationsUIState } from '../store'\nimport type { Channel } from '../types'\nimport { InboxView } from './InboxView'\n\nconst CHANNELS: Channel[] = ['sms', 'whatsapp', 'instagram', 'email', 'webchat']\n\n/** Quem o link de entrada pediu para abrir.\n *\n * A rota da shell é por hash, então os parâmetros vêm depois do `?` DENTRO do\n * hash — `#/conversations?personId=…`. `window.location.search` está vazio\n * aqui, e foi o primeiro lugar onde procurei.\n */\nfunction requestedContact(): { personId?: string; name: string; handle: string; channel: Channel } | null {\n try {\n const hash = window.location.hash\n const qs = hash.includes('?') ? hash.slice(hash.indexOf('?') + 1) : ''\n if (!qs) return null\n const p = new URLSearchParams(qs)\n const handle = (p.get('handle') ?? '').trim()\n const name = (p.get('name') ?? '').trim()\n const personId = (p.get('personId') ?? '').trim() || undefined\n // Sem por onde falar não há conversa para abrir. Criar uma thread sem\n // handle produziria um registro que nunca entrega nada.\n if (!handle) return null\n const raw = (p.get('channel') ?? 'whatsapp').toLowerCase()\n const channel = (CHANNELS as string[]).includes(raw) ? (raw as Channel) : 'whatsapp'\n return { personId, name: name || handle, handle, channel }\n } catch {\n return null\n }\n}\n\nexport function ConversationsPage({ store, config }: {\n store: StoreApi<ConversationsUIState>\n config?: ResolvedConversationsConfig\n}) {\n React.useEffect(() => {\n const wanted = requestedContact()\n if (!wanted) {\n void store.getState().load()\n return\n }\n\n // `openForPerson` carrega a caixa sozinho quando precisa, então não há\n // `load()` aqui: os dois juntos disputariam a mesma lista e a thread recém\n // aberta poderia ser substituída pela resposta mais lenta.\n void store.getState().openForPerson(wanted).catch(() => {\n void store.getState().load()\n })\n\n // O parâmetro é uma ORDEM, não um estado: cumprida uma vez, sai da barra de\n // endereços. Sem isto, um F5 meia hora depois reabriria — ou pior,\n // recriaria — a conversa daquela pessoa, e o link ficaria colado no\n // histórico do navegador.\n try {\n const hash = window.location.hash\n const clean = hash.includes('?') ? hash.slice(0, hash.indexOf('?')) : hash\n window.history.replaceState(null, '', clean || '#/conversations')\n } catch { /* a barra fica como está; a conversa já abriu */ }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n return (\n <ConversationsContextProvider store={store} config={config}>\n <InboxView />\n </ConversationsContextProvider>\n )\n}\n","import type { Channel } from '../types'\n\n// Solid brand hex per channel — mirrors CHANNEL_ACCENT in ../channel.ts but\n// React-free so the data providers can stamp a sensible avatar accent on new\n// conversations without importing the icon layer.\nexport const CHANNEL_ACCENT_HEX: Record<Channel, string> = {\n whatsapp: '#22c55e',\n sms: '#6366f1',\n instagram: '#ec4899',\n email: '#0ea5e9',\n webchat: '#f59e0b',\n}\n","import type { ConversationsProvider } from './types'\nimport { CHANNEL_ACCENT_HEX } from './accents'\nimport type {\n Conversation,\n Message,\n ListConversationsQuery,\n SendMessageInput,\n CreateConversationInput,\n ConversationStatus,\n} from '../types'\n\n// Deterministic relative timestamps (no Date.now at module init for stable seeds).\nfunction minutesAgo(base: number, mins: number): string {\n return new Date(base - mins * 60_000).toISOString()\n}\n\nfunction seed(): { conversations: Conversation[]; messages: Message[] } {\n const base = new Date('2026-06-16T14:00:00Z').getTime()\n\n const conversations: Conversation[] = [\n {\n id: 'c1', contactName: 'Marina Alves', contactHandle: '+55 11 99876-1020', channel: 'whatsapp',\n lastMessagePreview: 'Perfect, can we book for Friday at 3pm?', lastMessageAt: minutesAgo(base, 4),\n lastMessageDirection: 'inbound',\n unreadCount: 2, status: 'open', assignedTo: 'You', accent: '#22c55e', tags: ['Hot lead'],\n location: 'São Paulo, BR', note: 'Referred by Instagram ad — interested in full color + cut.',\n },\n {\n id: 'c2', contactName: 'Jordan Pierce', contactHandle: '+1 (415) 555-0142', channel: 'sms',\n lastMessagePreview: 'Got it — sending the deposit now.', lastMessageAt: minutesAgo(base, 22),\n lastMessageDirection: 'inbound',\n unreadCount: 0, status: 'open', assignedTo: 'You', accent: '#6366f1', tags: ['Customer'],\n location: 'San Francisco, US',\n },\n {\n id: 'c3', contactName: '@thehairloft', contactHandle: 'thehairloft', channel: 'instagram',\n lastMessagePreview: 'Do you offer balayage on weekends?', lastMessageAt: minutesAgo(base, 51),\n lastMessageDirection: 'inbound',\n unreadCount: 1, status: 'open', accent: '#ec4899', tags: ['New'],\n },\n {\n id: 'c4', contactName: 'David Whitman', contactHandle: 'david@whitman.co', channel: 'email',\n lastMessagePreview: 'Re: Proposal — looks great, one question on pricing…', lastMessageAt: minutesAgo(base, 95),\n // Read, never answered — zero unread and a customer still waiting. The\n // exact case `unreadCount` alone gets wrong.\n lastMessageDirection: 'inbound',\n unreadCount: 0, status: 'open', assignedTo: 'Sofia', accent: '#0ea5e9', tags: ['Proposal'],\n location: 'Austin, US', note: 'Evaluating the retainer tier. Decision expected this week.',\n },\n {\n id: 'c5', contactName: 'Website visitor', contactHandle: 'live chat · acme.com', channel: 'webchat',\n lastMessagePreview: 'Is anyone available to chat?', lastMessageAt: minutesAgo(base, 140),\n lastMessageDirection: 'inbound',\n unreadCount: 0, status: 'snoozed', accent: '#f59e0b', tags: [],\n },\n {\n id: 'c6', contactName: 'Priya Nair', contactHandle: '+44 7700 900123', channel: 'whatsapp',\n lastMessagePreview: 'Thank you! See you next week 🙌', lastMessageAt: minutesAgo(base, 1440),\n lastMessageDirection: 'outbound',\n unreadCount: 0, status: 'closed', assignedTo: 'You', accent: '#14b8a6', tags: ['Customer'],\n location: 'London, UK',\n },\n ]\n\n const messages: Message[] = [\n msg('m1', 'c1', 'whatsapp', 'inbound', 'Hi! I saw your ad — do you have availability this week?', 'Marina Alves', minutesAgo(base, 18)),\n msg('m2', 'c1', 'whatsapp', 'outbound', 'Hi Marina! Yes, we do. What service are you interested in?', 'You', minutesAgo(base, 15)),\n msg('m3', 'c1', 'whatsapp', 'inbound', 'A full color + cut.', 'Marina Alves', minutesAgo(base, 9)),\n msg('m4', 'c1', 'whatsapp', 'inbound', 'Perfect, can we book for Friday at 3pm?', 'Marina Alves', minutesAgo(base, 4)),\n\n msg('m5', 'c2', 'sms', 'outbound', 'Your appointment is confirmed for tomorrow at 10am.', 'You', minutesAgo(base, 40)),\n msg('m6', 'c2', 'sms', 'inbound', 'Got it — sending the deposit now.', 'Jordan Pierce', minutesAgo(base, 22)),\n\n msg('m7', 'c3', 'instagram', 'inbound', 'Do you offer balayage on weekends?', '@thehairloft', minutesAgo(base, 51)),\n\n msg('m8', 'c4', 'email', 'inbound', 'Re: Proposal — looks great, one question on pricing for the retainer tier.', 'David Whitman', minutesAgo(base, 95)),\n msg('m9', 'c4', 'email', 'outbound', 'Happy to walk you through it — are you free for a quick call tomorrow?', 'Sofia', minutesAgo(base, 80)),\n\n msg('m10', 'c5', 'webchat', 'inbound', 'Is anyone available to chat?', 'Website visitor', minutesAgo(base, 140)),\n\n msg('m11', 'c6', 'whatsapp', 'outbound', 'You are all set for next Tuesday. Anything else?', 'You', minutesAgo(base, 1500)),\n msg('m12', 'c6', 'whatsapp', 'inbound', 'Thank you! See you next week 🙌', 'Priya Nair', minutesAgo(base, 1440)),\n ]\n\n return { conversations, messages }\n}\n\nfunction msg(\n id: string, conversationId: string, channel: Message['channel'],\n direction: Message['direction'], body: string, author: string, at: string,\n): Message {\n return { id, conversationId, channel, direction, body, author, at }\n}\n\nexport interface MockConversationsConfig {\n /** Tenant id (value or getter) used to namespace the localStorage snapshot. */\n tenantId?: string | (() => string | undefined)\n /** Display name stamped as the author of outbound messages. */\n selfAuthor?: string\n}\n\ninterface Snapshot {\n conversations: Conversation[]\n messages: Message[]\n counter: number\n}\n\n// ---------------------------------------------------------------------------\n// In-memory mock inbox with best-effort localStorage persistence, so a browser\n// reload keeps whatever the demo created/sent within the session. The store is\n// namespaced per tenant; SSR / no-window environments degrade to pure memory.\n// ---------------------------------------------------------------------------\nexport function createMockConversationsProvider(\n config?: MockConversationsConfig,\n): ConversationsProvider {\n const selfAuthor = config?.selfAuthor ?? 'You'\n\n function resolveTenant(): string {\n const raw = typeof config?.tenantId === 'function' ? config.tenantId() : config?.tenantId\n return raw || 'default'\n }\n\n const storageKey = () => `saas:mock:conversations:${resolveTenant()}`\n\n function hasStorage(): boolean {\n try {\n return typeof window !== 'undefined' && !!window.localStorage\n } catch {\n return false\n }\n }\n\n function load(): Snapshot {\n if (hasStorage()) {\n try {\n const raw = window.localStorage.getItem(storageKey())\n if (raw) {\n const parsed = JSON.parse(raw) as Snapshot\n if (Array.isArray(parsed.conversations) && Array.isArray(parsed.messages)) {\n return {\n conversations: parsed.conversations,\n messages: parsed.messages,\n counter: parsed.counter ?? 100,\n }\n }\n }\n } catch {\n // Corrupt snapshot — fall through to a fresh seed.\n }\n }\n const seeded = seed()\n return { conversations: seeded.conversations, messages: seeded.messages, counter: 100 }\n }\n\n const state = load()\n\n function persist(): void {\n if (!hasStorage()) return\n try {\n window.localStorage.setItem(\n storageKey(),\n JSON.stringify({\n conversations: state.conversations,\n messages: state.messages,\n counter: state.counter,\n } satisfies Snapshot),\n )\n } catch {\n // Quota / privacy mode — keep working in memory only.\n }\n }\n\n // Persist the initial seed so a first reload is already stable.\n persist()\n\n return {\n async listConversations(query?: ListConversationsQuery): Promise<Conversation[]> {\n let list = [...state.conversations]\n if (query?.channel && query.channel !== 'all') list = list.filter((c) => c.channel === query.channel)\n if (query?.status && query.status !== 'all') list = list.filter((c) => c.status === query.status)\n if (query?.search) {\n const q = query.search.toLowerCase()\n list = list.filter(\n (c) => c.contactName.toLowerCase().includes(q) || c.lastMessagePreview.toLowerCase().includes(q),\n )\n }\n return list.sort((a, b) => b.lastMessageAt.localeCompare(a.lastMessageAt))\n },\n\n async getMessages(conversationId: string): Promise<Message[]> {\n return state.messages\n .filter((m) => m.conversationId === conversationId)\n .sort((a, b) => a.at.localeCompare(b.at))\n },\n\n async createConversation(input: CreateConversationInput): Promise<Conversation> {\n const now = new Date().toISOString()\n const firstMessage = input.firstMessage?.trim()\n const id = `c${++state.counter}`\n const conversation: Conversation = {\n id,\n contactName: input.contactName.trim(),\n contactPersonId: input.contactPersonId,\n contactHandle: input.contactHandle?.trim() ?? '',\n channel: input.channel,\n lastMessagePreview: firstMessage ?? '',\n lastMessageAt: now,\n // A thread the house opened is not waiting on the house. Left undefined\n // when there is no first message: nobody has spoken, so there is no\n // last speaker to name, and `isWaitingOnUs` falls back to unread (0).\n lastMessageDirection: firstMessage ? 'outbound' : undefined,\n unreadCount: 0,\n status: 'open',\n assignedTo: selfAuthor,\n accent: CHANNEL_ACCENT_HEX[input.channel],\n tags: [],\n note: input.note?.trim() || undefined,\n }\n state.conversations.unshift(conversation)\n if (firstMessage) {\n state.messages.push({\n id: `m${++state.counter}`,\n conversationId: id,\n channel: input.channel,\n direction: 'outbound',\n body: firstMessage,\n author: selfAuthor,\n at: now,\n })\n }\n persist()\n return conversation\n },\n\n async sendMessage(input: SendMessageInput): Promise<Message> {\n const conv = state.conversations.find((c) => c.id === input.conversationId)\n const created: Message = {\n id: `m${++state.counter}`,\n conversationId: input.conversationId,\n channel: conv?.channel ?? 'sms',\n direction: 'outbound',\n body: input.body,\n author: selfAuthor,\n at: new Date().toISOString(),\n }\n state.messages.push(created)\n if (conv) {\n conv.lastMessagePreview = input.body\n conv.lastMessageAt = created.at\n conv.lastMessageDirection = 'outbound'\n conv.unreadCount = 0\n if (conv.status === 'closed') conv.status = 'open'\n }\n persist()\n return created\n },\n\n async markRead(conversationId: string): Promise<void> {\n const conv = state.conversations.find((c) => c.id === conversationId)\n if (conv && conv.unreadCount !== 0) {\n conv.unreadCount = 0\n persist()\n }\n },\n\n async setStatus(conversationId: string, status: ConversationStatus): Promise<Conversation> {\n const conv = state.conversations.find((c) => c.id === conversationId)\n if (!conv) throw new Error('Conversation not found')\n conv.status = status\n persist()\n return conv\n },\n }\n}\n","// Physical table names for the conversations plugin (plg_conversations* prefix).\n// Import T and reference T.<key> in the data provider so a rename lands in\n// exactly one place.\nexport const T = {\n conversations: 'plg_conversations',\n messages: 'plg_conversation_messages',\n channels: 'plg_conversations_channels',\n} as const\n","import { getSupabaseClientOptional } from '@fayz-ai/core'\nimport type { ConversationsProvider } from './types'\nimport { T } from './tables'\nimport { CHANNEL_ACCENT_HEX } from './accents'\nimport type {\n Conversation,\n Message,\n ListConversationsQuery,\n SendMessageInput,\n CreateConversationInput,\n ConversationStatus,\n MessageDirection,\n} from '../types'\n\n// ---------------------------------------------------------------------------\n// Supabase-backed conversations provider — reads/writes the project's\n// `conversations` + `conversation_messages` tables (tenant-scoped via RLS +\n// an explicit tenant filter from the active-org context). Mirrors the inline\n// client-casting style of @fayz-ai/core's createSupabaseProvider; the global\n// client is `unknown`-typed so each call narrows the shape it needs.\n//\n// Real channel connectors (Twilio, WhatsApp Cloud, Meta, IMAP) deliver inbound\n// rows into these tables out-of-band; this provider is the read/compose surface.\n// ---------------------------------------------------------------------------\n\nexport interface SupabaseConversationsConfig {\n /** Supabase client; defaults to the global one registered by createFayzApp. */\n supabaseClient?: unknown\n /** Active tenant id (value or getter) used to scope reads and stamp writes. */\n tenantId?: string | (() => string | undefined)\n /** Display name stamped as the author of outbound messages. */\n selfAuthor?: string\n}\n\ntype Row = Record<string, unknown>\n\nfunction mapConversation(r: Row): Conversation {\n return {\n id: String(r.id),\n contactName: (r.contact_name as string) ?? '',\n contactPersonId: (r.contact_person_id as string | null) ?? undefined,\n contactHandle: (r.contact_handle as string) ?? '',\n channel: (r.channel as Conversation['channel']) ?? 'sms',\n lastMessagePreview: (r.last_message_preview as string) ?? '',\n lastMessageAt: (r.last_message_at as string) ?? '',\n // Kept current by the trigger in migration 001, never written from here:\n // the column has one writer on purpose (last_message_preview/at have five,\n // which is the mistake this one was not going to repeat).\n lastMessageDirection: (r.last_message_direction as MessageDirection | null) ?? undefined,\n unreadCount: Number(r.unread_count ?? 0),\n status: (r.status as ConversationStatus) ?? 'open',\n assignedTo: (r.assigned_to as string | null) ?? undefined,\n accent: (r.accent as string) ?? '#6366f1',\n tags: (r.tags as string[] | null) ?? [],\n location: (r.location as string | null) ?? undefined,\n note: (r.note as string | null) ?? undefined,\n }\n}\n\nfunction mapMessage(r: Row): Message {\n return {\n id: String(r.id),\n conversationId: String(r.conversation_id),\n channel: (r.channel as Message['channel']) ?? 'sms',\n direction: (r.direction as Message['direction']) ?? 'inbound',\n body: (r.body as string) ?? '',\n at: (r.at as string) ?? '',\n author: (r.author as string) ?? '',\n deliveryStatus: (r.delivery_status as string) ?? null,\n providerMessageId: (r.provider_message_id as string) ?? null,\n }\n}\n\nexport function createSupabaseConversationsProvider(\n config?: SupabaseConversationsConfig,\n): ConversationsProvider {\n /** O que o provedor chama de status, no vocabulário que a nossa coluna aceita.\n * Um valor desconhecido vira `sent`: a mensagem saiu, e inventar um estado\n * que o CHECK recusa transforma um envio bem-sucedido num erro. */\nconst DELIVERY = new Set(['queued','sent','delivered','read','failed','expired','delivery_timeout','cancelled','opted_out'])\nfunction mapDeliveryStatus(raw: unknown): string {\n const v = String(raw ?? '').toLowerCase()\n if (v === 'accepted' || v === 'received') return 'sent'\n return DELIVERY.has(v) ? v : 'sent'\n}\n\nconst selfAuthor = config?.selfAuthor ?? 'You'\n\n function resolveTenantId(): string | undefined {\n if (!config?.tenantId) return undefined\n return typeof config.tenantId === 'function' ? config.tenantId() : config.tenantId\n }\n\n function client(): { from: (t: string) => Row } {\n const supabase = (config?.supabaseClient ?? getSupabaseClientOptional()) as\n | { from: (t: string) => Row }\n | null\n if (!supabase) {\n throw new Error(\n '[plugin-conversations] Supabase client not available. Pass supabaseClient or register the global client via createFayzApp.',\n )\n }\n return supabase\n }\n\n return {\n async listConversations(query?: ListConversationsQuery): Promise<Conversation[]> {\n let q = (client().from(T.conversations) as { select: (s: string) => Row }).select('*')\n\n const tenantId = resolveTenantId()\n if (tenantId) q = (q as { eq: (c: string, v: string) => Row }).eq('tenant_id', tenantId)\n if (query?.channel && query.channel !== 'all') {\n q = (q as { eq: (c: string, v: string) => Row }).eq('channel', query.channel)\n }\n if (query?.status && query.status !== 'all') {\n q = (q as { eq: (c: string, v: string) => Row }).eq('status', query.status)\n }\n if (query?.search) {\n const term = `%${query.search}%`\n q = (q as { or: (c: string) => Row }).or(\n `contact_name.ilike.${term},last_message_preview.ilike.${term}`,\n )\n }\n q = (q as { order: (c: string, o: unknown) => Row }).order('last_message_at', {\n ascending: false,\n })\n\n const { data, error } = (await q) as { data: Row[] | null; error: unknown }\n if (error) throw error\n return (data ?? []).map(mapConversation)\n },\n\n async getMessages(conversationId: string): Promise<Message[]> {\n const selected = (\n client().from(T.messages) as { select: (s: string) => Row }\n ).select('*')\n const filtered = (selected as { eq: (c: string, v: string) => Row }).eq(\n 'conversation_id',\n conversationId,\n )\n const ordered = (filtered as { order: (c: string, o: unknown) => Row }).order('at', {\n ascending: true,\n })\n const { data, error } = (await ordered) as { data: Row[] | null; error: unknown }\n if (error) throw error\n return (data ?? []).map(mapMessage)\n },\n\n async createConversation(input: CreateConversationInput): Promise<Conversation> {\n const tenantId = resolveTenantId()\n // Insert with a null tenant_id fails the plg_conversations RLS WITH CHECK\n // (tenant_id ∈ user_tenant_ids()) with an opaque Postgres error. When the\n // active tenant hasn't resolved yet (bootstrap race), fail fast with an\n // actionable message the compose modal can surface via toast.\n if (!tenantId) {\n throw new Error('[plugin-conversations] Active tenant not resolved — cannot create conversation. Try again in a moment.')\n }\n const now = new Date().toISOString()\n const firstMessage = input.firstMessage?.trim()\n\n const convRow: Row = {\n contact_name: input.contactName.trim(),\n // Only written when the picker resolved a real person — an app that\n // hasn't run migration 002 yet would reject an unknown column, and\n // omitting it keeps the free-text path working there.\n ...(input.contactPersonId ? { contact_person_id: input.contactPersonId } : {}),\n contact_handle: input.contactHandle?.trim() || null,\n channel: input.channel,\n last_message_preview: firstMessage || null,\n last_message_at: now,\n unread_count: 0,\n status: 'open',\n assigned_to: selfAuthor,\n accent: CHANNEL_ACCENT_HEX[input.channel],\n tags: [],\n note: input.note?.trim() || null,\n }\n if (tenantId) convRow.tenant_id = tenantId\n\n const { data: created, error } = (await (\n (client().from(T.conversations) as { insert: (r: Row) => Row }).insert(convRow) as {\n select: () => { single: () => Promise<{ data: Row | null; error: unknown }> }\n }\n )\n .select()\n .single()) as { data: Row | null; error: unknown }\n if (error) throw error\n if (!created) throw new Error('Conversation not created')\n\n // Seed the thread with the first outbound message when provided.\n if (firstMessage) {\n const msgRow: Row = {\n conversation_id: String(created.id),\n channel: input.channel,\n direction: 'outbound',\n body: firstMessage,\n author: selfAuthor,\n at: now,\n }\n if (tenantId) msgRow.tenant_id = tenantId\n await (client().from(T.messages) as { insert: (r: Row) => Promise<unknown> }).insert(msgRow)\n }\n\n return mapConversation(created)\n },\n\n async sendMessage(input: SendMessageInput): Promise<Message> {\n\n const tenantId = resolveTenantId()\n\n // Resolve the channel from the parent conversation so the message matches.\n const convSelected = (client().from(T.conversations) as { select: (s: string) => Row }).select(\n 'channel, contact_handle',\n )\n const convFiltered = (convSelected as { eq: (c: string, v: string) => Row }).eq(\n 'id',\n input.conversationId,\n )\n const { data: conv } = (await (\n convFiltered as { maybeSingle: () => Promise<{ data: Row | null }> }\n ).maybeSingle()) as { data: Row | null }\n const channel = (conv?.channel as Message['channel']) ?? 'sms'\n\n const at = new Date().toISOString()\n const row: Row = {\n conversation_id: input.conversationId,\n channel,\n direction: 'outbound',\n body: input.body,\n author: selfAuthor,\n at,\n }\n if (tenantId) row.tenant_id = tenantId\n\n const { data: created, error } = (await (\n (client().from(T.messages) as { insert: (r: Row) => Row }).insert(row) as {\n select: () => { single: () => Promise<{ data: Row | null; error: unknown }> }\n }\n )\n .select()\n .single()) as { data: Row | null; error: unknown }\n if (error) throw error\n\n // E AGORA A MENSAGEM SAI DE VERDADE.\n //\n // Até aqui `sendMessage` gravava a linha e terminava. A caixa mostrava a\n // mensagem enviada, o destinatário nunca recebia nada, e não havia erro\n // em lugar nenhum — duas mensagens ficaram meses paradas nesta tabela\n // antes de alguém perguntar por quê.\n //\n // O envio é DEPOIS da gravação, de propósito: se a entrega falhar, a\n // linha continua existindo com a falha registrada. O contrário — mandar\n // e só então gravar — perde a mensagem quando a gravação falha, e aí\n // ninguém sabe o que foi dito.\n //\n // A falha não é silenciosa: ela sobe. Quem chamou decide o que mostrar.\n // O que o envio devolveu, para o objeto RETORNADO carregar. A primeira\n // versão devolvia a linha capturada ANTES do envio: sem id do provedor e\n // sem status. A caixa então desenhava vermelho — \"sem id, não saiu\" — uma\n // mensagem que tinha acabado de ser entregue. O banco estava certo e a\n // tela mentia até o próximo recarregamento.\n let sentId: string | null = null\n let sentStatus: string | null = null\n\n if (channel === 'whatsapp') {\n const handle = String((conv as Row | null)?.contact_handle ?? '')\n try {\n const { data: out, error: sendErr } = await (client() as any).functions.invoke(\n 'conversations-provider',\n { body: { tenantId, action: 'send', to: handle, text: input.body } },\n )\n if (sendErr) {\n const detail = (await sendErr.context?.json?.().catch(() => null)) as { message?: string } | null\n throw new Error(detail?.message ?? sendErr.message)\n }\n if ((out as any)?.error) throw new Error((out as any).message ?? (out as any).error)\n sentId = (out as any)?.providerMessageId ?? null\n // `queued` é uma resposta, não uma falha: o provedor desta casa só é\n // alcançável da máquina dela, e a nuvem deixou a linha para o\n // disparador local. O balão fica cinza com o relógio até ele passar.\n sentStatus = (out as any)?.queued ? 'queued' : mapDeliveryStatus((out as any)?.status)\n if (created?.id) {\n await ((client().from(T.messages) as { update: (r: Row) => Row }).update({\n provider_message_id: sentId,\n // O provedor fala o dialeto dele. A Tyxter devolve `accepted`,\n // que NÃO existe no CHECK desta coluna — o update explodia, caía\n // no catch, e a mensagem era marcada como falha mesmo tendo\n // saído. Foi assim que \"opa\" apareceu verde e contou como erro.\n delivery_status: sentStatus,\n }) as { eq: (c: string, v: string) => Promise<unknown> }).eq('id', String(created.id))\n }\n } catch (err) {\n if (created?.id) {\n await ((client().from(T.messages) as { update: (r: Row) => Row }).update({\n delivery_status: 'failed',\n }) as { eq: (c: string, v: string) => Promise<unknown> }).eq('id', String(created.id))\n }\n throw err\n }\n }\n\n // Roll the parent conversation forward (preview / timestamp / unread / reopen).\n await (\n (client().from(T.conversations) as {\n update: (r: Row) => Row\n }).update({\n last_message_preview: input.body,\n last_message_at: at,\n unread_count: 0,\n status: 'open',\n }) as { eq: (c: string, v: string) => Promise<unknown> }\n ).eq('id', input.conversationId)\n\n return {\n ...mapMessage(created ?? row),\n providerMessageId: sentId,\n deliveryStatus: sentStatus,\n }\n },\n\n async markRead(conversationId: string): Promise<void> {\n const { error } = (await (\n (client().from(T.conversations) as { update: (r: Row) => Row }).update({\n unread_count: 0,\n }) as { eq: (c: string, v: string) => Promise<{ error: unknown }> }\n ).eq('id', conversationId)) as { error: unknown }\n if (error) throw error\n },\n\n async setStatus(conversationId: string, status: ConversationStatus): Promise<Conversation> {\n const updated = (client().from(T.conversations) as { update: (r: Row) => Row }).update({\n status,\n })\n const filtered = (updated as { eq: (c: string, v: string) => Row }).eq('id', conversationId)\n const selected = (filtered as { select: () => Row }).select()\n const { data, error } = (await (\n selected as { single: () => Promise<{ data: Row | null; error: unknown }> }\n ).single()) as { data: Row | null; error: unknown }\n if (error) throw error\n if (!data) throw new Error('Conversation not found')\n return mapConversation(data)\n },\n }\n}\n","import { createStore, type StoreApi } from 'zustand/vanilla'\nimport type { ConversationsProvider } from './data/types'\nimport type {\n Conversation,\n Message,\n Channel,\n ConversationStatus,\n CreateConversationInput,\n} from './types'\n\n/** Quem, por onde, e como chamá-lo. Vem de outro módulo — hoje a ficha do lead\n * no CRM — que sabe a pessoa e não sabe nada sobre conversas. */\nexport interface OpenForPersonInput {\n /** `public.people` id. É por ele que a conversa é reconhecida; o telefone é\n * o desempate para threads antigas, criadas antes de haver vínculo. */\n personId?: string\n name: string\n handle: string\n channel: Channel\n}\n\n/** Só os dígitos. `+55 11 99999-0000` e `5511999990000` são o mesmo telefone, e\n * comparar como texto cru abriria uma segunda thread para a mesma pessoa. */\nfunction handleKey(h: string | undefined | null): string {\n const v = (h ?? '').trim()\n if (!v) return ''\n return /[@a-z]/i.test(v) ? v.toLowerCase() : v.replace(/\\D/g, '')\n}\n\nexport interface ConversationsUIState {\n conversations: Conversation[]\n messages: Message[]\n selectedId: string | null\n refreshMessages: () => Promise<void>\n channelFilter: Channel | 'all'\n search: string\n loading: boolean\n sending: boolean\n\n load(): Promise<void>\n select(id: string): Promise<void>\n deselect(): void\n setChannelFilter(channel: Channel | 'all'): Promise<void>\n setSearch(search: string): Promise<void>\n create(input: CreateConversationInput): Promise<Conversation>\n /** Abrir a conversa DESTA pessoa neste canal, criando-a se ainda não houver. */\n openForPerson(input: OpenForPersonInput): Promise<Conversation>\n send(body: string): Promise<void>\n setStatus(status: ConversationStatus): Promise<void>\n}\n\nexport function createConversationsStore(\n provider: ConversationsProvider,\n): StoreApi<ConversationsUIState> {\n return createStore<ConversationsUIState>((set, get) => ({\n conversations: [],\n messages: [],\n selectedId: null,\n channelFilter: 'all',\n search: '',\n loading: false,\n sending: false,\n\n async load() {\n set({ loading: true })\n const conversations = await provider.listConversations({\n channel: get().channelFilter,\n search: get().search || undefined,\n })\n const selectedId = get().selectedId ?? conversations[0]?.id ?? null\n set({ conversations, loading: false, selectedId })\n if (selectedId) await get().select(selectedId)\n },\n\n async select(id: string) {\n set({ selectedId: id })\n const messages = await provider.getMessages(id)\n set({ messages })\n await provider.markRead(id)\n set((s) => ({\n conversations: s.conversations.map((c) => (c.id === id ? { ...c, unreadCount: 0 } : c)),\n }))\n },\n\n /** Relê as mensagens da conversa aberta, sem tocar em mais nada.\n *\n * O status de uma mensagem de WhatsApp muda DEPOIS do envio — entregue,\n * lida, às vezes minutos depois — e quem avisa é o webhook, que escreve no\n * banco sem a tela saber. Sem isto a caixa mostra para sempre o status do\n * instante do envio: um tique só, numa mensagem que já foi lida.\n *\n * Não usa `select()` de propósito: aquele marca a conversa como lida e\n * zera o contador, e uma releitura de fundo não é um gesto de ninguém. */\n async refreshMessages() {\n const id = get().selectedId\n if (!id) return\n try {\n const messages = await provider.getMessages(id)\n // Só escreve se mudou algo. `set` a cada dez segundos redesenharia a\n // thread inteira e roubaria a seleção de texto de quem está lendo.\n const before = get().messages\n const same = before.length === messages.length &&\n before.every((m, i) => m.id === messages[i].id && m.deliveryStatus === messages[i].deliveryStatus)\n if (!same) set({ messages })\n } catch { /* a caixa continua com o que tem; a próxima volta tenta de novo */ }\n },\n\n deselect() {\n set({ selectedId: null, messages: [] })\n },\n\n async setChannelFilter(channel) {\n set({ channelFilter: channel })\n await get().load()\n },\n\n async setSearch(search) {\n set({ search })\n await get().load()\n },\n\n async create(input) {\n const created = await provider.createConversation(input)\n // The thread EXISTS the moment the insert returns, so resolve on that and\n // show it optimistically. Clear filters that would hide it, prepend it,\n // select it — all local.\n set((s) => ({\n channelFilter: 'all',\n search: '',\n conversations: [created, ...s.conversations.filter((c) => c.id !== created.id)],\n selectedId: created.id,\n }))\n\n // Reconciliation (authoritative list + the thread's messages) runs in the\n // BACKGROUND. It used to be awaited here, which made the caller — the\n // compose modal — hostage to two extra round-trips: on a slow pool the\n // conversation was already in the database while the modal sat open\n // looking broken, inviting a second click and a duplicate thread.\n // Failures are non-fatal: the optimistic row above already renders.\n void (async () => {\n try {\n const conversations = await provider.listConversations({})\n // Read-after-write guard: a refetch that races the insert may not\n // return the new row yet — keep the local one rather than dropping it.\n const merged = conversations.some((c) => c.id === created.id)\n ? conversations\n : [created, ...conversations]\n set({ conversations: merged })\n await get().select(created.id)\n } catch {\n /* optimistic state stands; the next load() reconciles */\n }\n })()\n\n return created\n },\n\n // Chegar aqui vindo de fora — da ficha de um lead, por exemplo — não pode\n // criar uma segunda thread com alguém com quem a casa já conversa. Procura\n // primeiro pela PESSOA, e só então pelo telefone: o vínculo é a verdade, e\n // o handle é o que resgata threads criadas antes de o vínculo existir.\n async openForPerson({ personId, name, handle, channel }) {\n // Uma caixa ainda não carregada não tem como saber que a thread existe.\n if (get().conversations.length === 0 && !get().loading) {\n try { await get().load() } catch { /* segue para a criação */ }\n }\n const key = handleKey(handle)\n const existing = get().conversations.find((c) =>\n c.channel === channel &&\n ((personId && c.contactPersonId === personId) ||\n (key !== '' && handleKey(c.contactHandle) === key)))\n\n if (existing) {\n // Limpa o que esconderia a thread: chegar por link e cair numa lista\n // filtrada é indistinguível de a conversa não existir.\n set({ channelFilter: 'all', search: '' })\n await get().select(existing.id)\n return existing\n }\n\n return get().create({\n contactName: name,\n contactPersonId: personId,\n contactHandle: handle,\n channel,\n })\n },\n\n async send(body: string) {\n const id = get().selectedId\n if (!id || !body.trim()) return\n set({ sending: true })\n const created = await provider.sendMessage({ conversationId: id, body: body.trim() })\n set((s) => ({\n sending: false,\n messages: [...s.messages, created],\n conversations: s.conversations.map((c) =>\n // `lastMessageDirection` acompanha a prévia, senão a fila mente. O\n // gatilho da migration 001 carimba a coluna no banco, mas a lista só\n // volta a ler no próximo `load()` — e até lá a thread que ACABOU de\n // ser respondida continua em \"esperando nós\", que é exatamente a\n // pergunta que essa coluna existe para responder.\n //\n // Antes da 001 isto não aparecia: a coluna vinha sempre indefinida e\n // `isWaitingOnUs` caía no `unreadCount`. A migration não criou o\n // defeito, tornou-o visível.\n c.id === id\n ? { ...c, lastMessagePreview: created.body, lastMessageAt: created.at, lastMessageDirection: 'outbound' as const }\n : c,\n ),\n }))\n },\n\n async setStatus(status) {\n const id = get().selectedId\n if (!id) return\n const updated = await provider.setStatus(id, status)\n set((s) => ({\n conversations: s.conversations.map((c) => (c.id === id ? updated : c)),\n }))\n },\n }))\n}\n","export const en: Record<string, string> = {\n 'conversations.title': 'Conversations',\n 'conversations.subtitle': 'Unified inbox across every channel',\n\n // Conversation list\n 'conversations.list.search': 'Search conversations',\n 'conversations.list.loading': 'Loading…',\n 'conversations.list.empty': 'No conversations',\n 'conversations.list.new': 'New conversation',\n\n // Channel filters\n 'conversations.filter.all': 'All',\n 'conversations.filter.whatsapp': 'WhatsApp',\n 'conversations.filter.sms': 'SMS',\n 'conversations.filter.instagram': 'Instagram',\n 'conversations.filter.email': 'Email',\n 'conversations.filter.webchat': 'Web',\n\n // Status labels\n 'conversations.status.open': 'Open',\n 'conversations.status.snoozed': 'Snoozed',\n 'conversations.status.closed': 'Closed',\n\n // Empty state\n 'conversations.empty.select': 'Select a conversation to start chatting',\n\n // Thread\n 'conversations.thread.back': 'Back to conversations',\n 'conversations.thread.snooze': 'Snooze',\n 'conversations.thread.close': 'Close',\n 'conversations.thread.details': 'Toggle contact details',\n 'conversations.thread.empty': 'No messages yet',\n 'conversations.thread.reply': 'Reply via {{channel}}…',\n 'conversations.thread.send': 'Send',\n\n // Contact panel\n 'conversations.contact.details': 'Details',\n 'conversations.contact.closeDetails': 'Close details',\n 'conversations.contact.channel': 'Channel',\n 'conversations.contact.status': 'Status',\n 'conversations.contact.assignedTo': 'Assigned to',\n 'conversations.contact.location': 'Location',\n 'conversations.contact.tags': 'Tags',\n 'conversations.contact.note': 'Note',\n 'conversations.contact.linkedRecords': 'Linked records',\n 'conversations.contact.noLinkedRecords': 'No linked records yet.',\n\n // New-conversation modal\n 'conversations.new.title': 'New conversation',\n 'conversations.new.channel': 'Channel',\n 'conversations.new.contactName': 'Contact name',\n 'conversations.new.contactNamePlaceholder': 'e.g. Jane Doe',\n 'conversations.new.handle': 'Phone / handle / email',\n 'conversations.new.handlePlaceholder': '+1 555 000 0000',\n // The handle field only surfaces when the picked contact has nothing usable\n // for the active channel — otherwise it is derived and shown on the chip.\n 'conversations.new.addHandle': 'Add {label}',\n 'conversations.new.handleLabel.phone': 'phone',\n 'conversations.new.handleLabel.email': 'email',\n 'conversations.new.handleLabel.instagram': 'Instagram handle',\n 'conversations.new.handleLabel.webchat': 'web chat id',\n 'conversations.new.firstMessage': 'First message',\n 'conversations.new.firstMessagePlaceholder': 'Write the first message (optional)…',\n 'conversations.new.cancel': 'Cancel',\n 'conversations.new.create': 'Start conversation',\n 'conversations.new.creating': 'Starting…',\n 'conversations.new.createFailed': 'Could not create the conversation',\n\n // Dashboard — the queue of people waiting on a reply\n 'conversations.dashboard.waiting': 'Waiting on you',\n 'conversations.dashboard.waitingOldest': 'Oldest waiting {{time}}',\n 'conversations.dashboard.waitingNone': 'Nobody waiting',\n 'conversations.dashboard.openThreads': 'Open conversations',\n 'conversations.dashboard.openThreadsSub': '{{count}} waiting on a reply',\n 'conversations.dashboard.needsYou': 'Needs you',\n 'conversations.dashboard.needsYouEmpty': 'Nobody waiting. Inbox is clear.',\n 'conversations.dashboard.who': 'Who',\n 'conversations.dashboard.waited': 'Waiting',\n 'conversations.waited.minutes': '{{count}} min',\n 'conversations.waited.hours': '{{count}} h',\n 'conversations.waited.days': '{{count}} d',\n\n // Inbox settings\n 'conversations.settings.title': 'Conversations',\n 'conversations.settings.channels': 'Your numbers',\n 'conversations.settings.channelsHelp': 'Where messages come in and go out. Connect and manage channels in the Integrations tab.',\n 'conversations.settings.channelsEmpty': 'No channel connected yet — until then the inbox stays empty. Connect WhatsApp in the Integrations tab.',\n 'conversations.settings.channelsUnavailable': 'Could not load your channels right now.',\n 'conversations.settings.channelDedicated': 'Your own number',\n 'conversations.settings.channelShared': 'Platform number',\n 'conversations.settings.status.active': 'Active',\n 'conversations.settings.status.requested': 'Requested',\n 'conversations.settings.status.provisioning': 'Provisioning',\n 'conversations.settings.status.provisioned': 'Provisioned',\n 'conversations.settings.status.verifying': 'Verifying',\n 'conversations.settings.status.failed': 'Failed',\n 'conversations.settings.status.released': 'Released',\n 'conversations.settings.status.disconnected': 'Disconnected',\n}\n","export const ptBR: Record<string, string> = {\n 'conversations.title': 'Conversas',\n 'conversations.subtitle': 'Caixa de entrada unificada de todos os canais',\n\n // Lista de conversas\n 'conversations.list.search': 'Buscar conversas',\n 'conversations.list.loading': 'Carregando…',\n 'conversations.list.empty': 'Nenhuma conversa',\n 'conversations.list.new': 'Nova conversa',\n\n // Filtros de canal\n 'conversations.filter.all': 'Todas',\n 'conversations.filter.whatsapp': 'WhatsApp',\n 'conversations.filter.sms': 'SMS',\n 'conversations.filter.instagram': 'Instagram',\n 'conversations.filter.email': 'E-mail',\n 'conversations.filter.webchat': 'Web',\n\n // Rótulos de status\n 'conversations.status.open': 'Aberta',\n 'conversations.status.snoozed': 'Adiada',\n 'conversations.status.closed': 'Encerrada',\n\n // Estado vazio\n 'conversations.empty.select': 'Selecione uma conversa para começar a conversar',\n\n // Thread\n 'conversations.thread.back': 'Voltar às conversas',\n 'conversations.thread.snooze': 'Adiar',\n 'conversations.thread.close': 'Encerrar',\n 'conversations.thread.details': 'Alternar detalhes do contato',\n 'conversations.thread.empty': 'Nenhuma mensagem ainda',\n 'conversations.thread.reply': 'Responder via {{channel}}…',\n 'conversations.thread.send': 'Enviar',\n\n // Painel do contato\n 'conversations.contact.details': 'Detalhes',\n 'conversations.contact.closeDetails': 'Fechar detalhes',\n 'conversations.contact.channel': 'Canal',\n 'conversations.contact.status': 'Status',\n 'conversations.contact.assignedTo': 'Responsável',\n 'conversations.contact.location': 'Localização',\n 'conversations.contact.tags': 'Etiquetas',\n 'conversations.contact.note': 'Nota',\n 'conversations.contact.linkedRecords': 'Registros vinculados',\n 'conversations.contact.noLinkedRecords': 'Nenhum registro vinculado ainda.',\n\n // Modal de nova conversa\n 'conversations.new.title': 'Nova conversa',\n 'conversations.new.channel': 'Canal',\n 'conversations.new.contactName': 'Nome do contato',\n 'conversations.new.contactNamePlaceholder': 'ex.: Maria Silva',\n 'conversations.new.handle': 'Telefone / usuário / e-mail',\n 'conversations.new.handlePlaceholder': '+55 11 99999-0000',\n // O campo de handle só aparece quando o contato escolhido não tem o dado do\n // canal ativo — caso contrário ele é derivado e mostrado no chip.\n 'conversations.new.addHandle': 'Adicionar {label}',\n 'conversations.new.handleLabel.phone': 'telefone',\n 'conversations.new.handleLabel.email': 'e-mail',\n 'conversations.new.handleLabel.instagram': '@ do Instagram',\n 'conversations.new.handleLabel.webchat': 'id do chat',\n 'conversations.new.firstMessage': 'Primeira mensagem',\n 'conversations.new.firstMessagePlaceholder': 'Escreva a primeira mensagem (opcional)…',\n 'conversations.new.cancel': 'Cancelar',\n 'conversations.new.create': 'Iniciar conversa',\n 'conversations.new.creating': 'Iniciando…',\n 'conversations.new.createFailed': 'Não foi possível criar a conversa',\n\n // Painel — a fila de quem está esperando resposta\n 'conversations.dashboard.waiting': 'Esperando resposta',\n 'conversations.dashboard.waitingOldest': 'A mais antiga há {{time}}',\n 'conversations.dashboard.waitingNone': 'Ninguém na fila',\n 'conversations.dashboard.openThreads': 'Conversas abertas',\n 'conversations.dashboard.openThreadsSub': '{{count}} esperando resposta',\n 'conversations.dashboard.needsYou': 'Precisa de você',\n 'conversations.dashboard.needsYouEmpty': 'Ninguém esperando. Caixa de entrada em dia.',\n 'conversations.dashboard.who': 'Quem',\n 'conversations.dashboard.waited': 'Esperando há',\n 'conversations.waited.minutes': '{{count}} min',\n 'conversations.waited.hours': '{{count}} h',\n 'conversations.waited.days': '{{count}} d',\n\n // Configurações da caixa de entrada\n 'conversations.settings.title': 'Conversas',\n 'conversations.settings.channels': 'Seus números',\n 'conversations.settings.channelsHelp': 'Por onde a mensagem entra e sai. Ligue e gerencie os canais na aba Integrações.',\n 'conversations.settings.channelsEmpty': 'Nenhum canal ligado ainda — enquanto isso, a caixa de entrada fica vazia. Ligue o WhatsApp na aba Integrações.',\n 'conversations.settings.channelsUnavailable': 'Não foi possível consultar seus canais agora.',\n 'conversations.settings.channelDedicated': 'Número só seu',\n 'conversations.settings.channelShared': 'Número da plataforma',\n 'conversations.settings.status.active': 'Ativo',\n 'conversations.settings.status.requested': 'Solicitado',\n 'conversations.settings.status.provisioning': 'Provisionando',\n 'conversations.settings.status.provisioned': 'Provisionado',\n 'conversations.settings.status.verifying': 'Verificando',\n 'conversations.settings.status.failed': 'Falhou',\n 'conversations.settings.status.released': 'Devolvido',\n 'conversations.settings.status.disconnected': 'Desconectado',\n}\n","import { en } from './en'\nimport { ptBR } from './pt-BR'\n\nexport const conversationsLocales: Record<string, Record<string, string>> = {\n en,\n 'pt-BR': ptBR,\n}\n","import React, { useEffect, useMemo } from 'react'\nimport type { StoreApi } from 'zustand'\nimport type { DashboardWidgetDef } from '@fayz-ai/core'\nimport { useTranslation } from '@fayz-ai/core'\nimport {\n KpiCard, TableWidget, defineKpiWidget, defineTableWidget,\n type TableWidgetProps,\n} from '@fayz-ai/ui'\nimport {\n ConversationsContextProvider,\n useConversationsStore,\n type ResolvedConversationsConfig,\n} from '../context'\nimport type { ConversationsUIState } from '../store'\nimport { isWaitingOnUs, type Conversation } from '../types'\n\n// ---------------------------------------------------------------------------\n// Inbox widgets for the dashboard registry.\n//\n// This plugin registered nothing here until now, which meant the one number a\n// front desk actually opens the day for — \"who is waiting on me\" — existed on\n// no surface but the inbox itself. A dashboard that shows revenue and pipeline\n// while somebody has been waiting since Tuesday is reporting on the past.\n//\n// ── What \"waiting\" means ──────────────────────────────────────────────────\n//\n// `isWaitingOnUs` (see ../types), and not a rule spelled out again here: the\n// KPI, the table and anything that later filters the inbox have to agree, and a\n// dashboard that says three while the list shows one is worse than neither.\n//\n// It reads `lastMessageDirection`, which migration 001 added and a trigger\n// keeps current. The first pass of this file used `unreadCount > 0` because the\n// column did not exist — an approximation that missed the case that hurts\n// most: a thread somebody opened, read, meant to answer later and forgot. Zero\n// unread, customer still waiting. The fallback is still there for threads that\n// genuinely do not know their direction.\n// ---------------------------------------------------------------------------\n\n/** The store dedups the fetch, so every widget may ensure its own data. */\nfunction useEnsureConversations() {\n const load = useConversationsStore((s) => s.load)\n const loaded = useConversationsStore((s) => s.conversations.length > 0)\n useEffect(() => { if (!loaded) void load() }, [])\n}\n\nfunction useWaiting(): { waiting: Conversation[]; open: number; loading: boolean } {\n useEnsureConversations()\n const conversations = useConversationsStore((s) => s.conversations)\n const loading = useConversationsStore((s) => s.loading)\n return useMemo(() => {\n const open = conversations.filter((c) => c.status === 'open')\n const waiting = open\n .filter(isWaitingOnUs)\n // Oldest first: the list is a queue, and a queue that puts the newest\n // arrival on top is the reason somebody waits three days.\n .sort((a, b) => Date.parse(a.lastMessageAt) - Date.parse(b.lastMessageAt))\n return { waiting, open: open.length, loading }\n }, [conversations, loading])\n}\n\n/**\n * How long they have been waiting, in the coarsest unit that is still true.\n *\n * Minutes for the first hour, then hours, then days — an exact \"2h 14min\" reads\n * as precision about something nobody measures that finely, and it is one more\n * number to parse in a cell that exists to be scanned.\n */\nfunction waitedFor(iso: string, t: (key: string, vars?: Record<string, string>) => string): string {\n const ms = Date.now() - Date.parse(iso)\n if (!Number.isFinite(ms) || ms < 0) return '—'\n const minutes = Math.floor(ms / 60000)\n if (minutes < 60) return t('conversations.waited.minutes', { count: String(Math.max(minutes, 1)) })\n const hours = Math.floor(minutes / 60)\n if (hours < 24) return t('conversations.waited.hours', { count: String(hours) })\n return t('conversations.waited.days', { count: String(Math.floor(hours / 24)) })\n}\n\n// ---------------------------------------------------------------------------\n// KPIs\n// ---------------------------------------------------------------------------\n\nfunction WaitingKpi() {\n const t = useTranslation()\n const { waiting, loading } = useWaiting()\n const oldest = waiting[0]\n return (\n <KpiCard\n label={t('conversations.dashboard.waiting')}\n icon=\"MessageCircleWarning\"\n loading={loading && waiting.length === 0}\n value={String(waiting.length)}\n // The count alone does not say whether this is a busy morning or a\n // failure: three people waiting ten minutes is a queue, one person\n // waiting two days is an apology.\n sub={\n oldest\n ? t('conversations.dashboard.waitingOldest', { time: waitedFor(oldest.lastMessageAt, t) })\n : t('conversations.dashboard.waitingNone')\n }\n // More is worse here, so the arrow has to be read the other way round.\n invertTrend\n />\n )\n}\n\nfunction OpenThreadsKpi() {\n const t = useTranslation()\n const { open, waiting, loading } = useWaiting()\n return (\n <KpiCard\n label={t('conversations.dashboard.openThreads')}\n icon=\"MessagesSquare\"\n loading={loading && open === 0}\n value={String(open)}\n sub={t('conversations.dashboard.openThreadsSub', { count: String(waiting.length) })}\n />\n )\n}\n\n// ---------------------------------------------------------------------------\n// The queue itself\n// ---------------------------------------------------------------------------\n\nfunction WaitingTable() {\n const t = useTranslation()\n const { waiting, loading } = useWaiting()\n\n // Column type borrowed from the widget that consumes it rather than from\n // @tanstack/react-table directly: this plugin has no table dependency of its\n // own, and adding one to name a type would put a second copy of the table\n // library in the tree of every app that installs the inbox.\n const columns = useMemo<TableWidgetProps<Conversation>['columns']>(() => [\n {\n accessorKey: 'contactName',\n header: t('conversations.dashboard.who'),\n cell: ({ row }) => (\n <span className=\"font-medium\">{row.original.contactName}</span>\n ),\n },\n {\n id: 'preview',\n header: '',\n // The message, not just the name: half of triage is deciding which of\n // five waiting people is the one worth answering first, and that is\n // decided by what they said.\n cell: ({ row }) => (\n <span className=\"line-clamp-1 text-xs text-muted-foreground\">{row.original.lastMessagePreview}</span>\n ),\n },\n {\n id: 'channel',\n header: t('conversations.contact.channel'),\n cell: ({ row }) => (\n <span className=\"text-xs text-muted-foreground\">\n {t(`conversations.filter.${row.original.channel}`)}\n </span>\n ),\n },\n {\n id: 'waited',\n header: t('conversations.dashboard.waited'),\n cell: ({ row }) => (\n <span className=\"whitespace-nowrap text-xs font-medium tabular-nums\">\n {waitedFor(row.original.lastMessageAt, t)}\n </span>\n ),\n },\n ], [t])\n\n return (\n <TableWidget\n title={t('conversations.dashboard.needsYou')}\n icon=\"MessageCircleWarning\"\n columns={columns}\n data={waiting}\n loading={loading && waiting.length === 0}\n emptyMessage={t('conversations.dashboard.needsYouEmpty')}\n // Straight into the thread. A widget that names a person waiting and then\n // makes you go find them in a list is a notification, not a queue.\n onRowClick={(row) => { window.location.hash = `/conversations?thread=${row.id}` }}\n />\n )\n}\n\n// ---------------------------------------------------------------------------\n// Registry builder — each widget carries its own provider, so it renders on the\n// app home with no inbox page around it.\n// ---------------------------------------------------------------------------\n\nexport function createConversationsDashboardWidgets(ctx: {\n store: StoreApi<ConversationsUIState>\n config: ResolvedConversationsConfig\n}): DashboardWidgetDef[] {\n const withCtx = (Inner: React.ComponentType): React.ComponentType<unknown> => {\n const Wrapped = () => (\n <ConversationsContextProvider store={ctx.store} config={ctx.config}>\n <Inner />\n </ConversationsContextProvider>\n )\n Wrapped.displayName = `ConversationsWidget(${Inner.displayName ?? Inner.name})`\n return Wrapped\n }\n\n return [\n // Visible on the home by DEFAULT, unlike most plugin widgets. The rest of\n // the registry is opt-in because a dashboard of everything is a dashboard\n // of nothing — but an unanswered customer is the one item on any home\n // screen that is time-critical, and hiding it behind Customize means it\n // only reaches the people who already knew to look.\n defineKpiWidget({\n id: 'conversations.kpi.waiting',\n title: 'conversations.dashboard.waiting',\n domain: 'conversations',\n defaultOrder: -1,\n component: withCtx(WaitingKpi),\n }),\n defineKpiWidget({\n id: 'conversations.kpi.open',\n title: 'conversations.dashboard.openThreads',\n domain: 'conversations',\n defaultOrder: 4,\n defaultVisible: false,\n component: withCtx(OpenThreadsKpi),\n }),\n defineTableWidget({\n id: 'conversations.table.waiting',\n title: 'conversations.dashboard.needsYou',\n domain: 'conversations',\n span: 2,\n defaultOrder: 10,\n component: withCtx(WaitingTable),\n }),\n ]\n}\n","import React from 'react'\nimport { Check, Loader2, QrCode, RefreshCw, ShieldAlert, ShieldCheck, Zap } from 'lucide-react'\nimport { SettingsGroup } from '@fayz-ai/admin'\nimport { Button, Input, toast } from '@fayz-ai/ui'\nimport { providersApi, type ProviderId, type ProviderRow, type ProviderNumber } from '../data/providers'\n\n/**\n * Onde a casa escolhe por onde o WhatsApp sai.\n *\n * ── os dois não são intercambiáveis, e a tela diz isso ──────────────────\n *\n * A tentação é desenhar dois cartões iguais e um botão de rádio. Seria mentira:\n *\n * Tyxter API oficial. Iniciar conversa EXIGE template aprovado pela Meta.\n * A Meta mede qualidade e limite do número, e responde por eles.\n * Evolution WhatsApp Web. Manda texto para quem quiser, sem template e sem\n * janela — e o número pode ser banido sem aviso.\n *\n * Quem troca de um para o outro precisa saber o que está trocando. Por isso\n * cada cartão carrega a consequência escrita, e não só o nome.\n *\n * ── a chave não volta ───────────────────────────────────────────────────\n *\n * O campo de chave está sempre vazio, mesmo com credencial guardada. Não é\n * esquecimento: o segredo não sai do servidor, nem mascarado, porque\n * `tx_live_••••` ainda é a chave inteira viajando até o navegador para ser\n * escondida na renderização. `hasCredential` responde a única pergunta que a\n * tela precisa fazer — já foi posta? — e o cadeado diz o resto.\n */\n\nconst COPY: Record<ProviderId, {\n name: string\n what: string\n strength: string\n cost: string\n fields: Array<{ key: string; label: string; placeholder: string; secret?: boolean }>\n}> = {\n tyxter: {\n name: 'Tyxter',\n what: 'API oficial do WhatsApp Business, via revenda.',\n strength: 'Entrega confiável, sem risco de banimento. A Meta mede a qualidade do número.',\n cost: 'Para iniciar conversa é obrigatório um template aprovado pela Meta (~1 dia). Texto livre só dentro de 24h da resposta do cliente.',\n fields: [\n { key: 'api_key', label: 'Chave da API', placeholder: 'tx_live_…', secret: true },\n { key: 'default_sender_id', label: 'Número remetente (id)', placeholder: 'cm…' },\n ],\n },\n evolution: {\n name: 'Evolution API',\n what: 'Gateway auto-hospedado, sobre o WhatsApp Web.',\n strength: 'Manda texto livre para quem quiser, sem template e sem janela de 24h.',\n cost: 'O número pode ser banido sem aviso, e a sessão cai sozinha. Aquecer o número não é opcional aqui.',\n fields: [\n { key: 'base_url', label: 'URL da instância', placeholder: 'https://evo.seudominio.com' },\n { key: 'api_key', label: 'Chave da API', placeholder: 'sua apikey', secret: true },\n { key: 'instance', label: 'Nome da instância', placeholder: 'chefcontrol' },\n ],\n },\n}\n\nconst ALL: ProviderId[] = ['tyxter', 'evolution']\n\nfunction StatusChip({ row }: { row: ProviderRow | undefined }) {\n if (!row || row.status === 'unconfigured') {\n return <span className=\"rounded-full bg-muted px-2 py-0.5 text-[10px] text-muted-foreground\">não configurado</span>\n }\n const map: Record<string, string> = {\n connected: 'bg-success-soft text-success-soft-foreground',\n connecting: 'bg-warning-soft text-warning-soft-foreground',\n disconnected: 'bg-muted text-muted-foreground',\n error: 'bg-destructive-soft text-destructive-soft-foreground',\n }\n const label: Record<string, string> = {\n connected: 'conectado', connecting: 'aguardando teste',\n disconnected: 'desconectado', error: 'com erro',\n }\n return (\n <span className={`rounded-full px-2 py-0.5 text-[10px] font-medium ${map[row.status] ?? 'bg-muted'}`}>\n {label[row.status] ?? row.status}\n </span>\n )\n}\n\nfunction NumberRow({ n }: { n: ProviderNumber }) {\n return (\n <div className=\"flex flex-wrap items-center gap-x-3 gap-y-1 py-1.5 text-xs\">\n <span className={`h-1.5 w-1.5 shrink-0 rounded-full ${n.ready ? 'bg-success' : 'bg-muted-foreground/40'}`} />\n <span className=\"font-medium tabular-nums\">{n.phone ?? n.id}</span>\n {n.displayName && <span className=\"text-muted-foreground\">{n.displayName}</span>}\n {/* Qualidade e tier só existem no provedor oficial. Nulo vira \"—\": o\n Evolution não é medido pela Meta, e fingir um \"bom\" aqui seria\n inventar a única informação que decide quanto se pode mandar. */}\n <span className=\"text-muted-foreground\">\n qualidade: <span className=\"text-foreground\">{n.quality ?? '—'}</span>\n </span>\n <span className=\"text-muted-foreground\">\n limite: <span className=\"text-foreground\">{n.tier ?? '—'}</span>\n </span>\n {n.used24h != null && n.allowance != null && (\n <span className=\"text-muted-foreground\">\n 24h: <span className=\"text-foreground tabular-nums\">{n.used24h}/{n.used24h + n.allowance}</span>\n </span>\n )}\n {!n.ready && <span className=\"text-warning\">não pronto para enviar</span>}\n </div>\n )\n}\n\nexport function WhatsAppProviders() {\n const [rows, setRows] = React.useState<ProviderRow[] | null>(null)\n const [draft, setDraft] = React.useState<Record<string, Record<string, string>>>({})\n const [busy, setBusy] = React.useState<string | null>(null)\n const [numbers, setNumbers] = React.useState<Record<string, ProviderNumber[]>>({})\n const [qr, setQr] = React.useState<{ provider: string; image: string | null; code: string | null } | null>(null)\n\n const load = React.useCallback(async () => {\n try { setRows(await providersApi.list()) } catch { setRows([]) }\n }, [])\n React.useEffect(() => { void load() }, [load])\n\n const byId = React.useMemo(\n () => Object.fromEntries((rows ?? []).map((r) => [r.provider, r])) as Record<ProviderId, ProviderRow | undefined>,\n [rows],\n )\n\n function field(p: ProviderId, key: string): string {\n const row = byId[p]\n // A chave nunca é preenchida de volta — ver o cabeçalho.\n if (key === 'api_key') return draft[p]?.[key] ?? ''\n return draft[p]?.[key] ?? String((row?.config as any)?.[key] ?? '')\n }\n\n function setField(p: ProviderId, key: string, v: string) {\n setDraft((d) => ({ ...d, [p]: { ...(d[p] ?? {}), [key]: v } }))\n }\n\n async function run(id: string, fn: () => Promise<void>) {\n setBusy(id)\n try { await fn() } catch (e: any) { toast.error(e?.message ?? 'Falhou') } finally { setBusy(null) }\n }\n\n const save = (p: ProviderId) => run(`save:${p}`, async () => {\n const d = draft[p] ?? {}\n const config: Record<string, unknown> = {}\n for (const f of COPY[p].fields) {\n if (f.secret) continue\n const v = field(p, f.key)\n if (v) config[f.key] = v\n }\n if (p === 'tyxter' && !config.api_base_url) config.api_base_url = 'https://api.tyxter.com'\n await providersApi.save(p, config, d.api_key)\n setDraft((x) => ({ ...x, [p]: { ...(x[p] ?? {}), api_key: '' } }))\n await load()\n toast.success('Guardado')\n })\n\n const test = (p: ProviderId) => run(`test:${p}`, async () => {\n const h = await providersApi.health(p)\n await load()\n toast[h.ok ? 'success' : 'error'](h.ok ? `Conectado — ${h.detail ?? 'ok'}` : `Não conectou: ${h.detail ?? '?'}`)\n })\n\n const refreshNumbers = (p: ProviderId) => run(`numbers:${p}`, async () => {\n const d = await providersApi.numbers(p)\n setNumbers((n) => ({ ...n, [p]: d.numbers }))\n if (d.numbers.length === 0) toast.error('Nenhum número nesta conta')\n })\n\n const activate = (p: ProviderId) => run(`on:${p}`, async () => {\n await providersApi.activate(p)\n await load()\n toast.success(`${COPY[p].name} está valendo agora`)\n })\n\n const pair = (p: ProviderId) => run(`qr:${p}`, async () => {\n const d = await providersApi.pair(p)\n setQr({ provider: p, image: d.qr, code: d.code })\n if (!d.qr && !d.code) toast.error('A instância não devolveu QR — já está pareada?')\n })\n\n return (\n <SettingsGroup\n title=\"WhatsApp — por onde as mensagens saem\"\n description=\"Dois caminhos, um valendo de cada vez. Troque quando quiser; a configuração do outro fica guardada.\"\n >\n {rows === null && (\n <div className=\"flex items-center gap-2 py-6 text-sm text-muted-foreground\">\n <Loader2 className=\"h-4 w-4 animate-spin\" /> carregando…\n </div>\n )}\n\n {rows !== null && ALL.map((p) => {\n const row = byId[p]\n const copy = COPY[p]\n const active = Boolean(row?.isActive)\n const caps = row?.capabilities ?? {}\n\n return (\n <div\n key={p}\n className={`rounded-card border p-4 my-2 ${active ? 'border-primary bg-primary/[0.03]' : 'bg-card'}`}\n data-testid={`provider-${p}`}\n >\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"text-sm font-semibold\">{copy.name}</span>\n {active && (\n <span className=\"inline-flex items-center gap-1 rounded-full bg-primary px-2 py-0.5 text-[10px] font-medium text-primary-foreground\">\n <Check className=\"h-3 w-3\" /> valendo agora\n </span>\n )}\n <StatusChip row={row} />\n {row?.hasCredential && (\n <span className=\"inline-flex items-center gap-1 text-[10px] text-muted-foreground\">\n <ShieldCheck className=\"h-3 w-3\" /> chave guardada\n </span>\n )}\n {/* O risco de banimento é a diferença que decide a escolha, e ela\n fica ao lado do nome — não num rodapé que ninguém lê. */}\n {caps.ban_risk === 'high' && (\n <span className=\"inline-flex items-center gap-1 text-[10px] text-warning\">\n <ShieldAlert className=\"h-3 w-3\" /> risco de banimento\n </span>\n )}\n </div>\n\n <p className=\"mt-1 text-xs text-muted-foreground\">{copy.what}</p>\n <div className=\"mt-2 grid gap-1 sm:grid-cols-2\">\n <p className=\"text-[11px] text-success-soft-foreground\">✓ {copy.strength}</p>\n <p className=\"text-[11px] text-warning\">! {copy.cost}</p>\n </div>\n\n <div className=\"mt-3 grid gap-2 sm:grid-cols-2\">\n {copy.fields.map((f) => (\n <label key={f.key} className=\"block\">\n <span className=\"text-[11px] text-muted-foreground\">{f.label}</span>\n <Input\n className=\"mt-0.5 h-8 text-sm\"\n type={f.secret ? 'password' : 'text'}\n autoComplete=\"off\"\n placeholder={f.secret && row?.hasCredential ? '•••••••• (guardada)' : f.placeholder}\n value={field(p, f.key)}\n onChange={(e) => setField(p, f.key, e.target.value)}\n />\n </label>\n ))}\n </div>\n\n <div className=\"mt-3 flex flex-wrap gap-2\">\n <Button size=\"sm\" variant=\"outline\" disabled={busy === `save:${p}`} onClick={() => save(p)}>\n {busy === `save:${p}` ? <Loader2 className=\"h-3 w-3 animate-spin\" /> : 'Guardar'}\n </Button>\n <Button size=\"sm\" variant=\"outline\" disabled={!row?.hasCredential || busy === `test:${p}`} onClick={() => test(p)}>\n {busy === `test:${p}` ? <Loader2 className=\"h-3 w-3 animate-spin\" /> : <><RefreshCw className=\"mr-1 h-3 w-3\" /> Testar</>}\n </Button>\n <Button size=\"sm\" variant=\"outline\" disabled={!row?.hasCredential || busy === `numbers:${p}`} onClick={() => refreshNumbers(p)}>\n Números\n </Button>\n {/* Só aparece onde existe. O Tyxter não pareia por QR, e um botão\n desabilitado ainda promete algo que nunca vai acontecer. */}\n {caps.qr_pairing && (\n <Button size=\"sm\" variant=\"outline\" disabled={!row?.hasCredential || busy === `qr:${p}`} onClick={() => pair(p)}>\n <QrCode className=\"mr-1 h-3 w-3\" /> Parear\n </Button>\n )}\n {!active && (\n <Button size=\"sm\" disabled={!row?.hasCredential || busy === `on:${p}`} onClick={() => activate(p)}>\n <Zap className=\"mr-1 h-3 w-3\" /> Usar este\n </Button>\n )}\n </div>\n\n {numbers[p]?.length ? (\n <div className=\"mt-3 border-t pt-2\">\n {numbers[p].map((n) => <NumberRow key={n.id} n={n} />)}\n </div>\n ) : null}\n\n {qr?.provider === p && (\n <div className=\"mt-3 border-t pt-3\">\n <p className=\"text-xs text-muted-foreground mb-2\">\n Abra o WhatsApp no celular → Aparelhos conectados → Conectar aparelho.\n </p>\n {qr.image && <img src={qr.image} alt=\"QR\" className=\"h-48 w-48 rounded border bg-white p-2\" />}\n {qr.code && <p className=\"mt-2 font-mono text-lg tracking-widest\">{qr.code}</p>}\n </div>\n )}\n </div>\n )\n })}\n </SettingsGroup>\n )\n}\n","// Channel accounts — the numbers a message can leave by (migration 003).\n//\n// Read-only by construction: the table grants members SELECT and nothing else,\n// so this accessor has no write path to offer. Rows are created by the claim\n// function and advanced by the provider's webhooks.\nimport { getSupabaseClientOptional } from '@fayz-ai/core'\nimport { T } from './tables'\nimport type { Channel } from '../types'\n\n/** Whose number it is. `fallback` is the product's, shared by every tenant. */\nexport type MessagingChannelKind = 'fallback' | 'dedicated'\n\n/** The provider's own lifecycle, kept verbatim. Only `active` may carry a send. */\nexport type MessagingChannelStatus =\n | 'requested'\n | 'provisioning'\n | 'provisioned'\n | 'verifying'\n | 'active'\n | 'failed'\n | 'released'\n | 'disconnected'\n\nexport interface MessagingChannelAccount {\n id: string\n /** `null` on the product fallback row — it belongs to the software, not a tenant. */\n tenantId: string | null\n channel: Channel\n provider: string\n providerNumberId: string\n phoneE164: string | null\n kind: MessagingChannelKind\n status: MessagingChannelStatus\n displayName: string | null\n metadata: Record<string, unknown>\n createdAt: string\n updatedAt: string\n}\n\nexport interface ListMessagingChannelsOptions {\n /** Narrow to one provider ('tyxter'). Omitted: every provider in the pool. */\n provider?: string\n /** Narrow to one channel ('whatsapp'). */\n channel?: Channel\n /** Supabase client override; defaults to the globally registered one. */\n supabaseClient?: unknown\n}\n\ntype Row = Record<string, unknown>\n\nfunction mapChannel(r: Row): MessagingChannelAccount {\n return {\n id: String(r.id),\n tenantId: (r.tenant_id as string | null) ?? null,\n channel: (r.channel as Channel) ?? 'whatsapp',\n provider: String(r.provider ?? ''),\n providerNumberId: String(r.provider_number_id ?? ''),\n phoneE164: (r.phone_e164 as string | null) ?? null,\n kind: (r.kind as MessagingChannelKind) ?? 'dedicated',\n status: (r.status as MessagingChannelStatus) ?? 'requested',\n displayName: (r.display_name as string | null) ?? null,\n metadata: (r.metadata as Record<string, unknown>) ?? {},\n createdAt: String(r.created_at ?? ''),\n updatedAt: String(r.updated_at ?? ''),\n }\n}\n\n/**\n * Every number this tenant can send by — its own, plus the product fallback.\n *\n * No tenant filter is applied, and that is deliberate: RLS already returns the\n * tenant's rows and the fallback row, and the fallback has no tenant to filter\n * on. Filtering here would hide exactly the row the panel most needs to show.\n */\nexport async function listMessagingChannels(\n options: ListMessagingChannelsOptions = {},\n): Promise<MessagingChannelAccount[]> {\n const supabase = (options.supabaseClient ?? getSupabaseClientOptional()) as\n | { from: (t: string) => Row }\n | null\n if (!supabase) {\n throw new Error(\n '[plugin-conversations] Supabase client not available. Pass supabaseClient or register the global client via createFayzApp.',\n )\n }\n\n let q = (supabase.from(T.channels) as { select: (s: string) => Row }).select('*')\n if (options.provider) q = (q as { eq: (c: string, v: string) => Row }).eq('provider', options.provider)\n if (options.channel) q = (q as { eq: (c: string, v: string) => Row }).eq('channel', options.channel)\n // Dedicated before fallback: the tenant's own number is the answer whenever\n // there is one, and the readers of this list want it first.\n q = (q as { order: (c: string, o: unknown) => Row }).order('kind', { ascending: true })\n\n const { data, error } = (await q) as { data: Row[] | null; error: unknown }\n if (error) throw error\n return (data ?? []).map(mapChannel)\n}\n\n// ── The claim's lifecycle, as a thing the UI can render ─────────────────────\n//\n// Everything below is PURE. The claim itself is an edge function call and lives\n// in ../integrations/tyxter/data.ts; what belongs here is the vocabulary of the\n// table — which states a number walks through, which of them mean \"still\n// coming\", and which single row is the tenant's own. Keeping it here rather than\n// in the panel is what lets it be tested without React.\n\n/**\n * The happy path, in order. Observed live against the provider's sandbox on\n * 2026-08-24: a provision answered `requested` and the number then walked\n * `provisioned` → `verifying` → `active` in about a second. `provisioning` is in\n * the provider's enum and was not seen at 150ms polling — kept here because the\n * enum has it and a step missing from a timeline reads as a step that failed.\n */\nexport const CHANNEL_CLAIM_TIMELINE: MessagingChannelStatus[] = [\n 'requested',\n 'provisioning',\n 'provisioned',\n 'verifying',\n 'active',\n]\n\n/**\n * The states in which the tenant already has a claim and must not start another.\n *\n * Mirrors `LIVE_CHANNEL_STATUSES` in functions/_shared/tyxter-runtime.js, which\n * is where the edge function enforces it. Two copies because an edge function\n * may not import from src/ — the vitest suite asserts they agree, so a change to\n * one that is not made to the other fails a test rather than letting the panel\n * offer a button the server will refuse.\n */\nexport const LIVE_CHANNEL_STATUSES: MessagingChannelStatus[] = [\n 'requested',\n 'provisioning',\n 'provisioned',\n 'verifying',\n 'active',\n]\n\n/** Terminal, and not a reason to stop the tenant trying again. */\nexport function isDeadChannelStatus(status: MessagingChannelStatus): boolean {\n return !LIVE_CHANNEL_STATUSES.includes(status)\n}\n\n/** On its way, but not carrying messages yet — the state the panel polls in. */\nexport function isPendingChannelStatus(status: MessagingChannelStatus): boolean {\n return LIVE_CHANNEL_STATUSES.includes(status) && status !== 'active'\n}\n\n/** The tenant's own number, live or on its way. Null when they have none. */\nexport function findDedicatedChannel(\n channels: MessagingChannelAccount[],\n tenantId?: string | null,\n): MessagingChannelAccount | null {\n return channels.find(\n (channel) => channel.kind === 'dedicated'\n && (!tenantId || channel.tenantId === tenantId)\n && !isDeadChannelStatus(channel.status),\n ) ?? null\n}\n\n/** The product's shared number, which every tenant sends by until it has its own. */\nexport function findFallbackChannel(\n channels: MessagingChannelAccount[],\n): MessagingChannelAccount | null {\n return channels.find((channel) => channel.kind === 'fallback') ?? null\n}\n\n/**\n * Can this tenant ask for a number right now?\n *\n * The same rule the edge function answers 409 on, asked before the round trip\n * so the button is disabled rather than the click refused.\n */\nexport function canClaimDedicated(\n channels: MessagingChannelAccount[],\n tenantId?: string | null,\n): boolean {\n return findDedicatedChannel(channels, tenantId) === null\n}\n","import * as React from 'react'\nimport { WhatsAppProviders } from './WhatsAppProviders'\nimport { useTranslation } from '@fayz-ai/core'\nimport { PluginSettingsPanel, SettingsGroup } from '@fayz-ai/admin'\nimport { Badge } from '@fayz-ai/ui'\nimport {\n listMessagingChannels,\n isPendingChannelStatus,\n type MessagingChannelAccount,\n} from '../data/channels'\n\n// ---------------------------------------------------------------------------\n// The inbox's settings screen.\n//\n// It exists for one reason: without it the WhatsApp connector is UNREACHABLE.\n// Connectors are surfaced by PluginSettingsPanel's Integrations tab, and that\n// tab only exists inside a plugin settings panel — so a plugin that declares a\n// connector and no settings route has declared a connector nobody can open.\n// Every app that mounted the inbox alone was in exactly that state.\n//\n// The General tab is deliberately not a copy of the connector's panel. That one\n// manages the Tyxter account (claim a number, register with Meta). This one\n// answers the question somebody asks BEFORE they know a connector exists:\n// \"why isn't my WhatsApp showing up in here?\" — so it states, plainly, which\n// numbers this inbox can send and receive by, and sends you one tab over when\n// the answer is \"none\".\n// ---------------------------------------------------------------------------\n\nfunction channelLabel(\n account: MessagingChannelAccount,\n t: (key: string) => string,\n): string {\n const key = `conversations.filter.${account.channel}`\n const translated = t(key)\n return translated === key ? account.channel : translated\n}\n\nfunction ChannelRow({ account }: { account: MessagingChannelAccount }) {\n const t = useTranslation()\n const pending = isPendingChannelStatus(account.status)\n return (\n <div className=\"flex items-center justify-between gap-3 border-b py-2.5 last:border-b-0\">\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium\">\n {account.phoneE164 ?? account.displayName ?? channelLabel(account, t)}\n </p>\n <p className=\"text-xs text-muted-foreground\">\n {channelLabel(account, t)}\n {' · '}\n {t(\n account.kind === 'dedicated'\n ? 'conversations.settings.channelDedicated'\n : 'conversations.settings.channelShared',\n )}\n </p>\n </div>\n {/* The provider's own word for the state, translated where we have a\n translation and passed through where we do not — a status invented\n here would drift from what the connector panel says one tab over. */}\n <Badge variant={account.status === 'active' ? 'default' : pending ? 'secondary' : 'outline'}>\n {t(`conversations.settings.status.${account.status}`) === `conversations.settings.status.${account.status}`\n ? account.status\n : t(`conversations.settings.status.${account.status}`)}\n </Badge>\n </div>\n )\n}\n\nfunction ConversationsGeneralSettings() {\n const t = useTranslation()\n const [channels, setChannels] = React.useState<MessagingChannelAccount[] | null>(null)\n // `null` = still loading, `[]` = loaded and empty, `'error'` = could not ask.\n // Three states and not two, because \"nenhum número conectado\" and \"não\n // consegui perguntar\" send somebody to opposite places.\n const [failed, setFailed] = React.useState(false)\n\n React.useEffect(() => {\n let cancelled = false\n listMessagingChannels()\n .then((rows) => { if (!cancelled) setChannels(rows) })\n .catch(() => { if (!cancelled) { setFailed(true); setChannels([]) } })\n return () => { cancelled = true }\n }, [])\n\n return (\n <div className=\"space-y-6\">\n {/* Primeiro por ONDE sai, depois QUAIS números saíram. A ordem inversa\n mostrava uma lista vazia antes de explicar que faltava escolher um\n provedor — e uma lista vazia parece defeito, não pendência. */}\n <WhatsAppProviders />\n\n <SettingsGroup\n title={t('conversations.settings.channels')}\n description={t('conversations.settings.channelsHelp')}\n >\n {channels === null && (\n <p className=\"py-2 text-sm text-muted-foreground\">{t('conversations.list.loading')}</p>\n )}\n {channels !== null && failed && (\n <p className=\"py-2 text-sm text-muted-foreground\">\n {t('conversations.settings.channelsUnavailable')}\n </p>\n )}\n {channels !== null && !failed && channels.length === 0 && (\n <p className=\"py-2 text-sm text-muted-foreground\">\n {t('conversations.settings.channelsEmpty')}\n </p>\n )}\n {channels?.map((account) => <ChannelRow key={account.id} account={account} />)}\n </SettingsGroup>\n </div>\n )\n}\nConversationsGeneralSettings.displayName = 'ConversationsGeneralSettings'\n\nexport function ConversationsSettingsTab() {\n const t = useTranslation()\n return (\n <PluginSettingsPanel\n title={t('conversations.settings.title')}\n generalSettings={<ConversationsGeneralSettings />}\n hostPluginId=\"conversations\"\n routeBase=\"/settings/conversations\"\n />\n )\n}\nConversationsSettingsTab.displayName = 'ConversationsSettingsTab'\n","import { countByTenant, type OnboardingProgramDef } from '@fayz-ai/core'\nimport { T } from '../data/tables'\n\n/**\n * Implantação da caixa de entrada — um canal e um hábito.\n *\n * Ordem 30, à frente da base do workspace (50) e do CRM (80), e isso é uma\n * opinião: numa operação que atende, o cliente escreve antes de qualquer outra\n * coisa acontecer. Convidar a equipe e desenhar o funil são trabalho de quem já\n * tem alguém escrevendo; ligar o canal é o que faz aparecer o primeiro.\n *\n * Dois passos, e o segundo não é uma tela. A caixa de entrada é o módulo que\n * mais parece pronto vazio: ela abre, tem filtro por canal, tem busca — e não\n * chega mensagem nenhuma porque ninguém ligou o número. O passo que fecha é o\n * único que prova que o caminho inteiro existe: uma mensagem que entrou e foi\n * respondida daqui.\n */\nexport function buildConversationsOnboarding(): OnboardingProgramDef {\n return {\n id: 'conversations',\n title: 'Quem fala com você',\n order: 30,\n role: 'frontdesk',\n instructions: `Você está ligando a caixa de entrada. O risco aqui não é a pessoa não achar a tela — é ela achar a tela, ver que está vazia e concluir que o produto não funciona.\n\n- Comece pelo CANAL. Sem número ligado não entra mensagem nenhuma, e todo o resto do módulo é decoração. O caminho é Configurações › Conversas › Integrações.\n- Explique a diferença entre o número da plataforma e um número dedicado ANTES de ela escolher: no compartilhado ela já começa a receber hoje; no dedicado sai o nome dela, mas ainda passa por um cadastro na Meta.\n- O segundo passo é a prova: peça para ela mandar uma mensagem do próprio celular para o número e responder daqui. É o que mostra que ida e volta funcionam.\n- Se ela já tem WhatsApp Business no celular, diga com todas as letras o que muda: as conversas passam a ficar aqui, onde a equipe inteira vê, em vez de num aparelho só.`,\n steps: [\n {\n key: 'channel',\n title: 'Ligue o canal onde seu cliente já fala',\n description: 'Sem número conectado, a caixa de entrada abre vazia para sempre.',\n icon: 'MessageCircle',\n route: '/settings/conversations/_integrations',\n priority: 'urgent',\n // O passo É a tela: um app que monte o inbox sem a rota de configurações\n // não tem onde ligar canal nenhum, e a linha ficaria aberta para sempre.\n requiresRoute: true,\n agentHint:\n 'Abra Integrações e apresente as duas opções pelo que elas custam à pessoa HOJE: o número da plataforma começa a receber agora, o dedicado sai com o nome dela mas espera o cadastro na Meta.',\n howTo:\n 'Configurações › Conversas › Integrações › Tyxter. O número da plataforma já está ligado e é compartilhado com outros negócios — dá para começar a receber por ele hoje. Um número dedicado sai só com o seu nome, libera os seus próprios modelos de mensagem e tem limite de envio próprio; ele é alugado no mês e ainda passa por um cadastro rápido na Meta antes do primeiro envio.',\n celebrate: 'Canal ligado — agora dá para te acharem 📲',\n script: [\n { kind: 'goto', route: '/settings/conversations/_integrations' },\n {\n kind: 'ask',\n text: 'Estamos nas Integrações da caixa de entrada. Mostre o número da plataforma e o dedicado, explique o que muda em cada um e ajude a pessoa a escolher.',\n },\n ],\n check: async () => (await countByTenant(T.channels)) > 0,\n },\n {\n key: 'first-reply',\n title: 'Receba e responda a primeira mensagem',\n description: 'A prova de que a ida e a volta funcionam.',\n icon: 'Send',\n route: '/conversations',\n priority: 'high',\n agentHint:\n 'Não é passo de cadastro. Peça para a pessoa mandar uma mensagem do próprio celular para o número ligado e responder aqui de dentro — e fique com ela até a resposta chegar do outro lado.',\n howTo:\n 'Pegue o seu celular, mande uma mensagem qualquer para o número que você acabou de ligar e volte aqui: a conversa aparece em Conversas. Responda por aqui e confira que chegou no celular. É o teste que prova que a caixa de entrada está no ar — e é também como a equipe vai trabalhar todo dia, com a conversa num lugar que todo mundo vê em vez de num aparelho só.',\n celebrate: 'Ida e volta funcionando 💬',\n // A conversa existe assim que a primeira mensagem entra, então a\n // contagem de threads é a checagem — e não a de mensagens, que seria\n // fechada por um envio que ninguém respondeu.\n check: async () => (await countByTenant(T.conversations)) > 0,\n },\n ],\n }\n}\n","// The one place that names the Tyxter number-claim data plane.\n//\n// Kept apart from connectorDef.tsx for the reason data.ts exists on the Kiwify\n// and Stripe connectors: the credential path is the half that moves when the\n// platform broker lands, and when it does only this file should change.\nimport {\n CONNECTOR_RUNTIME_TOKEN_HEADER,\n connectorRuntimeToken,\n getSupabaseClientOptional,\n} from '@fayz-ai/core'\n\n/** The edge function that holds every phone-number call this connector makes. */\nexport const TYXTER_NUMBER_CLAIM_FUNCTION = 'tyxter-number-claim'\n\n/** The channel row, as the function hands it back. A subset of the table on purpose. */\nexport interface ClaimedChannel {\n id: string\n channel: string\n provider: string\n /** Null while the row is still a reservation with no number behind it. */\n providerNumberId: string | null\n phoneE164: string | null\n kind: string\n status: string\n displayName: string | null\n}\n\n/**\n * Where to send the human to finish the Meta registration, and how good the\n * link is.\n *\n * `source` is surfaced rather than hidden because the three are not equivalent:\n * 'session' returns the person to this very panel and names the tenant to Meta;\n * 'error_link' is the provider's own pre-targeted page; 'default' is the generic\n * dashboard, which works but assumes the browser is signed into the right\n * organization. The panel says less when the source is worse.\n */\nexport interface MetaRegistrationHandoff {\n source: 'session' | 'error_link' | 'default'\n url: string\n note: string | null\n sessionId?: string\n expiresAt?: string | null\n}\n\nexport interface ClaimResult {\n channel: ClaimedChannel\n metaRegistration: MetaRegistrationHandoff | null\n}\n\ninterface MinimalClient {\n functions: {\n invoke: (\n name: string,\n options: { body: unknown; headers?: Record<string, string> },\n ) => Promise<{ data: unknown; error: { message: string } | null }>\n }\n}\n\nfunction client(): MinimalClient {\n const supabase = getSupabaseClientOptional() as MinimalClient | null\n if (!supabase) throw new Error('Sem conexão com o banco para falar com a Tyxter.')\n return supabase\n}\n\n/**\n * One call to the data plane.\n *\n * The Supabase session rides in `Authorization` — the gateway checks it, and the\n * function re-checks membership in the tenant against it, because renting a\n * number costs money every month.\n *\n * The runtime token is BEST-EFFORT here, unlike on Kiwify. There it identifies\n * whose credential to redeem and a call without it is unanswerable; here the\n * credential is the platform's own, held once for the whole product, and the\n * function falls back to machine redemption keyed by the tenant. An app that\n * installed no token source can still claim a number, which is the right\n * outcome — refusing would be enforcing a requirement that does not exist.\n */\nasync function invoke<T>(body: Record<string, unknown>): Promise<T> {\n let headers: Record<string, string> | undefined\n try {\n headers = { [CONNECTOR_RUNTIME_TOKEN_HEADER]: await connectorRuntimeToken({ tenantId: String(body.tenantId ?? '') }) }\n } catch {\n headers = undefined\n }\n\n const { data, error } = await client().functions.invoke(TYXTER_NUMBER_CLAIM_FUNCTION, {\n body,\n ...(headers ? { headers } : {}),\n })\n if (error) throw new Error(error.message)\n return data as T\n}\n\n/**\n * A page address, cleaned up for `return_url`.\n *\n * The provider's allowlist refuses a URL carrying a fragment, credentials or a\n * non-https scheme, so the fragment goes — and this app is a hash router, which\n * means the fragment is the WHOLE route. Losing it returns the human to the\n * app's front door rather than to this panel, which is a smaller failure than a\n * session the provider refuses to create at all.\n */\nexport function returnUrlFrom(href: string | null | undefined): string | null {\n if (!href) return null\n try {\n const url = new URL(href)\n url.hash = ''\n url.username = ''\n url.password = ''\n return url.toString()\n } catch {\n return null\n }\n}\n\n/** The page the human is standing on, as a `return_url` the provider will accept. */\nexport function currentReturnUrl(): string | null {\n return typeof window === 'undefined' ? null : returnUrlFrom(window.location.href)\n}\n\n/** Rent a dedicated number for this tenant, in the given area code. */\nexport function claimDedicatedNumber(input: { tenantId: string; ddd: string }): Promise<ClaimResult> {\n return invoke<ClaimResult>({\n action: 'claim',\n tenantId: input.tenantId,\n ddd: input.ddd,\n returnUrl: currentReturnUrl(),\n })\n}\n\n/**\n * Re-read the tenant's numbers at the provider and sync the rows.\n *\n * The poll the panel runs while a claim is pending. It is not redundant with the\n * webhook: four of the five lifecycle steps emit no event at all (verified\n * live — a full provision to `active` produced zero `phone_number.*` events), so\n * without this the row would sit on \"Solicitado\" until the day the number is\n * disconnected.\n */\nexport function refreshDedicatedNumbers(tenantId: string): Promise<{ channels: ClaimedChannel[] }> {\n return invoke<{ channels: ClaimedChannel[] }>({ action: 'status', tenantId })\n}\n\n/** The link that hands the human to Meta's Embedded Signup, which no key can do headlessly. */\nexport function metaRegistrationHandoff(tenantId: string): Promise<{\n metaRegistration: MetaRegistrationHandoff | null\n channel: ClaimedChannel\n}> {\n return invoke({ action: 'handoff', tenantId, returnUrl: currentReturnUrl() })\n}\n","// \"Can this app take a Pix inside a conversation?\" — asked from the browser,\n// answered by the provider.\n//\n// It is a function call and not a table read because the answer does not live in\n// this pool. It lives at the provider, behind the PLATFORM's key, which a\n// browser must never hold — so `tyxter-payment-preflight` asks on the panel's\n// behalf and hands back only the verdict. There is nothing to cache here: a\n// merchant who has just finished the credential handoff is exactly the person\n// pressing the button again.\nimport { getActiveTenantId, getSupabaseClientOptional } from '@fayz-ai/core'\n\n/** The edge function that holds both questions. */\nexport const PAYMENT_PREFLIGHT_FUNCTION = 'tyxter-payment-preflight'\n\n/**\n * Which rail is actually behind the payments channel.\n *\n * The distinction that matters and is easy to collapse: `sandbox_default` is\n * READY — charges open, settle and fire webhooks — but the money is not real,\n * and the transparent-Pix provider options are refused. A green badge over it\n * is how a salon finds out on the day of its first real charge, so the panel\n * says \"sandbox\" beside the tick instead.\n */\nexport type PaymentActiveMode = 'sandbox_default' | 'iniciador' | 'abacate_pay'\n\nexport interface PaymentReadiness {\n /** The provider's own verdict. True for the sandbox fixture too — see `sandbox`. */\n ready: boolean\n status: string\n mode: PaymentActiveMode | null\n /** `mode === 'sandbox_default'`: ready, but not with real money. */\n sandbox: boolean\n environment: string | null\n /** Why not, when the provider says why. */\n reason: string | null\n}\n\n/** The browser handoff where the merchant enters credentials this app never sees. */\nexport interface PaymentSetupSession {\n setupUrl: string | null\n requestId: string | null\n expiresAt: string | null\n environment: string | null\n}\n\ninterface MinimalClient {\n functions: {\n invoke: (\n name: string,\n options: { body: unknown },\n ) => Promise<{ data: unknown; error: { message: string } | null }>\n }\n}\n\nasync function invoke<T>(body: Record<string, unknown>, tenantId?: string): Promise<T> {\n const supabase = getSupabaseClientOptional() as MinimalClient | null\n if (!supabase) throw new Error('Sem conexão com o banco para verificar os pagamentos.')\n const tenant = tenantId ?? getActiveTenantId()\n if (!tenant) throw new Error('Sem negócio selecionado.')\n\n const { data, error } = await supabase.functions.invoke(PAYMENT_PREFLIGHT_FUNCTION, {\n body: { ...body, tenantId: tenant },\n })\n if (!error) return data as T\n\n // supabase-js buries the function's own body in the error context. Without\n // digging it out every refusal reads as \"Edge Function returned a non-2xx\n // status code\", which is what \"not connected\", \"no key\" and a CORS preflight\n // all look like — while the function answered each with a sentence.\n let message = ''\n try {\n const payload = (await (error as { context?: { json?: () => Promise<unknown> } })\n ?.context?.json?.()) as { message?: string; error?: string } | undefined\n message = String(payload?.message ?? payload?.error ?? '').trim()\n } catch {\n /* no JSON body */\n }\n throw new Error(message || error.message)\n}\n\nexport async function readPaymentReadiness(tenantId?: string): Promise<PaymentReadiness> {\n const answer = await invoke<{ payments: PaymentReadiness }>({ action: 'status' }, tenantId)\n return answer.payments\n}\n\nexport async function openPaymentSetupSession(tenantId?: string): Promise<PaymentSetupSession> {\n return await invoke<PaymentSetupSession>({ action: 'connect' }, tenantId)\n}\n\n/**\n * Is the `pay` action safe to offer in a message template?\n *\n * The sandbox fixture counts, deliberately: a build being dogfooded on sandbox\n * credentials must be able to exercise the whole flow, and the panel is where\n * the \"sandbox\" qualifier is shown. What is refused is a channel with nothing\n * selected behind it, where a Pix button would produce a charge that never opens.\n */\nexport function canOfferPayment(readiness: PaymentReadiness | null): boolean {\n if (!readiness?.ready) return false\n return readiness.mode !== null\n}\n","// Tyxter — WhatsApp as a channel of the inbox, and the first messaging\n// connector on the standard (docs/integrations/CONNECTORS.md).\n//\n// It is the connector with NO connect form at all, and that is the whole design\n// rather than an omission. FayaLabs holds one Tyxter project per product and\n// one dashboard key with it; a tenant gets WhatsApp by agreeing to use it, not\n// by going to find a key that was never issued to them. So `fields` is empty,\n// no credential sink is involved, and connecting writes one thing: the\n// connections row that says this tenant switched it on. The key is redeemed\n// server-side, per call, by the edge functions that land in WP3–WP6.\n//\n// `authKind: 'api-key'` still, and not 'oauth': Tyxter has no consent screen and\n// no app to install, so declaring OAuth would ship a button with no flow behind\n// it (the Bling failure named in the standard).\n//\n// No SQL of its own for the CONNECTION — that is a connections row (029).\n// The table this plugin does add, plg_conversations_channels, is not connection\n// state: it is which phone number a message leaves by, which outlives any one\n// connector and is read by the inbox itself.\nimport React, { useCallback, useEffect, useRef, useState } from 'react'\nimport { ExternalLink, Loader2 } from 'lucide-react'\nimport { Button, Input, toast } from '@fayz-ai/ui'\nimport {\n connectionRuns,\n connectionStatus,\n createConnectionStore,\n useActiveTenantId,\n type ConnectorDefinition,\n} from '@fayz-ai/core'\nimport {\n CHANNEL_CLAIM_TIMELINE,\n canClaimDedicated,\n findDedicatedChannel,\n findFallbackChannel,\n isPendingChannelStatus,\n listMessagingChannels,\n type MessagingChannelAccount,\n type MessagingChannelStatus,\n} from '../../data/channels'\nimport {\n claimDedicatedNumber,\n metaRegistrationHandoff,\n refreshDedicatedNumbers,\n type MetaRegistrationHandoff,\n} from './data'\nimport {\n canOfferPayment,\n openPaymentSetupSession,\n readPaymentReadiness,\n type PaymentReadiness,\n} from '../../data/payment-preflight'\n\nexport const TYXTER_CONNECTOR_ID = 'tyxter'\n\n/**\n * Four seconds.\n *\n * Nobody is staring at a spinner here — the caller is an automation reacting to\n * a booking — but the budget is what stops a slow provider from holding the\n * automation executor's worker while the next twenty events queue behind it.\n * Tyxter answers `POST /v1/messages` with a 202 and does the delivery\n * asynchronously, so the call being timed is an accept, not a send.\n */\nexport const TYXTER_LATENCY_BUDGET_MS = 4000\n\n// One account per tenant: the platform's project is the same one for everybody,\n// and what the row records is consent, so there is no instance to key on.\nconst store = createConnectionStore(TYXTER_CONNECTOR_ID)\n\nconst KIND_LABEL: Record<MessagingChannelAccount['kind'], string> = {\n fallback: 'Número da plataforma',\n dedicated: 'Número dedicado',\n}\n\nconst STATUS_LABEL: Record<MessagingChannelAccount['status'], string> = {\n requested: 'Solicitado',\n provisioning: 'Em preparação',\n provisioned: 'Preparado',\n verifying: 'Em verificação',\n active: 'Ativo',\n failed: 'Falhou',\n released: 'Liberado',\n disconnected: 'Desconectado',\n}\n\n/** How long between polls while a claim is on its way. */\nconst POLL_INTERVAL_MS = 5000\n\n/**\n * The lifecycle, drawn.\n *\n * Five steps and only the last one sends. Drawn as a list rather than a single\n * badge because the wait is the thing being explained: a person who asked for a\n * phone line and sees \"Em preparação\" for two minutes needs to know how many\n * steps are left, not just which one is current.\n */\nfunction ClaimTimeline({ status }: { status: MessagingChannelStatus }) {\n if (status === 'failed' || status === 'released' || status === 'disconnected') {\n return (\n <p className=\"text-xs text-destructive\">\n {status === 'failed'\n ? 'O pedido não foi concluído. Você pode tentar de novo.'\n : `Este número saiu do ar (${STATUS_LABEL[status]}).`}\n </p>\n )\n }\n const current = CHANNEL_CLAIM_TIMELINE.indexOf(status)\n return (\n <ol className=\"space-y-1\">\n {CHANNEL_CLAIM_TIMELINE.map((step, index) => {\n const done = current > index\n const now = current === index\n return (\n <li\n key={step}\n className={`flex items-center gap-2 text-xs ${\n now ? 'font-medium text-foreground' : done ? 'text-muted-foreground' : 'text-muted-foreground/60'\n }`}\n >\n <span aria-hidden className={`h-1.5 w-1.5 rounded-full ${done || now ? 'bg-primary' : 'bg-border'}`} />\n {STATUS_LABEL[step]}\n {now && status !== 'active' && <Loader2 className=\"h-3 w-3 animate-spin\" />}\n </li>\n )\n })}\n </ol>\n )\n}\n\n/**\n * The handoff a machine cannot make.\n *\n * A rented number is not a registered one: Meta has never seen it, and WhatsApp\n * Embedded Signup is a browser window a person clicks through — the provider's\n * own connect endpoint refuses a Salvy number with `meta_registration_required`\n * and says there is no headless equivalent. So this is the honest end of the\n * flow, and it is prominent rather than tucked away, because a claim that stops\n * here without saying so looks like a claim that worked.\n */\nfunction MetaRegistrationCard({ handoff }: { handoff: MetaRegistrationHandoff }) {\n return (\n <div className=\"space-y-2 rounded-md border border-primary/40 bg-primary/5 p-3\">\n <p className=\"text-xs font-medium text-foreground\">Falta liberar o número na Meta</p>\n <p className=\"text-xs text-muted-foreground\">\n O número já é seu, mas ainda não pode enviar: a Meta exige que alguém conclua o cadastro do\n WhatsApp numa janela do navegador. É rápido, e só precisa ser feito uma vez.\n </p>\n <Button asChild size=\"sm\">\n <a href={handoff.url} target=\"_blank\" rel=\"noopener noreferrer\">\n Concluir cadastro na Meta\n <ExternalLink className=\"ml-1.5 h-3.5 w-3.5\" />\n </a>\n </Button>\n {handoff.source === 'default' && (\n <p className=\"text-[11px] text-muted-foreground\">\n Confira se a conta aberta no navegador é a certa antes de concluir.\n </p>\n )}\n </div>\n )\n}\n\n/**\n * Can the customer pay inside the conversation, and if not, what has to happen?\n *\n * The answer is not in this pool. It is at the provider, behind the platform's\n * key, so `tyxter-payment-preflight` asks and this renders the verdict. Three\n * states and they are deliberately not two:\n *\n * ready, real a payment connection is selected. Nothing to do.\n * ready, sandbox charges open and settle, with money that is not real.\n * Shown as ready WITH the word \"sandbox\", because a green\n * tick over a fixture is discovered on the day of the first\n * real charge.\n * not ready the handoff button, which opens a session at the provider\n * rather than linking to a dashboard the merchant then has\n * to navigate.\n *\n * The credentials are typed at the provider and never reach this app. That is\n * the accepted redundancy with the app's own AbacatePay connector, and it is\n * also the reason this panel has no form: a secret that never enters the\n * product is one the product cannot leak.\n */\nfunction TyxterPaymentsPanel() {\n const [readiness, setReadiness] = useState<PaymentReadiness | null>(null)\n const [error, setError] = useState<string | null>(null)\n const [loading, setLoading] = useState(true)\n const [handoff, setHandoff] = useState<string | null>(null)\n const [opening, setOpening] = useState(false)\n\n useEffect(() => {\n let cancelled = false\n void readPaymentReadiness()\n .then((value) => { if (!cancelled) setReadiness(value) })\n .catch((caught: unknown) => {\n if (!cancelled) setError(caught instanceof Error ? caught.message : 'Não deu para verificar agora.')\n })\n .finally(() => { if (!cancelled) setLoading(false) })\n return () => { cancelled = true }\n }, [])\n\n const connect = async () => {\n setOpening(true)\n setError(null)\n try {\n const session = await openPaymentSetupSession()\n setHandoff(session.setupUrl)\n // Opened by the merchant and not by us: a popup fired from an async\n // callback is the one a browser blocks, and a blocked popup reads as a\n // button that does nothing.\n } catch (caught) {\n setError(caught instanceof Error ? caught.message : 'Não deu para abrir a configuração.')\n } finally {\n setOpening(false)\n }\n }\n\n const ready = canOfferPayment(readiness)\n\n return (\n <div className=\"space-y-3 border-t pt-4\">\n <div>\n <p className=\"text-sm font-medium text-foreground\">Pagamento no WhatsApp</p>\n <p className=\"mt-0.5 text-xs text-muted-foreground\">\n Com isso ligado, o cliente recebe o Pix na própria conversa e o agendamento fica marcado\n como pago sozinho.\n </p>\n </div>\n\n {loading && <p className=\"text-xs text-muted-foreground\">Verificando…</p>}\n\n {!loading && ready && (\n <p className=\"text-xs text-foreground\">\n <span className=\"font-medium text-emerald-600\">Pronto para cobrar</span>\n {readiness?.sandbox && (\n <span className=\"ml-2 rounded bg-amber-100 px-1.5 py-0.5 text-[11px] font-medium text-amber-800\">\n sandbox — o dinheiro não é real\n </span>\n )}\n </p>\n )}\n\n {!loading && !ready && (\n <div className=\"space-y-2\">\n <p className=\"text-xs text-muted-foreground\">\n Ainda não dá para cobrar por aqui. A conta de pagamento é configurada na Tyxter, em uma\n página deles — a sua chave não passa por este app.\n {readiness?.reason ? ` (${readiness.reason})` : ''}\n </p>\n {handoff ? (\n <a\n href={handoff}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"inline-block rounded-md border px-3 py-1.5 text-xs font-medium hover:bg-muted\"\n >\n Abrir a configuração de pagamento →\n </a>\n ) : (\n <button\n type=\"button\"\n onClick={() => { void connect() }}\n disabled={opening}\n className=\"rounded-md border px-3 py-1.5 text-xs font-medium hover:bg-muted disabled:opacity-50\"\n >\n {opening ? 'Abrindo…' : 'Configurar pagamento'}\n </button>\n )}\n </div>\n )}\n\n {error && <p className=\"text-xs text-destructive\">{error}</p>}\n </div>\n )\n}\n\n/**\n * Which numbers this tenant sends by, and the button that gets it one of its own.\n *\n * ── Why this polls ──────────────────────────────────────────────────────────\n * Verified against the provider's sandbox on 2026-08-24: provisioning a number\n * walked requested → provisioned → verifying → active in about a second and\n * emitted ZERO `phone_number.*` webhooks. The deliverable events are verified /\n * disconnected / released / quality_changed / messaging_limit_*, and none of\n * them fired. So the webhook (WP4) is not the mechanism that advances a claim —\n * asking is, and this panel asks every few seconds while the row is pending and\n * stops the moment it is not.\n */\nfunction TyxterExtraPanel() {\n const tenantId = useActiveTenantId()\n const [channels, setChannels] = useState<MessagingChannelAccount[] | null>(null)\n const [error, setError] = useState<string | null>(null)\n const [ddd, setDdd] = useState('')\n const [claiming, setClaiming] = useState(false)\n const [handoff, setHandoff] = useState<MetaRegistrationHandoff | null>(null)\n const mounted = useRef(true)\n\n useEffect(() => {\n mounted.current = true\n return () => { mounted.current = false }\n }, [])\n\n const load = useCallback(async () => {\n try {\n const rows = await listMessagingChannels({ provider: TYXTER_CONNECTOR_ID, channel: 'whatsapp' })\n if (mounted.current) { setChannels(rows); setError(null) }\n } catch {\n if (mounted.current) setError('Não conseguimos ler os números agora.')\n }\n }, [])\n\n useEffect(() => { void load() }, [load])\n\n const dedicated = channels ? findDedicatedChannel(channels, tenantId) : null\n const fallback = channels ? findFallbackChannel(channels) : null\n const pending = dedicated ? isPendingChannelStatus(dedicated.status) : false\n\n // The poll, and the reason it is keyed on `pending`: a screen that keeps\n // asking after the answer arrived is a cost with no reader.\n useEffect(() => {\n if (!pending || !tenantId) return undefined\n const timer = setInterval(() => {\n void refreshDedicatedNumbers(tenantId).then(() => load()).catch(() => {})\n }, POLL_INTERVAL_MS)\n return () => clearInterval(timer)\n }, [pending, tenantId, load])\n\n // The link is asked for once the number exists and while it cannot send.\n // Not during the wait — there is nothing to register yet — and not once the\n // row carries a Meta id, because asking then costs two provider calls per\n // per panel open to be told what the row already said.\n const registered = !!dedicated?.metadata?.meta_phone_number_id\n useEffect(() => {\n if (!tenantId || !dedicated || dedicated.status !== 'active' || registered || handoff) return\n void metaRegistrationHandoff(tenantId)\n .then((answer) => { if (mounted.current) setHandoff(answer.metaRegistration) })\n .catch(() => {})\n }, [tenantId, dedicated, registered, handoff])\n\n const canClaim = !!channels && !!tenantId && canClaimDedicated(channels, tenantId)\n\n async function onClaim() {\n if (!tenantId || !/^\\d{2}$/.test(ddd)) return\n setClaiming(true)\n try {\n const result = await claimDedicatedNumber({ tenantId, ddd })\n setHandoff(result.metaRegistration)\n setDdd('')\n await load()\n toast.success('Número solicitado. Acompanhe aqui o andamento.')\n } catch (caught) {\n toast.error(caught instanceof Error ? caught.message : 'Não foi possível pedir o número.')\n } finally {\n if (mounted.current) setClaiming(false)\n }\n }\n\n return (\n <div className=\"space-y-3\">\n <div>\n <p className=\"text-sm font-medium text-foreground\">Números</p>\n <p className=\"mt-0.5 text-xs text-muted-foreground\">\n Por onde as suas mensagens de WhatsApp saem hoje.\n </p>\n </div>\n\n {error && <p className=\"text-xs text-destructive\">{error}</p>}\n\n {channels && channels.length === 0 && (\n <p className=\"text-xs text-muted-foreground\">\n Nenhum número configurado ainda. Assim que a plataforma liberar o número compartilhado,\n ele aparece aqui.\n </p>\n )}\n\n {channels && channels.length > 0 && (\n <ul className=\"space-y-2\">\n {channels.map((channel) => (\n <li\n key={channel.id}\n className=\"flex flex-wrap items-baseline justify-between gap-2 rounded-md border bg-muted/30 p-3 text-xs\"\n >\n <span className=\"font-medium text-foreground\">\n {channel.phoneE164 ?? channel.providerNumberId}\n </span>\n <span className=\"text-muted-foreground\">\n {KIND_LABEL[channel.kind]} · {STATUS_LABEL[channel.status]}\n </span>\n </li>\n ))}\n </ul>\n )}\n\n {dedicated && (\n <div className=\"space-y-2 rounded-md border p-3\">\n <p className=\"text-xs font-medium text-foreground\">Seu número dedicado</p>\n <ClaimTimeline status={dedicated.status} />\n {dedicated.status === 'active' && !handoff && (\n <p className=\"text-xs text-muted-foreground\">\n Ativo. As suas mensagens saem por ele, com o seu nome e sem dividir com ninguém.\n </p>\n )}\n </div>\n )}\n\n {handoff && <MetaRegistrationCard handoff={handoff} />}\n\n {canClaim && (\n <div className=\"space-y-2 border-t pt-3\">\n <p className=\"text-xs font-medium text-foreground\">Quer um número só seu?</p>\n <p className=\"text-xs text-muted-foreground\">\n {fallback\n ? 'Hoje as suas mensagens saem pelo número da plataforma, dividido com outros negócios. Um número dedicado sai só com o seu nome, libera os seus próprios modelos de mensagem e tem limite de envio próprio.'\n : 'Um número dedicado sai só com o seu nome e tem limite de envio próprio.'}\n </p>\n <div className=\"flex items-end gap-2\">\n <label className=\"text-xs text-muted-foreground\">\n DDD\n <Input\n value={ddd}\n onChange={(e) => setDdd(e.target.value.replace(/\\D/g, '').slice(0, 2))}\n inputMode=\"numeric\"\n placeholder=\"21\"\n className=\"mt-1 w-20\"\n aria-label=\"DDD do número dedicado\"\n />\n </label>\n <Button size=\"sm\" disabled={claiming || !/^\\d{2}$/.test(ddd)} onClick={() => void onClaim()}>\n {claiming && <Loader2 className=\"mr-1.5 h-3.5 w-3.5 animate-spin\" />}\n Solicitar número dedicado\n </Button>\n </div>\n <p className=\"text-[11px] text-muted-foreground\">\n O número é alugado no mês e, depois que chegar, ainda precisa de um cadastro rápido na\n Meta para começar a enviar.\n </p>\n </div>\n )}\n\n <TyxterPaymentsPanel />\n </div>\n )\n}\n\nexport const tyxterConnectorDef: ConnectorDefinition = {\n id: TYXTER_CONNECTOR_ID,\n hostPluginId: 'conversations',\n name: 'Tyxter',\n description:\n 'WhatsApp já pronto: confirmações e lembretes saem por um número da plataforma, e a resposta do cliente cai na sua caixa de entrada.',\n icon: 'MessageCircle',\n logoUrl: 'https://assets.fayz.ai/connectors/tyxter.png',\n category: 'messaging',\n authKind: 'api-key',\n // Sending is in line with the automation that asked for it; the reply, the\n // delivery status and the template approvals arrive later and file runs.\n planes: ['request', 'sync'],\n direction: 'outbound',\n docsUrl: 'https://tyxter.com/docs',\n // A message that does not go out must never take the booking with it. The\n // opposite verdict from a payment gateway, and for the opposite reason.\n latencyBudgetMs: TYXTER_LATENCY_BUDGET_MS,\n onFailure: 'degrade',\n permissions: [\n {\n kind: 'data',\n resource: 'conversation',\n actions: ['read', 'write'],\n reason:\n 'Abrir a conversa do cliente no WhatsApp e mantê-la em dia — é onde a mensagem enviada e a resposta dele ficam juntas.',\n },\n {\n kind: 'data',\n resource: 'message',\n actions: ['read', 'write'],\n reason:\n 'Guardar cada mensagem enviada e recebida, com o status que o WhatsApp devolve (enviada, entregue, lida).',\n },\n {\n kind: 'data',\n resource: 'message_template',\n actions: ['read', 'write'],\n reason:\n 'Ler os modelos de mensagem deste app e guardar o que a Meta respondeu sobre cada um: aprovado, recusado ou pausado.',\n },\n ],\n // No form: the key is the platform's. The connect step says so on its own\n // (`connectors.authorize.nothingToFill`) rather than drawing an empty grid.\n fields: [],\n invokedAt: [\n {\n key: 'messaging.send',\n label: 'Uma automação dispara e a mensagem sai no WhatsApp do cliente',\n touches: ['conversation', 'message'],\n },\n {\n key: 'conversations.inbound',\n label: 'O cliente responde, e a resposta entra na caixa de entrada',\n plane: 'sync',\n trigger: 'webhook',\n touches: ['conversation', 'message'],\n },\n {\n key: 'messaging.template_sync',\n label: 'Os modelos de mensagem vão para aprovação da Meta e o resultado volta para cá',\n plane: 'sync',\n trigger: 'scheduled',\n touches: ['message_template'],\n },\n ],\n howItSyncs: [\n 'As mensagens saem por um número de WhatsApp da plataforma, que já vem pronto — você não cadastra chave nenhuma nem precisa de um chip só seu.',\n 'Como esse número é dividido com outros negócios, o nome do seu vai escrito no começo da mensagem, e só modelos já aprovados pela Meta podem sair por ele.',\n 'Se você pedir um número dedicado, ele passa a ser o remetente assim que ficar ativo — aí a mensagem sai só com o seu nome e nada mais é dividido.',\n 'A resposta do cliente volta para Conversas em segundos, e botões como Confirmar e Cancelar já atualizam o agendamento sozinhos.',\n ],\n ...connectionStatus(\n TYXTER_CONNECTOR_ID,\n (connection) => ({ connected: !!connection?.active, plane: 'request' }),\n { store: () => store },\n ),\n ...connectionRuns(store),\n // Nothing to receive: the hub split out a credential half that does not exist\n // for this connector, so `values` is empty and consent is the whole act.\n saveConnection: async () => {\n await store.save({ active: true })\n },\n disconnect: () => store.disconnect(),\n disconnectNote:\n 'Desconectar para de enviar e de receber mensagens no WhatsApp. As conversas que já existem continuam aqui.',\n ExtraPanel: TyxterExtraPanel,\n}\n","// AUTO-GENERATED from 000_baseline.sql, 001_a_caixa_sabe_quem_falou_por_ultimo.sql, 002_a_casa_escolhe_por_onde_o_whatsapp_sai.sql, 003_a_campanha_sai_no_ritmo_que_o_numero_aguenta.sql, 004_a_tela_sabe_se_pode_falar_antes_de_deixar_escrever.sql, 005_a_conversa_conta_ao_funil_que_houve_contato.sql — regenerate with scripts/embed-migrations.mjs\n// SQL files are the source of truth; this inline copy lets the manifest declare\n// migrations as data. Do not edit by hand — run the embed script instead.\n\nexport const MIGRATION_000_BASELINE = `-- ============================================================================\n-- plugins/plugin-conversations/src/migrations/000_baseline.sql — what installing this provisions, as one file.\n--\n-- GENERATED by scripts/emit-unit-baselines.mjs (#338). Do not edit by hand:\n-- change the schema, regenerate, and let scripts/check-chain-equivalence.mjs\n-- prove the result still matches.\n--\n-- This replaced 9 migration files. They are kept, unapplied, in\n-- migrations.archive/ — a year of decisions is worth reading even when it is no\n-- longer worth replaying.\n--\n-- Object order is fixed rather than dependency-sorted: schemas, types,\n-- sequences, tables, defaults, functions, constraints, views, indexes, foreign\n-- keys, triggers, row security, policies, grants. Dependencies BETWEEN units\n-- are carried by the order packages/db/chain.json declares.\n-- ============================================================================\n\n-- A LANGUAGE sql function binds its body at CREATE time, and 570 functions\n-- sorted by name are not sorted by who calls whom. This is what lets them load\n-- in any order; every body is still compiled on first call, so a genuinely\n-- broken reference surfaces then rather than never. Session-scoped.\nSET check_function_bodies = false;\n\n\n\n-- ── default privileges: reset ───────────────────────────────────────────\n-- Cleared BEFORE anything is created, so every object below gets the ACL the\n-- dump describes rather than the image's defaults on top of it. Restored at\n-- the end of this file.\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public REVOKE ALL ON SEQUENCES FROM anon, authenticated, service_role, fayz_connector$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public REVOKE ALL ON SEQUENCES FROM anon, authenticated, service_role, fayz_connector$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public REVOKE ALL ON SEQUENCES FROM anon, authenticated, service_role, fayz_connector$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public REVOKE ALL ON SEQUENCES FROM anon, authenticated, service_role, fayz_connector$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public REVOKE ALL ON FUNCTIONS FROM anon, authenticated, service_role, fayz_connector$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public REVOKE ALL ON FUNCTIONS FROM anon, authenticated, service_role, fayz_connector$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public REVOKE ALL ON FUNCTIONS FROM anon, authenticated, service_role, fayz_connector$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public REVOKE ALL ON FUNCTIONS FROM anon, authenticated, service_role, fayz_connector$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public REVOKE ALL ON TABLES FROM anon, authenticated, service_role, fayz_connector$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public REVOKE ALL ON TABLES FROM anon, authenticated, service_role, fayz_connector$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public REVOKE ALL ON TABLES FROM anon, authenticated, service_role, fayz_connector$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public REVOKE ALL ON TABLES FROM anon, authenticated, service_role, fayz_connector$stmt$;\nEND $dp$;\n\n\n-- ── table ───────────────────────────────────────────────────────────────\n\nCREATE TABLE public.plg_conversation_messages (\n id uuid DEFAULT gen_random_uuid() NOT NULL,\n tenant_id uuid NOT NULL,\n conversation_id uuid NOT NULL,\n channel text,\n direction text,\n body text NOT NULL,\n author text,\n at timestamp with time zone DEFAULT now(),\n provider_message_id text,\n delivery_status text,\n sender_kind text,\n sender_label text,\n subject_type text,\n subject_id text,\n CONSTRAINT plg_conversation_messages_channel_check CHECK (((channel IS NULL) OR (channel = ANY (ARRAY['sms'::text, 'whatsapp'::text, 'instagram'::text, 'email'::text, 'webchat'::text])))),\n CONSTRAINT plg_conversation_messages_delivery_status_check CHECK (((delivery_status IS NULL) OR (delivery_status = ANY (ARRAY['queued'::text, 'sent'::text, 'delivered'::text, 'read'::text, 'failed'::text, 'expired'::text, 'delivery_timeout'::text, 'cancelled'::text, 'opted_out'::text])))),\n CONSTRAINT plg_conversation_messages_direction_check CHECK ((direction = ANY (ARRAY['inbound'::text, 'outbound'::text]))),\n CONSTRAINT plg_conversation_messages_sender_kind_check CHECK (((sender_kind IS NULL) OR (sender_kind = ANY (ARRAY['user'::text, 'system'::text, 'ai'::text, 'automation'::text]))))\n);\n\nCREATE TABLE public.plg_conversations (\n id uuid DEFAULT gen_random_uuid() NOT NULL,\n tenant_id uuid NOT NULL,\n contact_name text NOT NULL,\n contact_handle text,\n channel text NOT NULL,\n last_message_preview text,\n last_message_at timestamp with time zone DEFAULT now(),\n unread_count integer DEFAULT 0,\n status text DEFAULT 'open'::text,\n assigned_to text,\n accent text,\n tags text[],\n location text,\n note text,\n created_at timestamp with time zone DEFAULT now() NOT NULL,\n updated_at timestamp with time zone DEFAULT now() NOT NULL,\n contact_person_id uuid,\n CONSTRAINT plg_conversations_channel_check CHECK ((channel = ANY (ARRAY['sms'::text, 'whatsapp'::text, 'instagram'::text, 'email'::text, 'webchat'::text]))),\n CONSTRAINT plg_conversations_status_check CHECK ((status = ANY (ARRAY['open'::text, 'snoozed'::text, 'closed'::text])))\n);\n\nCREATE TABLE public.plg_conversations_channels (\n id uuid DEFAULT gen_random_uuid() NOT NULL,\n tenant_id uuid,\n channel text DEFAULT 'whatsapp'::text NOT NULL,\n provider text NOT NULL,\n provider_number_id text NOT NULL,\n phone_e164 text,\n kind text DEFAULT 'dedicated'::text NOT NULL,\n status text DEFAULT 'requested'::text NOT NULL,\n display_name text,\n metadata jsonb DEFAULT '{}'::jsonb NOT NULL,\n created_at timestamp with time zone DEFAULT now() NOT NULL,\n updated_at timestamp with time zone DEFAULT now() NOT NULL,\n CONSTRAINT plg_conversations_channels_channel_check CHECK ((channel = ANY (ARRAY['sms'::text, 'whatsapp'::text, 'instagram'::text, 'email'::text, 'webchat'::text]))),\n CONSTRAINT plg_conversations_channels_kind_check CHECK ((kind = ANY (ARRAY['fallback'::text, 'dedicated'::text]))),\n CONSTRAINT plg_conversations_channels_status_check CHECK ((status = ANY (ARRAY['requested'::text, 'provisioning'::text, 'provisioned'::text, 'verifying'::text, 'active'::text, 'failed'::text, 'released'::text, 'disconnected'::text])))\n);\n\nCREATE TABLE public.plg_conversations_optouts (\n id uuid DEFAULT gen_random_uuid() NOT NULL,\n channel text DEFAULT 'whatsapp'::text NOT NULL,\n phone_e164 text NOT NULL,\n tenant_id uuid,\n provider text NOT NULL,\n reason text,\n created_at timestamp with time zone DEFAULT now() NOT NULL,\n CONSTRAINT plg_conversations_optouts_channel_check CHECK ((channel = ANY (ARRAY['sms'::text, 'whatsapp'::text, 'instagram'::text, 'email'::text, 'webchat'::text])))\n);\n\nCREATE TABLE public.plg_conversations_payment_requests (\n id uuid DEFAULT gen_random_uuid() NOT NULL,\n tenant_id uuid NOT NULL,\n conversation_id uuid,\n message_id uuid,\n subject_type text,\n subject_id text,\n amount_cents integer NOT NULL,\n currency text DEFAULT 'BRL'::text NOT NULL,\n description text,\n provider text NOT NULL,\n status text DEFAULT 'created'::text NOT NULL,\n provider_payment_id text,\n payment_link_url text,\n pix_copy_paste text,\n link_delivered_at timestamp with time zone,\n settled_notified_at timestamp with time zone,\n settled_at timestamp with time zone,\n created_at timestamp with time zone DEFAULT now() NOT NULL,\n updated_at timestamp with time zone DEFAULT now() NOT NULL,\n CONSTRAINT plg_conversations_payment_requests_amount_cents_check CHECK ((amount_cents > 0)),\n CONSTRAINT plg_conversations_payment_requests_status_check CHECK ((status = ANY (ARRAY['created'::text, 'link_generated'::text, 'approval_requested'::text, 'approved'::text, 'paid'::text, 'failed'::text, 'expired'::text, 'cancelled'::text])))\n);\n\nCREATE TABLE public.plg_conversations_webhook_events (\n id uuid DEFAULT gen_random_uuid() NOT NULL,\n provider text NOT NULL,\n event_id text NOT NULL,\n event_type text NOT NULL,\n received_at timestamp with time zone DEFAULT now() NOT NULL\n);\n\n\n-- ── function ────────────────────────────────────────────────────────────\n\nCREATE FUNCTION public.plg_conversations_match_by_phone(p_channel text, p_last10 text, p_last8 text, p_tenant_id uuid DEFAULT NULL::uuid) RETURNS TABLE(id uuid, tenant_id uuid, contact_name text, contact_handle text, contact_person_id uuid)\n LANGUAGE sql STABLE SECURITY DEFINER\n SET search_path TO 'public'\n AS $$\n WITH candidate AS (\n SELECT c.id, c.tenant_id, c.contact_name, c.contact_handle, c.contact_person_id,\n c.last_message_at,\n regexp_replace(COALESCE(c.contact_handle, ''), '[^0-9]', '', 'g') AS digits\n FROM public.plg_conversations c\n WHERE c.channel = p_channel\n AND (p_tenant_id IS NULL OR c.tenant_id = p_tenant_id)\n )\n SELECT id, tenant_id, contact_name, contact_handle, contact_person_id\n FROM candidate\n WHERE length(digits) >= 8\n AND ((p_last10 IS NOT NULL AND right(digits, 10) = p_last10)\n OR (p_last8 IS NOT NULL AND right(digits, 8) = p_last8))\n -- The 10-digit match wins over an 8-digit one even when the 8-digit thread\n -- is newer: a wrong area code is a different person, and a newer wrong\n -- answer is still wrong.\n ORDER BY (p_last10 IS NOT NULL AND right(digits, 10) = p_last10) DESC,\n last_message_at DESC NULLS LAST\n LIMIT 1;\n$$;\n\n\n-- ── constraint ──────────────────────────────────────────────────────────\n\nALTER TABLE ONLY public.plg_conversation_messages\n ADD CONSTRAINT plg_conversation_messages_pkey PRIMARY KEY (id);\n\nALTER TABLE ONLY public.plg_conversations_channels\n ADD CONSTRAINT plg_conversations_channels_pkey PRIMARY KEY (id);\n\nALTER TABLE ONLY public.plg_conversations_optouts\n ADD CONSTRAINT plg_conversations_optouts_pkey PRIMARY KEY (id);\n\nALTER TABLE ONLY public.plg_conversations_payment_requests\n ADD CONSTRAINT plg_conversations_payment_requests_pkey PRIMARY KEY (id);\n\nALTER TABLE ONLY public.plg_conversations\n ADD CONSTRAINT plg_conversations_pkey PRIMARY KEY (id);\n\nALTER TABLE ONLY public.plg_conversations_webhook_events\n ADD CONSTRAINT plg_conversations_webhook_events_pkey PRIMARY KEY (id);\n\n\n-- ── index ───────────────────────────────────────────────────────────────\n\nCREATE INDEX idx_plg_conversation_messages_by_subject ON public.plg_conversation_messages USING btree (tenant_id, subject_type, subject_id) WHERE (subject_id IS NOT NULL);\n\nCREATE INDEX idx_plg_conversation_messages_subject ON public.plg_conversation_messages USING btree (conversation_id, at DESC) WHERE (subject_id IS NOT NULL);\n\nCREATE INDEX idx_plg_conversation_messages_thread ON public.plg_conversation_messages USING btree (conversation_id, at);\n\nCREATE INDEX idx_plg_conversations_channels_tenant ON public.plg_conversations_channels USING btree (tenant_id, channel, status) WHERE (tenant_id IS NOT NULL);\n\nCREATE INDEX idx_plg_conversations_handle_suffix ON public.plg_conversations USING btree (channel, \"right\"(regexp_replace(COALESCE(contact_handle, ''::text), '[^0-9]'::text, ''::text, 'g'::text), 8), last_message_at DESC);\n\nCREATE INDEX idx_plg_conversations_optouts_lookup ON public.plg_conversations_optouts USING btree (channel, phone_e164);\n\nCREATE INDEX idx_plg_conversations_payment_requests_subject ON public.plg_conversations_payment_requests USING btree (tenant_id, subject_type, subject_id) WHERE (subject_id IS NOT NULL);\n\nCREATE INDEX idx_plg_conversations_payment_requests_tenant ON public.plg_conversations_payment_requests USING btree (tenant_id, created_at DESC);\n\nCREATE INDEX idx_plg_conversations_person ON public.plg_conversations USING btree (tenant_id, contact_person_id) WHERE (contact_person_id IS NOT NULL);\n\nCREATE INDEX idx_plg_conversations_tenant ON public.plg_conversations USING btree (tenant_id);\n\nCREATE INDEX idx_plg_conversations_tenant_recent ON public.plg_conversations USING btree (tenant_id, last_message_at DESC);\n\nCREATE INDEX idx_plg_conversations_webhook_events_received ON public.plg_conversations_webhook_events USING btree (received_at);\n\nCREATE UNIQUE INDEX uq_plg_conversation_messages_provider_message ON public.plg_conversation_messages USING btree (provider_message_id) WHERE (provider_message_id IS NOT NULL);\n\nCREATE UNIQUE INDEX uq_plg_conversations_channels_fallback ON public.plg_conversations_channels USING btree (channel, provider) WHERE ((tenant_id IS NULL) AND (kind = 'fallback'::text));\n\nCREATE UNIQUE INDEX uq_plg_conversations_channels_number ON public.plg_conversations_channels USING btree (provider, provider_number_id);\n\nCREATE UNIQUE INDEX uq_plg_conversations_optouts_contact ON public.plg_conversations_optouts USING btree (channel, phone_e164, COALESCE(tenant_id, '00000000-0000-0000-0000-000000000000'::uuid));\n\nCREATE UNIQUE INDEX uq_plg_conversations_payment_requests_open_subject ON public.plg_conversations_payment_requests USING btree (tenant_id, subject_type, subject_id) WHERE ((subject_id IS NOT NULL) AND (status = ANY (ARRAY['created'::text, 'link_generated'::text, 'approval_requested'::text, 'approved'::text])));\n\nCREATE UNIQUE INDEX uq_plg_conversations_payment_requests_provider ON public.plg_conversations_payment_requests USING btree (provider, provider_payment_id) WHERE (provider_payment_id IS NOT NULL);\n\nCREATE UNIQUE INDEX uq_plg_conversations_webhook_events_event ON public.plg_conversations_webhook_events USING btree (provider, event_id);\n\n\n-- ── fk constraint ───────────────────────────────────────────────────────\n\nALTER TABLE ONLY public.plg_conversation_messages\n ADD CONSTRAINT plg_conversation_messages_conversation_id_fkey FOREIGN KEY (conversation_id) REFERENCES public.plg_conversations(id) ON DELETE CASCADE;\n\nALTER TABLE ONLY public.plg_conversation_messages\n ADD CONSTRAINT plg_conversation_messages_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE;\n\nALTER TABLE ONLY public.plg_conversations_channels\n ADD CONSTRAINT plg_conversations_channels_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE;\n\nALTER TABLE ONLY public.plg_conversations\n ADD CONSTRAINT plg_conversations_contact_person_id_fkey FOREIGN KEY (contact_person_id) REFERENCES public.people(id) ON DELETE SET NULL;\n\nALTER TABLE ONLY public.plg_conversations_optouts\n ADD CONSTRAINT plg_conversations_optouts_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE;\n\nALTER TABLE ONLY public.plg_conversations_payment_requests\n ADD CONSTRAINT plg_conversations_payment_requests_conversation_id_fkey FOREIGN KEY (conversation_id) REFERENCES public.plg_conversations(id) ON DELETE SET NULL;\n\nALTER TABLE ONLY public.plg_conversations_payment_requests\n ADD CONSTRAINT plg_conversations_payment_requests_message_id_fkey FOREIGN KEY (message_id) REFERENCES public.plg_conversation_messages(id) ON DELETE SET NULL;\n\nALTER TABLE ONLY public.plg_conversations_payment_requests\n ADD CONSTRAINT plg_conversations_payment_requests_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE;\n\nALTER TABLE ONLY public.plg_conversations\n ADD CONSTRAINT plg_conversations_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE;\n\n\n-- ── row security ────────────────────────────────────────────────────────\n\nALTER TABLE public.plg_conversation_messages ENABLE ROW LEVEL SECURITY;\n\nALTER TABLE public.plg_conversations ENABLE ROW LEVEL SECURITY;\n\nALTER TABLE public.plg_conversations_channels ENABLE ROW LEVEL SECURITY;\n\nALTER TABLE public.plg_conversations_optouts ENABLE ROW LEVEL SECURITY;\n\nALTER TABLE public.plg_conversations_payment_requests ENABLE ROW LEVEL SECURITY;\n\nALTER TABLE public.plg_conversations_webhook_events ENABLE ROW LEVEL SECURITY;\n\n\n-- ── policy ──────────────────────────────────────────────────────────────\n\nCREATE POLICY plg_conversation_messages_delete ON public.plg_conversation_messages FOR DELETE TO authenticated USING ((tenant_id IN ( SELECT public.user_tenant_ids() AS user_tenant_ids)));\n\nCREATE POLICY plg_conversation_messages_insert ON public.plg_conversation_messages FOR INSERT TO authenticated WITH CHECK ((tenant_id IN ( SELECT public.user_tenant_ids() AS user_tenant_ids)));\n\nCREATE POLICY plg_conversation_messages_select ON public.plg_conversation_messages FOR SELECT TO authenticated USING ((tenant_id IN ( SELECT public.user_tenant_ids() AS user_tenant_ids)));\n\nCREATE POLICY plg_conversation_messages_update ON public.plg_conversation_messages FOR UPDATE TO authenticated USING ((tenant_id IN ( SELECT public.user_tenant_ids() AS user_tenant_ids)));\n\nCREATE POLICY plg_conversations_channels_select ON public.plg_conversations_channels FOR SELECT TO authenticated USING (((tenant_id IS NULL) OR (tenant_id IN ( SELECT public.user_tenant_ids() AS user_tenant_ids))));\n\nCREATE POLICY plg_conversations_delete ON public.plg_conversations FOR DELETE TO authenticated USING ((tenant_id IN ( SELECT public.user_tenant_ids() AS user_tenant_ids)));\n\nCREATE POLICY plg_conversations_insert ON public.plg_conversations FOR INSERT TO authenticated WITH CHECK ((tenant_id IN ( SELECT public.user_tenant_ids() AS user_tenant_ids)));\n\nCREATE POLICY plg_conversations_optouts_select ON public.plg_conversations_optouts FOR SELECT TO authenticated USING (((tenant_id IS NULL) OR (tenant_id IN ( SELECT public.user_tenant_ids() AS user_tenant_ids))));\n\nCREATE POLICY plg_conversations_payment_requests_select ON public.plg_conversations_payment_requests FOR SELECT TO authenticated USING ((tenant_id IN ( SELECT public.user_tenant_ids() AS user_tenant_ids)));\n\nCREATE POLICY plg_conversations_select ON public.plg_conversations FOR SELECT TO authenticated USING ((tenant_id IN ( SELECT public.user_tenant_ids() AS user_tenant_ids)));\n\nCREATE POLICY plg_conversations_update ON public.plg_conversations FOR UPDATE TO authenticated USING ((tenant_id IN ( SELECT public.user_tenant_ids() AS user_tenant_ids)));\n\n\n-- ── acl ─────────────────────────────────────────────────────────────────\n\nREVOKE ALL ON FUNCTION public.plg_conversations_match_by_phone(p_channel text, p_last10 text, p_last8 text, p_tenant_id uuid) FROM PUBLIC;\nGRANT ALL ON FUNCTION public.plg_conversations_match_by_phone(p_channel text, p_last10 text, p_last8 text, p_tenant_id uuid) TO service_role;\n\nGRANT MAINTAIN ON TABLE public.plg_conversation_messages TO anon;\nGRANT ALL ON TABLE public.plg_conversation_messages TO authenticated;\nGRANT ALL ON TABLE public.plg_conversation_messages TO service_role;\n\nGRANT MAINTAIN ON TABLE public.plg_conversations TO anon;\nGRANT ALL ON TABLE public.plg_conversations TO authenticated;\nGRANT ALL ON TABLE public.plg_conversations TO service_role;\n\nGRANT MAINTAIN ON TABLE public.plg_conversations_channels TO anon;\nGRANT ALL ON TABLE public.plg_conversations_channels TO authenticated;\nGRANT ALL ON TABLE public.plg_conversations_channels TO service_role;\n\nGRANT MAINTAIN ON TABLE public.plg_conversations_optouts TO anon;\nGRANT ALL ON TABLE public.plg_conversations_optouts TO authenticated;\nGRANT ALL ON TABLE public.plg_conversations_optouts TO service_role;\n\nGRANT MAINTAIN ON TABLE public.plg_conversations_payment_requests TO anon;\nGRANT ALL ON TABLE public.plg_conversations_payment_requests TO authenticated;\nGRANT ALL ON TABLE public.plg_conversations_payment_requests TO service_role;\n\nGRANT MAINTAIN ON TABLE public.plg_conversations_webhook_events TO anon;\nGRANT ALL ON TABLE public.plg_conversations_webhook_events TO authenticated;\nGRANT ALL ON TABLE public.plg_conversations_webhook_events TO service_role;\n\n\n-- ── default privileges: restore ─────────────────────────────────────────\n-- Put back, so the NEXT thing installed inherits what the chain had.\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO postgres$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO postgres$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO anon$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO anon$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO authenticated$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO authenticated$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO service_role$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO service_role$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO postgres$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO postgres$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO anon$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO anon$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO authenticated$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO authenticated$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO service_role$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO service_role$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO postgres$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO postgres$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO authenticated$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO authenticated$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO service_role$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO service_role$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO postgres$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO postgres$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO anon$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO anon$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO authenticated$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO authenticated$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO service_role$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO service_role$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO postgres$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO postgres$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT MAINTAIN ON TABLES TO anon$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT MAINTAIN ON TABLES TO anon$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO authenticated$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO authenticated$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO service_role$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO service_role$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO postgres$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO postgres$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO anon$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO anon$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO authenticated$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO authenticated$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO service_role$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO service_role$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$--\n-- PostgreSQL database dump complete\n--$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set — %', $stmt$--\n-- PostgreSQL database dump complete\n--$stmt$;\nEND $dp$;\n`\n\nexport const MIGRATION_001_A_CAIXA_SABE_QUEM_FALOU_POR_ULTIMO = `-- ---------------------------------------------------------------------------\n-- 001_a_caixa_sabe_quem_falou_por_ultimo.sql\n--\n-- A conversa guarda o TEXTO e a HORA da última mensagem, e não guarda de quem\n-- ela foi. Sem isso, \"quem está esperando resposta\" — que é a pergunta com que\n-- este produto abre o dia — não é respondível a partir da lista: seria preciso\n-- carregar as mensagens de cada thread para descobrir quem falou por último.\n--\n-- O painel respondia com \\`unread_count > 0\\`, que é a aproximação honesta que o\n-- shape permitia e não é a resposta certa. Ela erra exatamente no caso que mais\n-- dói: a thread que alguém ABRIU, leu, decidiu responder depois e esqueceu. O\n-- não-lida zera na leitura; o cliente continua esperando.\n--\n-- ── Por que TRIGGER e não mais uma coluna que cada escritor preenche ───────\n--\n-- \\`last_message_preview\\` e \\`last_message_at\\` são escritos hoje em CINCO lugares:\n-- o provider do navegador (criar conversa, enviar), a função \\`messaging-send\\` e\n-- o \\`tyxter-webhook\\` (duas vezes, contando o handler de pagamento). Uma sexta\n-- coluna com a mesma disciplina é uma sexta chance de alguém esquecer — e o\n-- modo de falhar é silencioso: a fila simplesmente para de listar alguém.\n--\n-- O gatilho tem UM escritor e não pode ser esquecido pelo próximo chamador. Ele\n-- é deliberadamente estreito: cuida só de \\`last_message_direction\\`. Trazer o\n-- preview e a hora para cá seria a correção certa e é uma mudança maior, com\n-- cinco chamadores para reconciliar — fica anotado, não feito aqui.\n-- ---------------------------------------------------------------------------\n\nALTER TABLE public.plg_conversations\n ADD COLUMN IF NOT EXISTS last_message_direction text;\n\nDO $$\nBEGIN\n ALTER TABLE public.plg_conversations\n ADD CONSTRAINT plg_conversations_last_message_direction_check\n CHECK (last_message_direction IS NULL\n OR last_message_direction = ANY (ARRAY['inbound'::text, 'outbound'::text]));\nEXCEPTION WHEN duplicate_object THEN\n NULL;\nEND $$;\n\nCOMMENT ON COLUMN public.plg_conversations.last_message_direction IS\n 'Quem falou por último: inbound = o cliente, outbound = a casa (001). Mantida por gatilho em plg_conversation_messages, nunca escrita à mão. NULL = thread anterior a esta migration cujo backfill não achou mensagem nenhuma.';\n\n-- ── o único escritor ───────────────────────────────────────────────────────\n--\n-- AFTER INSERT: a mensagem já existe quando a conversa é atualizada, então uma\n-- falha aqui não pode desfazer o recebimento — e recebimento perdido é pior que\n-- fila desatualizada.\n--\n-- Sem cláusula de ordem: a mensagem que acabou de entrar É a última. Comparar\n-- com \\`last_message_at\\` para decidir seria correto e traria uma corrida com os\n-- cinco escritores daquela coluna, que é justamente o problema que o gatilho\n-- existe para não ter.\nCREATE OR REPLACE FUNCTION public.plg_conversations_stamp_direction()\nRETURNS trigger\nLANGUAGE plpgsql\nSECURITY DEFINER\nSET search_path TO ''\nAS $function$\nBEGIN\n IF NEW.direction IS NULL THEN\n RETURN NULL;\n END IF;\n\n UPDATE public.plg_conversations\n SET last_message_direction = NEW.direction\n WHERE id = NEW.conversation_id;\n\n RETURN NULL;\nEND $function$;\n\nCOMMENT ON FUNCTION public.plg_conversations_stamp_direction() IS\n 'Carimba plg_conversations.last_message_direction a cada mensagem inserida (001). O único escritor da coluna.';\n\nDROP TRIGGER IF EXISTS plg_conversation_messages_stamp_direction ON public.plg_conversation_messages;\nCREATE TRIGGER plg_conversation_messages_stamp_direction\n AFTER INSERT ON public.plg_conversation_messages\n FOR EACH ROW\n EXECUTE FUNCTION public.plg_conversations_stamp_direction();\n\n-- ── backfill ───────────────────────────────────────────────────────────────\n--\n-- DISTINCT ON pega uma linha por conversa, a mais recente. \\`at DESC NULLS LAST\\`\n-- e depois \\`id DESC\\` porque \\`at\\` tem default e não NOT NULL: duas mensagens no\n-- mesmo instante (ou ambas sem hora) precisam de um desempate estável, ou o\n-- backfill devolve resultado diferente a cada execução.\nUPDATE public.plg_conversations c\n SET last_message_direction = m.direction\n FROM (\n SELECT DISTINCT ON (conversation_id) conversation_id, direction\n FROM public.plg_conversation_messages\n WHERE direction IS NOT NULL\n ORDER BY conversation_id, at DESC NULLS LAST, id DESC\n ) m\n WHERE m.conversation_id = c.id\n AND c.last_message_direction IS DISTINCT FROM m.direction;\n\n-- Só as threads que interessam à fila, e só quando o cliente falou por último:\n-- é o índice que a pergunta \"quem está esperando\" faz, e ele fica pequeno\n-- porque a maioria das conversas de um tenant está encerrada.\nCREATE INDEX IF NOT EXISTS idx_plg_conversations_waiting\n ON public.plg_conversations (tenant_id, last_message_at DESC)\n WHERE status = 'open' AND last_message_direction = 'inbound';\n`\n\nexport const MIGRATION_002_A_CASA_ESCOLHE_POR_ONDE_O_WHATSAPP_SAI = `-- ---------------------------------------------------------------------------\n-- 002_a_casa_escolhe_por_onde_o_whatsapp_sai.sql — dois provedores de WhatsApp,\n-- a credencial de cada um, e a escolha de qual está valendo.\n--\n-- ── por que a credencial precisa morar aqui ─────────────────────────────\n--\n-- O \\`messaging-send\\` (WP3) pega a chave assim:\n--\n-- const credential = await redeemConnectorCredential({ connectorSlug: 'tyxter', … })\n--\n-- Quinze linhas que chamam a PLATAFORMA Fayz. Funciona para o Tyxter porque a\n-- chave é uma só, da FayaLabs, e a plataforma a serve para qualquer inquilino\n-- do projeto. Não funciona para o Evolution, e não vai funcionar nunca: o\n-- Evolution é auto-hospedado, e cada casa tem a própria URL e a própria chave.\n-- Credencial POR INQUILINO não é atalho, é requisito do segundo provedor.\n--\n-- Um app que não está ligado a projeto Fayz nenhum — como o FullControl hoje —\n-- também não resgata nada. Aqui a chave tem onde ficar nos dois casos.\n--\n-- ── os dois provedores não são a mesma coisa, e a tabela diz isso ───────\n--\n-- \\`tyxter\\` API oficial (Cloud API, via revenda). Iniciar conversa EXIGE\n-- template aprovado pela Meta; texto livre só dentro de 24h da\n-- última mensagem do cliente. Em troca: entrega confiável, sem\n-- risco de banimento, e a Meta responde por número e qualidade.\n-- \\`evolution\\` WhatsApp Web, auto-hospedado. Não tem template nem janela de\n-- 24h — manda o que quiser. Em troca: a sessão morre, o número\n-- pode ser banido, e ninguém responde por isso além de você.\n--\n-- \\`capabilities\\` guarda essa diferença como DADO e não como \\`if\\` espalhado pela\n-- tela. Um botão de \"submeter template\" num provedor que não tem template não\n-- pode existir; a tela lê daqui para desabilitar em vez de fingir.\n--\n-- ── o segredo não passa pelo navegador ──────────────────────────────────\n--\n-- A tabela nega SELECT a \\`authenticated\\`. Quem lê é o \\`service_role\\`, do lado\n-- do servidor. A tela sabe se EXISTE credencial e quais são os campos não\n-- secretos (a URL do Evolution, o nome da instância) por uma view; o segredo\n-- ela nunca vê, nem para reexibir mascarado.\n-- ---------------------------------------------------------------------------\n\n-- ── quem está valendo, por inquilino ───────────────────────────────────────\nCREATE TABLE IF NOT EXISTS public.plg_conversations_providers (\n tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,\n provider text NOT NULL,\n -- Não secreto: a URL base do Evolution, o nome da instância, o id do número.\n -- É isto que a tela mostra e deixa editar.\n config jsonb NOT NULL DEFAULT '{}'::jsonb,\n -- Secreto. Nenhuma política de leitura alcança esta coluna.\n secrets jsonb NOT NULL DEFAULT '{}'::jsonb,\n -- O que este provedor SABE fazer. Lido pela tela para desabilitar o que não\n -- existe, em vez de oferecer um botão que erra.\n capabilities jsonb NOT NULL DEFAULT '{}'::jsonb,\n -- Um só por inquilino fica ativo. O outro continua configurado, pronto para\n -- a troca — que é o pedido: poder alternar sem reconfigurar.\n is_active boolean NOT NULL DEFAULT false,\n status text NOT NULL DEFAULT 'unconfigured',\n status_detail text,\n checked_at timestamptz,\n created_at timestamptz NOT NULL DEFAULT now(),\n updated_at timestamptz NOT NULL DEFAULT now(),\n PRIMARY KEY (tenant_id, provider),\n CONSTRAINT plg_conversations_providers_provider\n CHECK (provider IN ('tyxter', 'evolution')),\n CONSTRAINT plg_conversations_providers_status\n CHECK (status IN ('unconfigured', 'connecting', 'connected', 'error', 'disconnected'))\n);\n\n-- UM ativo por inquilino, garantido pelo banco e não pela tela. Duas telas\n-- abertas em abas diferentes são o caso comum, e \"o último clique vence\" só é\n-- verdade se alguém impedir o empate.\nCREATE UNIQUE INDEX IF NOT EXISTS plg_conversations_providers_one_active\n ON public.plg_conversations_providers (tenant_id)\n WHERE is_active;\n\nALTER TABLE public.plg_conversations_providers ENABLE ROW LEVEL SECURITY;\nALTER TABLE public.plg_conversations_providers FORCE ROW LEVEL SECURITY;\n\n-- Nenhuma política para \\`authenticated\\`: o segredo mora numa coluna desta\n-- tabela, e uma política de SELECT aqui entregaria a chave ao navegador.\n--\n-- E o REVOKE explícito, porque a ausência de política NÃO é a única linha de\n-- defesa. O schema \\`public\\` deste cluster carrega grants amplos de fábrica —\n-- medido nesta própria tabela logo após criá-la: sete privilégios já\n-- concedidos a \\`authenticated\\` sem ninguém ter pedido. A RLS forçada barra\n-- assim mesmo, mas uma política escrita errada num dia ruim é tudo o que\n-- separa a chave do navegador. Duas fechaduras.\nREVOKE ALL ON public.plg_conversations_providers FROM PUBLIC, anon, authenticated;\nGRANT SELECT, INSERT, UPDATE, DELETE ON public.plg_conversations_providers TO service_role;\n\n-- ── o que a tela pode ver ──────────────────────────────────────────────────\n--\n-- Tudo menos o segredo. \\`has_credential\\` responde a única pergunta que a tela\n-- precisa fazer sobre ele: já foi posto?\n-- ── A ÚNICA VIEW DESTE REPO QUE NÃO É \\`security_invoker\\` ──────────────────\n--\n-- A regra da casa é \\`security_invoker = true\\`: a view enxerga o que o leitor\n-- enxerga, e a RLS da tabela continua valendo. Aqui isso se autoderrota.\n--\n-- \\`authenticated\\` NÃO pode ler \\`plg_conversations_providers\\` — é onde o segredo\n-- mora, e o REVOKE logo acima é deliberado. Uma view invoker sobre uma tabela\n-- que o chamador não alcança devolve \\`42501 permission denied\\`, que foi\n-- exatamente o que a tela recebeu no primeiro teste com um JWT de verdade.\n--\n-- Então esta view é definer POR PROJETO, e paga o preço sendo ela mesma a\n-- fronteira: o \\`WHERE tenant_id IN (user_tenant_ids())\\` abaixo não é\n-- decoração, é a RLS desta view. E a coluna do segredo não está na lista do\n-- SELECT — o que ela não seleciona não existe para quem lê.\nDROP VIEW IF EXISTS public.v_conversations_providers;\nCREATE VIEW public.v_conversations_providers AS\n SELECT p.tenant_id,\n p.provider,\n p.config,\n p.capabilities,\n p.is_active,\n p.status,\n p.status_detail,\n p.checked_at,\n -- Nunca o valor. Só se existe.\n (p.secrets ? 'api_key') AS has_credential,\n p.created_at,\n p.updated_at\n FROM public.plg_conversations_providers p\n WHERE p.tenant_id IN (SELECT public.user_tenant_ids());\n\nGRANT SELECT ON public.v_conversations_providers TO authenticated;\n\nCOMMENT ON VIEW public.v_conversations_providers IS\n 'Os provedores de WhatsApp da casa, SEM o segredo. \\`has_credential\\` diz se a chave foi posta; o valor não sai daqui (002).';\n\n-- ── pôr a credencial ───────────────────────────────────────────────────────\n--\n-- SECURITY DEFINER porque a tabela nega escrita a \\`authenticated\\`: é a única\n-- porta, e ela confere o inquilino antes de abrir. \\`search_path\\` vazio porque\n-- uma função definer sem isso é uma função que o chamador pode redirecionar.\nCREATE OR REPLACE FUNCTION public.conversations_set_provider(\n p_tenant uuid,\n p_provider text,\n p_config jsonb DEFAULT '{}'::jsonb,\n p_api_key text DEFAULT NULL,\n p_capabilities jsonb DEFAULT NULL\n) RETURNS void\nLANGUAGE plpgsql SECURITY DEFINER SET search_path TO ''\nAS $$\nDECLARE v_caps jsonb;\nBEGIN\n IF p_tenant IS NULL OR NOT (p_tenant IN (SELECT public.user_tenant_ids())) THEN\n RAISE EXCEPTION 'conversations_set_provider: não é uma casa sua';\n END IF;\n\n -- O que cada provedor sabe fazer é conhecimento do produto, não do usuário.\n -- Quem chama pode sobrescrever (um Evolution atrás de proxy pode ganhar\n -- capacidades), mas o padrão descreve a verdade de cada um.\n v_caps := coalesce(p_capabilities, CASE p_provider\n WHEN 'tyxter' THEN jsonb_build_object(\n 'templates', true, -- e são OBRIGATÓRIOS para iniciar conversa\n 'requires_template', true,\n 'session_window_hours', 24, -- texto livre só dentro dela\n 'delivery_receipts', true,\n 'qr_pairing', false,\n 'ban_risk', 'low')\n WHEN 'evolution' THEN jsonb_build_object(\n 'templates', false, -- não existem: manda texto direto\n 'requires_template', false,\n 'session_window_hours', null,\n 'delivery_receipts', true,\n 'qr_pairing', true, -- parear é ler um QR, não cadastrar na Meta\n 'ban_risk', 'high') -- e é por isso que o aquecimento importa\n ELSE '{}'::jsonb END);\n\n INSERT INTO public.plg_conversations_providers AS t\n (tenant_id, provider, config, capabilities, status,\n secrets)\n VALUES\n (p_tenant, p_provider, coalesce(p_config, '{}'::jsonb), v_caps,\n CASE WHEN p_api_key IS NULL THEN 'unconfigured' ELSE 'connecting' END,\n CASE WHEN p_api_key IS NULL THEN '{}'::jsonb\n ELSE jsonb_build_object('api_key', p_api_key) END)\n ON CONFLICT (tenant_id, provider) DO UPDATE\n SET config = coalesce(p_config, t.config),\n capabilities = v_caps,\n -- Chave nula é \"não mexe\", não \"apaga\". Salvar a URL do Evolution sem\n -- redigitar a chave é o gesto comum, e apagá-la aqui seria a surpresa.\n secrets = CASE WHEN p_api_key IS NULL THEN t.secrets\n ELSE jsonb_set(t.secrets, '{api_key}', to_jsonb(p_api_key)) END,\n status = CASE WHEN p_api_key IS NULL THEN t.status ELSE 'connecting' END,\n updated_at = now();\nEND $$;\n\nREVOKE ALL ON FUNCTION public.conversations_set_provider(uuid, text, jsonb, text, jsonb) FROM PUBLIC, anon;\nGRANT EXECUTE ON FUNCTION public.conversations_set_provider(uuid, text, jsonb, text, jsonb) TO authenticated, service_role;\n\n-- ── trocar qual está valendo ───────────────────────────────────────────────\nCREATE OR REPLACE FUNCTION public.conversations_activate_provider(\n p_tenant uuid, p_provider text\n) RETURNS void\nLANGUAGE plpgsql SECURITY DEFINER SET search_path TO ''\nAS $$\nBEGIN\n IF p_tenant IS NULL OR NOT (p_tenant IN (SELECT public.user_tenant_ids())) THEN\n RAISE EXCEPTION 'conversations_activate_provider: não é uma casa sua';\n END IF;\n\n -- Desligar ANTES de ligar: o índice único parcial recusaria os dois ativos,\n -- e numa transação só a ordem é o que decide entre trocar e falhar.\n UPDATE public.plg_conversations_providers\n SET is_active = false, updated_at = now()\n WHERE tenant_id = p_tenant AND is_active;\n\n UPDATE public.plg_conversations_providers\n SET is_active = true, updated_at = now()\n WHERE tenant_id = p_tenant AND provider = p_provider;\n\n IF NOT FOUND THEN\n RAISE EXCEPTION 'conversations_activate_provider: % não está configurado nesta casa', p_provider;\n END IF;\nEND $$;\n\nREVOKE ALL ON FUNCTION public.conversations_activate_provider(uuid, text) FROM PUBLIC, anon;\nGRANT EXECUTE ON FUNCTION public.conversations_activate_provider(uuid, text) TO authenticated, service_role;\n\n-- ── o canal aprende de que provedor ele é ──────────────────────────────────\n--\n-- \\`plg_conversations_channels.provider\\` já existe e já guarda 'tyxter'. O que\n-- falta é a ligação com a credencial: um número do Evolution precisa saber de\n-- QUAL instância ele saiu, porque a mesma casa pode ter mais de uma.\nALTER TABLE public.plg_conversations_channels\n ADD COLUMN IF NOT EXISTS instance_key text;\n\nCOMMENT ON COLUMN public.plg_conversations_channels.instance_key IS\n 'A instância do provedor de onde este número sai. Nulo no Tyxter (uma conta por projeto); no Evolution é o nome da instância (002).';\n`\n\nexport const MIGRATION_003_A_CAMPANHA_SAI_NO_RITMO_QUE_O_NUMERO_AGUENTA = `-- ---------------------------------------------------------------------------\n-- 003_a_campanha_sai_no_ritmo_que_o_numero_aguenta.sql — template, campanha,\n-- log de envio e o ritmo que impede o número de ser banido.\n--\n-- O desenho não é invenção: saiu de ler Mautic, EspoCRM, Odoo e Twenty, e de\n-- ler a documentação da Meta em vez do folclore que circula sobre ela. Onde os\n-- quatro discordam, este arquivo diz qual escolheu e por quê.\n--\n-- ── três coisas que a Meta documenta e que viram REGRA, não ajuste ──────\n--\n-- · 131050 (o usuário saiu do marketing) NUNCA se repete. Reenviar é outra\n-- infração, não outra tentativa.\n-- · 131048 (bloqueado/marcado como spam) é parada dura. É o alarme de\n-- qualidade, não um erro de rede.\n-- · A janela de 24h só abre por mensagem OU chamada DO CLIENTE. Template\n-- nosso não abre janela nenhuma — e é o erro que mais se vê em CRM.\n--\n-- O resto — a rampa de aquecimento, a faixa de atraso entre mensagens — é\n-- prática de comunidade, não documentação. Vira PADRÃO EDITÁVEL: quem opera o\n-- número sabe mais do que uma tabela escrita hoje.\n--\n-- ── por que o log de envio tem \\`rotation\\` e \\`claim_token\\` ───────────────\n--\n-- \\`rotation\\` é do Mautic: a chave única é (campanha, contato, rotação), e a\n-- rotação sobe quando alguém REINICIA a campanha de propósito. Sem ela, a única\n-- forma de reenviar é apagar o histórico — e aí ninguém consegue provar o que\n-- foi mandado.\n--\n-- \\`claim_token\\` + \\`claim_expires_at\\` é do Twenty: quem vai enviar ARRENDA a\n-- linha por cinco minutos. Duas instâncias do disparador não mandam a mesma\n-- mensagem duas vezes, e um processo que morre no meio devolve a linha sozinho\n-- quando o arrendamento vence. Os quatro CRMs resolvem isso com índice único\n-- mais arrendamento; nenhum usa advisory lock, e nenhum usa outbox.\n-- ---------------------------------------------------------------------------\n\n-- ── quem nunca mais deve ser contatado ─────────────────────────────────────\n--\n-- A tabela já existe e já é chaveada pelo NÚMERO e não pelo contato — que é o\n-- acerto do Odoo e do Twenty, e o erro do Mautic: número é reatribuído, contato\n-- é duplicado, e a supressão precisa sobreviver aos dois.\n--\n-- O que falta é não apagar nunca. O EspoCRM apaga a linha ao reinscrever e\n-- perde a prova de que alguém um dia pediu para sair; o Odoo desliga um\n-- booleano. Seguimos o Odoo.\nALTER TABLE public.plg_conversations_optouts\n ADD COLUMN IF NOT EXISTS active boolean NOT NULL DEFAULT true,\n ADD COLUMN IF NOT EXISTS revoked_at timestamptz,\n ADD COLUMN IF NOT EXISTS source text;\n\nCOMMENT ON COLUMN public.plg_conversations_optouts.active IS\n 'Desligar em vez de apagar: a linha é a prova de que a pessoa pediu para sair, e apagá-la é perder a prova (003).';\n\n-- ── o texto que se manda ───────────────────────────────────────────────────\n--\n-- Um template aqui NÃO é um template da Meta. É o texto da casa, com o espelho\n-- do que o provedor respondeu sobre ele. Assim o mesmo registro serve aos dois:\n-- no Tyxter ele carrega o nome e o status da aprovação; no Evolution, que não\n-- tem o conceito, ele é só o texto — e a campanha não precisa saber a diferença.\nCREATE TABLE IF NOT EXISTS public.plg_conversations_templates (\n id uuid PRIMARY KEY DEFAULT gen_random_uuid(),\n tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,\n name text NOT NULL,\n language text NOT NULL DEFAULT 'pt_BR',\n category text NOT NULL DEFAULT 'MARKETING',\n body text NOT NULL,\n -- Os nomes das variáveis, na ordem em que {{1}}, {{2}} aparecem. A Meta só\n -- entende posição; gente entende nome. Guardar os dois é o que deixa a tela\n -- pedir \"cargo\" em vez de \"variável 3\".\n variables text[] NOT NULL DEFAULT '{}',\n provider text,\n provider_template_id text,\n -- \\`draft\\` enquanto é nosso; depois é o que o provedor respondeu, verbatim.\n status text NOT NULL DEFAULT 'draft',\n status_detail text,\n submitted_at timestamptz,\n approved_at timestamptz,\n created_at timestamptz NOT NULL DEFAULT now(),\n updated_at timestamptz NOT NULL DEFAULT now()\n);\n\nCREATE UNIQUE INDEX IF NOT EXISTS plg_conversations_templates_name_uq\n ON public.plg_conversations_templates (tenant_id, name, language);\n\nALTER TABLE public.plg_conversations_templates ENABLE ROW LEVEL SECURITY;\nALTER TABLE public.plg_conversations_templates FORCE ROW LEVEL SECURITY;\nDROP POLICY IF EXISTS plg_conversations_templates_rw ON public.plg_conversations_templates;\nCREATE POLICY plg_conversations_templates_rw ON public.plg_conversations_templates\n FOR ALL TO authenticated\n USING (tenant_id IN (SELECT public.user_tenant_ids()))\n WITH CHECK (tenant_id IN (SELECT public.user_tenant_ids()));\nGRANT SELECT, INSERT, UPDATE, DELETE ON public.plg_conversations_templates TO authenticated, service_role;\n\n-- ── a campanha ─────────────────────────────────────────────────────────────\nCREATE TABLE IF NOT EXISTS public.plg_conversations_campaigns (\n id uuid PRIMARY KEY DEFAULT gen_random_uuid(),\n tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,\n name text NOT NULL,\n template_id uuid REFERENCES public.plg_conversations_templates(id) ON DELETE SET NULL,\n channel_id uuid REFERENCES public.plg_conversations_channels(id) ON DELETE SET NULL,\n provider text,\n status text NOT NULL DEFAULT 'draft',\n -- O ritmo. Nasce do padrão do provedor e é editável: a rampa é prática de\n -- comunidade, e quem opera o número sabe mais que esta tabela.\n pacing jsonb NOT NULL DEFAULT '{}'::jsonb,\n -- Por que parou, quando parou sozinha. Ver \\`crm\\` nenhum: é o alarme de\n -- qualidade da Meta chegando na tela de quem disparou.\n paused_reason text,\n started_at timestamptz,\n finished_at timestamptz,\n created_by uuid,\n created_at timestamptz NOT NULL DEFAULT now(),\n updated_at timestamptz NOT NULL DEFAULT now(),\n CONSTRAINT plg_conversations_campaigns_status\n CHECK (status IN ('draft', 'scheduled', 'running', 'paused', 'done', 'cancelled'))\n);\n\nALTER TABLE public.plg_conversations_campaigns ENABLE ROW LEVEL SECURITY;\nALTER TABLE public.plg_conversations_campaigns FORCE ROW LEVEL SECURITY;\nDROP POLICY IF EXISTS plg_conversations_campaigns_rw ON public.plg_conversations_campaigns;\nCREATE POLICY plg_conversations_campaigns_rw ON public.plg_conversations_campaigns\n FOR ALL TO authenticated\n USING (tenant_id IN (SELECT public.user_tenant_ids()))\n WITH CHECK (tenant_id IN (SELECT public.user_tenant_ids()));\nGRANT SELECT, INSERT, UPDATE, DELETE ON public.plg_conversations_campaigns TO authenticated, service_role;\n\n-- ── uma linha por destinatário, para sempre ────────────────────────────────\n--\n-- É o log de envio e a fila ao mesmo tempo. Vale para os dois provedores, e é\n-- o único lugar onde se pode PROVAR o que foi mandado, para quem, quando, e\n-- por que alguém NÃO foi contatado.\nCREATE TABLE IF NOT EXISTS public.plg_conversations_campaign_targets (\n id uuid PRIMARY KEY DEFAULT gen_random_uuid(),\n tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,\n campaign_id uuid NOT NULL REFERENCES public.plg_conversations_campaigns(id) ON DELETE CASCADE,\n -- A pessoa do CRM. \\`SET NULL\\` e não \\`CASCADE\\`: apagar a pessoa não pode\n -- apagar a prova de que uma mensagem foi enviada a ela.\n person_id uuid,\n phone_e164 text NOT NULL,\n -- Ver o cabeçalho: sobe quando alguém REINICIA a campanha de propósito.\n rotation integer NOT NULL DEFAULT 1,\n state text NOT NULL DEFAULT 'queued',\n skip_reason text,\n failure_code text,\n failure_detail text,\n -- O arrendamento. Só quem segura o token manda.\n claim_token uuid,\n claim_expires_at timestamptz,\n provider_message_id text,\n variables jsonb NOT NULL DEFAULT '{}'::jsonb,\n queued_at timestamptz NOT NULL DEFAULT now(),\n sent_at timestamptz,\n delivered_at timestamptz,\n read_at timestamptz,\n replied_at timestamptz,\n failed_at timestamptz,\n CONSTRAINT plg_conversations_campaign_targets_state\n CHECK (state IN ('queued', 'sending', 'sent', 'delivered', 'read', 'replied', 'failed', 'skipped')),\n -- O arrendamento existe se e só se está enviando. Duas verificações porque\n -- um token sem prazo é um token que nunca volta.\n CONSTRAINT plg_conversations_campaign_targets_lease\n CHECK (state <> 'sending' OR claim_expires_at IS NOT NULL),\n CONSTRAINT plg_conversations_campaign_targets_lease_pair\n CHECK ((claim_token IS NULL) = (claim_expires_at IS NULL))\n);\n\n-- Uma vez por campanha, por pessoa, por rodada. É esta linha que impede o\n-- disparo duplo — não um \\`if\\` no código do disparador.\nCREATE UNIQUE INDEX IF NOT EXISTS plg_conversations_campaign_targets_once\n ON public.plg_conversations_campaign_targets (campaign_id, phone_e164, rotation);\n\n-- O webhook do provedor chega mais de uma vez, de propósito (o Evolution tenta\n-- 10 vezes em ~50 min). A idempotência do recebimento é este índice.\nCREATE UNIQUE INDEX IF NOT EXISTS plg_conversations_campaign_targets_provider_msg\n ON public.plg_conversations_campaign_targets (tenant_id, provider_message_id)\n WHERE provider_message_id IS NOT NULL;\n\nCREATE INDEX IF NOT EXISTS plg_conversations_campaign_targets_pending\n ON public.plg_conversations_campaign_targets (campaign_id)\n WHERE state IN ('queued', 'sending');\n\n-- O ceifeiro: linhas cujo arrendamento venceu voltam para a fila.\nCREATE INDEX IF NOT EXISTS plg_conversations_campaign_targets_reaper\n ON public.plg_conversations_campaign_targets (claim_expires_at)\n WHERE state = 'sending';\n\nALTER TABLE public.plg_conversations_campaign_targets ENABLE ROW LEVEL SECURITY;\nALTER TABLE public.plg_conversations_campaign_targets FORCE ROW LEVEL SECURITY;\nDROP POLICY IF EXISTS plg_conversations_campaign_targets_rw ON public.plg_conversations_campaign_targets;\nCREATE POLICY plg_conversations_campaign_targets_rw ON public.plg_conversations_campaign_targets\n FOR ALL TO authenticated\n USING (tenant_id IN (SELECT public.user_tenant_ids()))\n WITH CHECK (tenant_id IN (SELECT public.user_tenant_ids()));\nGRANT SELECT, INSERT, UPDATE, DELETE ON public.plg_conversations_campaign_targets TO authenticated, service_role;\n\n-- ── o ritmo que o número aguenta ───────────────────────────────────────────\n--\n-- Mora no canal e não na campanha: o limite é do NÚMERO, e duas campanhas no\n-- mesmo número somam. Guardar na campanha deixaria cada uma achar que tem a\n-- cota inteira.\nALTER TABLE public.plg_conversations_channels\n ADD COLUMN IF NOT EXISTS pacing jsonb NOT NULL DEFAULT '{}'::jsonb,\n ADD COLUMN IF NOT EXISTS warmup_day integer,\n ADD COLUMN IF NOT EXISTS warmup_started_at timestamptz,\n ADD COLUMN IF NOT EXISTS quality text,\n ADD COLUMN IF NOT EXISTS quality_at timestamptz;\n\nCOMMENT ON COLUMN public.plg_conversations_channels.pacing IS\n 'Ritmo deste número: por minuto, por hora, por dia, faixa de atraso e janela de horário. Padrão por provedor, editável — a rampa é prática de comunidade, não documentação da Meta (003).';\n\n-- Os padrões, por provedor. Números da pesquisa; o comentário diz a\n-- procedência de cada um, porque \"por que 20?\" é a pergunta que sempre volta.\nCREATE OR REPLACE FUNCTION public.conversations_default_pacing(p_provider text)\nRETURNS jsonb LANGUAGE sql IMMUTABLE AS $$\n SELECT CASE p_provider\n -- Oficial. O teto real é o tier da Meta (250/2.000/10.000/100.000), que a\n -- própria API informa por número — então aqui só entra o que a Meta NÃO\n -- controla: o ritmo instantâneo. 1 msg / 6 s para o MESMO destinatário é\n -- documentado; 60/min é 75% do teto de 80/s por segurança.\n WHEN 'tyxter' THEN jsonb_build_object(\n 'per_minute', 60, 'per_hour', 1000, 'per_day', null,\n 'delay_seconds', jsonb_build_array(1, 3),\n 'same_recipient_seconds', 6,\n 'hours', jsonb_build_object('start', '09:00', 'end', '18:00', 'weekdays_only', true))\n -- Não oficial. Aqui não há teto do provedor — há banimento. Os números são\n -- a moda de treze rampas brasileiras publicadas: 10-30/dia na primeira\n -- semana, 200-300/dia no regime. Atraso ALEATÓRIO entre 15 e 45 s porque\n -- intervalo fixo é assinatura de robô.\n WHEN 'evolution' THEN jsonb_build_object(\n 'per_minute', 12, 'per_hour', 300, 'per_day', 250,\n 'delay_seconds', jsonb_build_array(15, 45),\n 'same_recipient_seconds', 6,\n 'batch_size', 50, 'batch_cooldown_minutes', 12,\n 'hours', jsonb_build_object('start', '09:00', 'end', '18:00', 'weekdays_only', true))\n ELSE '{}'::jsonb END\n$$;\n\nCOMMENT ON FUNCTION public.conversations_default_pacing(text) IS\n 'O ritmo inicial de cada provedor. Tyxter: o teto é da Meta, aqui só o ritmo instantâneo. Evolution: não há teto, há banimento (003).';\n\n-- A rampa de aquecimento, em dias. Prática de comunidade, editável — e a\n-- função existe para que o padrão seja UM lugar e não uma constante copiada.\nCREATE OR REPLACE FUNCTION public.conversations_warmup_allowance(p_day integer)\nRETURNS integer LANGUAGE sql IMMUTABLE AS $$\n SELECT CASE\n WHEN p_day IS NULL THEN NULL -- número sem aquecimento declarado: sem teto extra\n WHEN p_day <= 3 THEN 20\n WHEN p_day <= 7 THEN 50\n WHEN p_day <= 14 THEN 100\n WHEN p_day <= 21 THEN 200\n ELSE 300\n END\n$$;\n\nCOMMENT ON FUNCTION public.conversations_warmup_allowance(integer) IS\n 'Quantos destinatários NOVOS por dia, por dia de aquecimento. Moda de treze rampas brasileiras publicadas — comunidade, não Meta. Padrão editável (003).';\n\n-- ── o que a tela do disparo precisa ver ────────────────────────────────────\nDROP VIEW IF EXISTS public.v_conversations_campaign_progress;\nCREATE VIEW public.v_conversations_campaign_progress\nWITH (security_invoker = true) AS\n SELECT c.id AS campaign_id,\n c.tenant_id,\n c.name,\n c.status,\n c.paused_reason,\n c.provider,\n count(t.id) AS total,\n count(*) FILTER (WHERE t.state = 'queued') AS queued,\n count(*) FILTER (WHERE t.state = 'sending') AS sending,\n count(*) FILTER (WHERE t.state IN ('sent','delivered','read','replied')) AS sent,\n count(*) FILTER (WHERE t.state = 'delivered') AS delivered,\n count(*) FILTER (WHERE t.state = 'read') AS read,\n count(*) FILTER (WHERE t.state = 'replied') AS replied,\n count(*) FILTER (WHERE t.state = 'failed') AS failed,\n count(*) FILTER (WHERE t.state = 'skipped') AS skipped,\n -- A taxa de resposta é o sinal que decide se o número sobrevive: a\n -- prática diz que abaixo de 30% o risco de banimento sobe. Fica na\n -- view porque é o número que precisa estar na tela, não num relatório.\n CASE WHEN count(*) FILTER (WHERE t.state IN ('sent','delivered','read','replied')) > 0\n THEN round(100.0 * count(*) FILTER (WHERE t.state = 'replied')\n / count(*) FILTER (WHERE t.state IN ('sent','delivered','read','replied')), 1)\n END AS reply_rate,\n min(t.sent_at) AS first_sent_at,\n max(t.sent_at) AS last_sent_at\n FROM public.plg_conversations_campaigns c\n LEFT JOIN public.plg_conversations_campaign_targets t ON t.campaign_id = c.id\n WHERE c.tenant_id IN (SELECT public.user_tenant_ids())\n GROUP BY c.id, c.tenant_id, c.name, c.status, c.paused_reason, c.provider;\n\nGRANT SELECT ON public.v_conversations_campaign_progress TO authenticated;\n\n-- ── arrendar o próximo lote ────────────────────────────────────────────────\n--\n-- \\`FOR UPDATE SKIP LOCKED\\` é o que deixa dois disparadores trabalharem a mesma\n-- campanha sem pisar um no outro, e sem advisory lock. O arrendamento de cinco\n-- minutos é o que devolve a linha quando um deles morre no meio.\n--\n-- A supressão é conferida AQUI, na mesma transação que arrenda. Conferir antes\n-- e mandar depois deixa uma janela em que alguém pede para sair e recebe assim\n-- mesmo — e é justamente essa mensagem que vira denúncia.\nCREATE OR REPLACE FUNCTION public.conversations_claim_targets(\n p_campaign uuid, p_limit integer DEFAULT 10\n) RETURNS TABLE (\n id uuid, phone_e164 text, person_id uuid, variables jsonb, claim_token uuid\n)\nLANGUAGE plpgsql SECURITY DEFINER SET search_path TO ''\nAS $$\nDECLARE v_token uuid := gen_random_uuid(); v_tenant uuid;\nBEGIN\n SELECT c.tenant_id INTO v_tenant\n FROM public.plg_conversations_campaigns c WHERE c.id = p_campaign;\n IF v_tenant IS NULL THEN RAISE EXCEPTION 'campanha não encontrada'; END IF;\n\n -- TRÊS COMANDOS, E NÃO UM. A primeira versão fazia o ceifeiro, a supressão e\n -- o arrendamento em CTEs do mesmo UPDATE, e isso é uma armadilha: dentro de\n -- um comando só, todas as CTEs enxergam o MESMO instantâneo da tabela, e uma\n -- linha atualizada por duas delas recebe só a primeira — em silêncio. O\n -- suprimido vinha marcado E arrendado, e qual dos dois vencia dependia do\n -- plano que o Postgres escolhesse naquele dia.\n --\n -- Separado, cada passo enxerga o resultado do anterior. É mais lento por um\n -- par de milissegundos e é a diferença entre uma regra e uma coincidência.\n\n -- 1. O ceifeiro. Quem venceu o arrendamento volta para a fila, senão uma\n -- campanha inteira fica presa num processo que morreu.\n UPDATE public.plg_conversations_campaign_targets t\n SET state = 'queued', claim_token = NULL, claim_expires_at = NULL\n WHERE t.campaign_id = p_campaign AND t.state = 'sending'\n AND t.claim_expires_at < now();\n\n -- 2. Quem pediu para sair NUNCA entra num lote. A linha fica marcada em vez\n -- de sumir: é assim que se prova que a decisão foi deliberada.\n UPDATE public.plg_conversations_campaign_targets t\n SET state = 'skipped', skip_reason = 'suppressed'\n WHERE t.campaign_id = p_campaign AND t.state = 'queued'\n AND EXISTS (SELECT 1 FROM public.plg_conversations_optouts o\n WHERE o.tenant_id = t.tenant_id\n AND o.phone_e164 = t.phone_e164\n AND o.active);\n\n -- 3. Só então arrenda. \\`SKIP LOCKED\\` deixa dois disparadores trabalharem a\n -- mesma campanha sem pisar um no outro, e sem advisory lock.\n RETURN QUERY\n WITH escolhidos AS (\n SELECT t.id FROM public.plg_conversations_campaign_targets t\n WHERE t.campaign_id = p_campaign AND t.state = 'queued'\n ORDER BY t.queued_at\n LIMIT greatest(1, least(coalesce(p_limit, 10), 100))\n FOR UPDATE SKIP LOCKED\n )\n UPDATE public.plg_conversations_campaign_targets t\n SET state = 'sending', claim_token = v_token,\n claim_expires_at = now() + interval '5 minutes'\n FROM escolhidos e\n WHERE t.id = e.id\n RETURNING t.id, t.phone_e164, t.person_id, t.variables, t.claim_token;\nEND $$;\n\nREVOKE ALL ON FUNCTION public.conversations_claim_targets(uuid, integer) FROM PUBLIC, anon;\nGRANT EXECUTE ON FUNCTION public.conversations_claim_targets(uuid, integer) TO service_role;\n`\n\nexport const MIGRATION_004_A_TELA_SABE_SE_PODE_FALAR_ANTES_DE_DEIXAR_ESCREVER = `-- ---------------------------------------------------------------------------\n-- 004_a_tela_sabe_se_pode_falar_antes_de_deixar_escrever.sql — a janela de 24h,\n-- como DADO, para a tela responder antes de alguém digitar.\n--\n-- ── o defeito que isto conserta ─────────────────────────────────────────\n--\n-- Na API oficial do WhatsApp, texto livre só sai dentro de 24h contadas da\n-- última mensagem DO CLIENTE. Fora dela a Meta recusa, e a recusa chega como um\n-- código no log — não na tela de quem escreveu. O atendente digita, aperta\n-- enviar, vê a mensagem aparecer na conversa, e ela nunca chega.\n--\n-- Isso é pior que um erro: é uma tela que mente. E some inteiro num provedor\n-- não oficial, onde não existe janela nenhuma — então o MESMO campo de texto\n-- funciona ou falha calado dependendo de qual provedor está ativo.\n--\n-- A correção não é um aviso depois. É a tela saber ANTES: janela aberta, texto\n-- livre; janela fechada, só template, e o campo diz isso em vez de aceitar.\n--\n-- ── quem abre a janela ──────────────────────────────────────────────────\n--\n-- O CLIENTE, e só ele. Mensagem nossa não abre nada — nem template aprovado.\n-- É o erro mais comum de quem implementa isso, e é por isso que o cálculo\n-- abaixo olha exclusivamente \\`direction = 'inbound'\\`.\n--\n-- (Origem: developers.facebook.com/documentation/business-messaging/whatsapp —\n-- \"The user messages you about the product. This opens a 24 hour customer\n-- service window.\" Cliques em anúncio Click-to-WhatsApp abrem 72h; não\n-- distinguimos ainda, e 24h é o limite conservador dos dois.)\n-- ---------------------------------------------------------------------------\n\nDROP VIEW IF EXISTS public.v_conversations_window;\nCREATE VIEW public.v_conversations_window\nWITH (security_invoker = true) AS\n SELECT c.id AS conversation_id,\n c.tenant_id,\n c.channel,\n c.contact_handle,\n c.contact_person_id,\n u.last_inbound_at,\n -- Quando fecha. Nulo = nunca abriu, que é o caso de todo lead que\n -- ainda não respondeu — a maioria de uma lista de feira.\n (u.last_inbound_at + interval '24 hours') AS window_expires_at,\n (u.last_inbound_at IS NOT NULL\n AND u.last_inbound_at > now() - interval '24 hours') AS window_open,\n -- Quanto falta, em minutos. A tela mostra \"faltam 3h\" e não um\n -- carimbo de data: quem está respondendo quer saber se dá tempo.\n CASE WHEN u.last_inbound_at IS NOT NULL\n THEN greatest(0, floor(extract(epoch FROM\n (u.last_inbound_at + interval '24 hours') - now()) / 60))::int\n END AS minutes_left,\n -- Quem pediu para não ser mais contatado. Some da tela antes de\n -- qualquer janela: não importa se está aberta.\n EXISTS (SELECT 1 FROM public.plg_conversations_optouts o\n WHERE o.tenant_id = c.tenant_id\n AND o.phone_e164 = regexp_replace(coalesce(c.contact_handle,''), '\\\\D', '', 'g')\n AND o.active) AS opted_out\n FROM public.plg_conversations c\n LEFT JOIN LATERAL (\n SELECT max(m.at) AS last_inbound_at\n FROM public.plg_conversation_messages m\n WHERE m.conversation_id = c.id\n AND m.direction = 'inbound'\n ) u ON true\n WHERE c.tenant_id IN (SELECT public.user_tenant_ids());\n\nGRANT SELECT ON public.v_conversations_window TO authenticated;\n\nCOMMENT ON VIEW public.v_conversations_window IS\n 'Se dá para mandar texto livre para esta conversa agora. A janela de 24h só abre por mensagem DO CLIENTE — template nosso não abre nada (004).';\n\n-- O índice que a LATERAL usa. Sem ele, cada linha da caixa varre as mensagens\n-- da conversa inteira, e a caixa é justamente a tela que abre o dia todo.\nCREATE INDEX IF NOT EXISTS plg_conversation_messages_inbound_at\n ON public.plg_conversation_messages (conversation_id, at DESC)\n WHERE direction = 'inbound';\n`\n\nexport const MIGRATION_005_A_CONVERSA_CONTA_AO_FUNIL_QUE_HOUVE_CONTATO = `-- ---------------------------------------------------------------------------\n-- 005_a_conversa_conta_ao_funil_que_houve_contato.sql — a ponte que faltava.\n--\n-- ── o buraco, medido ────────────────────────────────────────────────────\n--\n-- No cluster: 8 conversas, 42 mensagens trocadas — incluindo respostas de\n-- gente de verdade — e 315 negócios TODOS parados em \"Novo (feira)\", com\n-- \\`contact_status = 'not_contacted'\\`. Zero atividades vindas da caixa.\n--\n-- Não era defeito: não existia ligação nenhuma. Nenhum \\`INSERT\\` em\n-- \\`plg_crm_activities\\` fora do plugin-crm, nenhuma fonte de linha do tempo\n-- registrada. A caixa de entrada e o funil olhavam para as mesmas pessoas e\n-- não se falavam.\n--\n-- O maquinário do CRM já estava pronto e correto: \\`crm_recompute_deal_status\\`\n-- (crm/020) lê as atividades, pega o ÚLTIMO \\`outcome\\` e deriva o status; o\n-- gatilho \\`crm_activity_restatus\\` dispara a cada atividade inserida. Faltava\n-- alguém inserir.\n--\n-- ── por que o \\`outcome\\` é o que é ───────────────────────────────────────\n--\n-- mensagem RECEBIDA → \\`replied\\` o lead respondeu. É o fato mais forte\n-- que a caixa produz, e o que mais move\n-- o status (\"Respondeu\").\n-- mensagem ENVIADA → \\`left_message\\` deixamos recado. Mandar NÃO é ser\n-- lido: tratar envio como contato feito\n-- encheria o funil de \"conectado\" sem\n-- ninguém do outro lado. A 020 mapeia\n-- este valor para \"Não atendeu\", que é\n-- literalmente o estado — ainda sem\n-- resposta.\n--\n-- O vocabulário é FECHADO por CHECK (crm/020:49): \\`connected, no_answer,\n-- left_message, replied, scheduled\\`. Conferido antes de escrever — a primeira\n-- versão desta migration usava \\`attempted\\`, que a regra da 020 nomeia mas a\n-- restrição da coluna NÃO aceita, e teria falhado em toda mensagem enviada.\n--\n-- ── e o \\`deal_id\\` não é opcional ────────────────────────────────────────\n--\n-- \\`crm_recompute_deal_status\\` agrega \\`WHERE a.deal_id = p_deal_id\\`. Uma\n-- atividade só com \\`lead_id\\` não entra na conta e não move status nenhum —\n-- por isso o gatilho procura o negócio do lead e o carimba.\n--\n-- ── uma atividade por CONVERSA E POR DIA, não por mensagem ──────────────\n--\n-- Uma conversa de trinta mensagens não são trinta contatos: é um. Registrar\n-- cada mensagem encheria a linha do tempo de ruído e afogaria o que importa —\n-- a ligação, a visita, a proposta. Então a primeira mensagem do dia abre a\n-- atividade e as seguintes ATUALIZAM a mesma linha (o corpo mais recente, e o\n-- \\`outcome\\` mais forte do dia).\n--\n-- Atualizar também dispara o \\`crm_activity_restatus\\` (o gatilho é INSERT OR\n-- UPDATE), então uma resposta que chega depois de um envio move o status no\n-- mesmo dia, que é o comportamento que se quer.\n--\n-- ── e se o contato não for um lead ──────────────────────────────────────\n--\n-- \\`contact_person_id\\` é nulo enquanto ninguém casou o telefone com uma pessoa.\n-- Aí não há a quem pendurar o fato e a função sai calada. Não inventa lead: a\n-- caixa recebe mensagem de entregador, de banco e de engano, e cada um deles\n-- viraria um card no funil de alguém.\n-- ---------------------------------------------------------------------------\n\nCREATE OR REPLACE FUNCTION public.conversations_log_crm_activity() RETURNS trigger\n LANGUAGE plpgsql SECURITY DEFINER\n SET search_path TO ''\n AS $$\nDECLARE\n v_tenant uuid;\n v_person uuid;\n v_canal text;\n v_deal uuid;\n v_outcome text;\n v_titulo text;\n v_corpo text;\n v_dia date := (NEW.at AT TIME ZONE 'UTC')::date;\n v_ativ uuid;\nBEGIN\n -- Sem CRM montado não há o que escrever. \\`to_regclass\\` em vez de referência\n -- direta porque conversas é plugin OPCIONAL e pode viver num cluster que não\n -- tem funil nenhum.\n IF to_regclass('public.plg_crm_activities') IS NULL THEN RETURN NULL; END IF;\n\n SELECT c.tenant_id, c.contact_person_id, c.channel\n INTO v_tenant, v_person, v_canal\n FROM public.plg_conversations c WHERE c.id = NEW.conversation_id;\n\n IF v_person IS NULL THEN RETURN NULL; END IF;\n\n SELECT d.id INTO v_deal\n FROM public.plg_crm_deals d\n WHERE d.tenant_id = v_tenant AND d.lead_id = v_person\n ORDER BY d.created_at DESC LIMIT 1;\n\n IF NEW.direction = 'inbound' THEN\n v_outcome := 'replied';\n v_titulo := 'Respondeu no ' || coalesce(v_canal, 'canal');\n ELSE\n v_outcome := 'left_message';\n v_titulo := 'Mensagem enviada no ' || coalesce(v_canal, 'canal');\n END IF;\n\n v_corpo := left(coalesce(NEW.body, ''), 2000);\n\n -- A atividade do dia, se já houver.\n SELECT a.id INTO v_ativ\n FROM public.plg_crm_activities a\n WHERE a.tenant_id = v_tenant\n AND a.lead_id = v_person\n AND a.activity_type = 'whatsapp'\n AND (a.completed_at AT TIME ZONE 'UTC')::date = v_dia\n ORDER BY a.completed_at DESC LIMIT 1;\n\n IF v_ativ IS NULL THEN\n INSERT INTO public.plg_crm_activities (\n tenant_id, deal_id, lead_id, contact_id, activity_type,\n title, description, outcome, completed_at, created_by_name\n ) VALUES (\n v_tenant, v_deal, v_person, v_person, 'whatsapp',\n v_titulo, v_corpo, v_outcome, NEW.at,\n -- Quem fez foi a caixa, não uma pessoa: o autor em uuid seria mentira,\n -- e \"(sistema)\" é a resposta honesta para quem lê a linha do tempo.\n 'Caixa de entrada'\n );\n ELSE\n UPDATE public.plg_crm_activities a SET\n -- \\`replied\\` NUNCA é rebaixado por um envio posterior: o lead ter\n -- respondido é o fato do dia, e mandar outra mensagem depois não o\n -- desfaz.\n outcome = CASE WHEN a.outcome = 'replied' THEN 'replied' ELSE v_outcome END,\n title = CASE WHEN a.outcome = 'replied' THEN a.title ELSE v_titulo END,\n description = v_corpo,\n completed_at = NEW.at,\n deal_id = coalesce(a.deal_id, v_deal)\n WHERE a.id = v_ativ;\n END IF;\n\n RETURN NULL;\nEND $$;\n\nREVOKE ALL ON FUNCTION public.conversations_log_crm_activity() FROM PUBLIC;\nGRANT EXECUTE ON FUNCTION public.conversations_log_crm_activity() TO authenticated, service_role;\n\nCOMMENT ON FUNCTION public.conversations_log_crm_activity() IS\n 'Uma mensagem vira contato na linha do tempo do lead e do negócio, e o status do card deriva disso pelo gatilho da crm/020. Uma atividade por conversa por dia (005).';\n\n-- AFTER: falhar ao registrar no CRM não pode recusar uma mensagem que já\n-- chegou do provedor. A caixa é a fonte; o funil é quem escuta.\nDROP TRIGGER IF EXISTS conversations_log_crm_activity ON public.plg_conversation_messages;\nCREATE TRIGGER conversations_log_crm_activity\n AFTER INSERT ON public.plg_conversation_messages\n FOR EACH ROW EXECUTE FUNCTION public.conversations_log_crm_activity();\n\n-- O índice que a busca da atividade do dia usa. Sem ele, cada mensagem varre\n-- as atividades do inquilino inteiro — e mensagem é o que mais chega aqui.\nDO $$\nBEGIN\n -- Guarda pela mesma razão da função: conversas é opcional e pode viver sem\n -- CRM. Um índice sobre tabela que não existe recusa o arquivo inteiro.\n IF to_regclass('public.plg_crm_activities') IS NULL THEN RETURN; END IF;\n CREATE INDEX IF NOT EXISTS plg_crm_activities_lead_day\n ON public.plg_crm_activities (tenant_id, lead_id, activity_type, completed_at DESC);\nEND $$;\n`\n\nexport const MIGRATIONS: Array<{ id: string; sql: string }> = [\n { id: \"000_baseline\", sql: MIGRATION_000_BASELINE },\n { id: \"001_a_caixa_sabe_quem_falou_por_ultimo\", sql: MIGRATION_001_A_CAIXA_SABE_QUEM_FALOU_POR_ULTIMO },\n { id: \"002_a_casa_escolhe_por_onde_o_whatsapp_sai\", sql: MIGRATION_002_A_CASA_ESCOLHE_POR_ONDE_O_WHATSAPP_SAI },\n { id: \"003_a_campanha_sai_no_ritmo_que_o_numero_aguenta\", sql: MIGRATION_003_A_CAMPANHA_SAI_NO_RITMO_QUE_O_NUMERO_AGUENTA },\n { id: \"004_a_tela_sabe_se_pode_falar_antes_de_deixar_escrever\", sql: MIGRATION_004_A_TELA_SABE_SE_PODE_FALAR_ANTES_DE_DEIXAR_ESCREVER },\n { id: \"005_a_conversa_conta_ao_funil_que_houve_contato\", sql: MIGRATION_005_A_CONVERSA_CONTA_AO_FUNIL_QUE_HOUVE_CONTATO },\n]\n","import React from 'react'\nimport { CampaignsView } from './views/CampaignsView'\nimport { TemplatesView } from './views/TemplatesView'\nimport type { EntityDef, PluginManifest, PluginScope, VerticalId } from '@fayz-ai/core'\nimport { getActiveTenantId, getSupabaseClientOptional, registerTranslations } from '@fayz-ai/core'\nimport type { EntityLookup } from '@fayz-ai/admin'\nimport { ConversationsPage } from './views/ConversationsPage'\nimport type { ResolvedConversationsConfig } from './context'\nimport type { ConversationsProvider } from './data/types'\nimport { createMockConversationsProvider } from './data/mock'\nimport { createSupabaseConversationsProvider } from './data/supabase'\nimport { createConversationsStore } from './store'\nimport { conversationsLocales } from './locales'\nimport { createConversationsDashboardWidgets } from './views/dashboardWidgets'\nimport { ConversationsSettingsTab } from './settings/ConversationsSettings'\nimport { buildConversationsOnboarding } from './lib/onboarding'\nimport { tyxterConnectorDef } from './integrations/tyxter/connectorDef'\nimport { MIGRATIONS } from './migrations'\n\n// ---------------------------------------------------------------------------\n// @fayz-ai/plugin-conversations — the GoHighLevel \"Conversations\" equivalent:\n// one omni-channel inbox (SMS / WhatsApp / Instagram / Email / Web chat).\n// Universal plugin — reusable by any vertical (beauty, resto, agency…).\n//\n// Supabase-backed (plg_conversations / plg_conversation_messages, tenant-scoped\n// via RLS) when a client is registered; a persisted mock inbox otherwise.\n// WhatsApp arrives through the Tyxter connector declared below; the other\n// channels still deliver inbound rows out-of-band, and this plugin stays the\n// read/compose surface for all of them.\n// ---------------------------------------------------------------------------\n\nexport interface ConversationsPluginOptions {\n navPosition?: number\n navSection?: 'main' | 'secondary' | 'settings'\n navLabel?: string\n scope?: PluginScope\n verticalId?: VerticalId\n dataProvider?: ConversationsProvider\n\n /**\n * `people.kind` for contacts created from the compose modal's picker. Each\n * vertical names its people differently (beauty 'client', agency 'lead'),\n * hence a knob rather than a hardcoded value. Default: 'contact'.\n */\n contactKind?: string\n /** Custom search source for the picker. Defaults to the person archetype lookup. */\n contactLookup?: EntityLookup\n /**\n * The app's contact entity, same definition its registry page uses. \"Create\"\n * in the picker opens THAT form. Without it the picker searches only.\n */\n contactEntityDef?: EntityDef\n}\n\nfunction createSafeProvider(): ConversationsProvider {\n // Real data when a Supabase client is registered; mock inbox otherwise.\n // Resolution is LAZY (per call): createConversationsPlugin runs at module\n // scope in the app config, BEFORE the Supabase client is registered — an\n // eager check here would capture the mock forever even on live backends.\n let real: ConversationsProvider | null = null\n let mock: ConversationsProvider | null = null\n const resolve = (): ConversationsProvider => {\n if (getSupabaseClientOptional()) {\n real ??= createSupabaseConversationsProvider({ tenantId: () => getActiveTenantId() })\n return real\n }\n mock ??= createMockConversationsProvider({ tenantId: () => getActiveTenantId() })\n return mock\n }\n return {\n listConversations: (...a) => resolve().listConversations(...a),\n getMessages: (...a) => resolve().getMessages(...a),\n sendMessage: (...a) => resolve().sendMessage(...a),\n markRead: (...a) => resolve().markRead(...a),\n setStatus: (...a) => resolve().setStatus(...a),\n createConversation: (...a) => resolve().createConversation(...a),\n }\n}\n\nexport function createConversationsPlugin(options?: ConversationsPluginOptions): PluginManifest {\n registerTranslations(conversationsLocales)\n const provider = options?.dataProvider ?? createSafeProvider()\n const store = createConversationsStore(provider)\n\n const config: ResolvedConversationsConfig = {\n contactKind: options?.contactKind ?? 'contact',\n contactLookup: options?.contactLookup,\n contactEntityDef: options?.contactEntityDef,\n }\n\n const dashboardWidgets = createConversationsDashboardWidgets({ store, config })\n\n const PageComponent: React.ComponentType<unknown> = () =>\n React.createElement(ConversationsPage, { store, config })\n PageComponent.displayName = 'ConversationsPage'\n\n // As campanhas moram numa rota irmã e não numa aba da caixa: a caixa é o\n // trabalho de quem atende e a campanha é o de quem dispara — duas pessoas,\n // dois momentos. Empilhar as duas faria a caixa carregar contadores que\n // ninguém naquele momento está olhando.\n const CampaignsPageComponent: React.ComponentType<unknown> = () =>\n React.createElement(CampaignsView)\n CampaignsPageComponent.displayName = 'ConversationsCampaignsPage'\n\n const TemplatesPageComponent: React.ComponentType<unknown> = () =>\n React.createElement(TemplatesView)\n TemplatesPageComponent.displayName = 'ConversationsTemplatesPage'\n\n return {\n id: 'conversations',\n defaultAgentRole: 'frontdesk',\n name: options?.navLabel ?? 'Conversations',\n icon: 'MessageCircle',\n version: '1.0.0',\n scope: options?.scope ?? 'universal',\n verticalId: options?.verticalId,\n defaultEnabled: true,\n dependencies: [],\n declaredFeatures: [{ id: 'conversations', label: 'Conversations', group: 'Engage' }],\n // Recurring monthly quota — counts conversation threads created this month.\n declaredLimits: [\n { key: 'conversations_month', label: 'Conversations this month', table: 'plg_conversations', period: 'month' },\n ],\n navigation: [\n {\n section: options?.navSection ?? 'main',\n position: options?.navPosition ?? 1,\n label: options?.navLabel ?? 'Conversations',\n route: '/conversations',\n icon: 'MessageCircle',\n permission: { feature: 'conversations', action: 'read' as const },\n },\n {\n section: options?.navSection ?? 'main',\n position: (options?.navPosition ?? 1) + 1,\n label: 'Campanhas',\n route: '/conversations/campaigns',\n icon: 'Send',\n permission: { feature: 'conversations', action: 'read' as const },\n },\n {\n section: options?.navSection ?? 'main',\n position: (options?.navPosition ?? 1) + 2,\n label: 'Templates',\n route: '/conversations/templates',\n icon: 'FileText',\n permission: { feature: 'conversations', action: 'read' as const },\n },\n ],\n routes: [\n {\n path: '/conversations/templates',\n component: TemplatesPageComponent,\n title: 'Templates',\n parentRoute: '/conversations',\n permission: { feature: 'conversations', action: 'read' as const },\n },\n {\n // Antes da rota-pai: a shell ordena por especificidade, mas declarar\n // a filha primeiro é o que deixa isso legível para quem ler depois.\n path: '/conversations/campaigns',\n component: CampaignsPageComponent,\n title: 'Campanhas',\n parentRoute: '/conversations',\n permission: { feature: 'conversations', action: 'read' as const },\n },\n {\n path: '/conversations',\n component: PageComponent,\n fullBleed: true,\n permission: { feature: 'conversations', action: 'read' as const },\n },\n ],\n widgets: [],\n dashboardWidgets,\n events: [\n { name: 'conversations.message.received', description: 'An inbound message arrived on any channel' },\n { name: 'conversations.message.sent', description: 'An outbound message was sent' },\n {\n // Emitted server-side by plg_emit_event, from the tyxter-webhook's\n // payment handler — the browser never sees this write, which is the\n // whole reason packages/db/migrations/027 exists.\n name: 'conversations.payment_received',\n title: 'Pagamento recebido na conversa',\n description: 'O cliente pagou uma cobrança enviada dentro da conversa',\n trigger: 'Quando o provedor confirma o pagamento de uma cobrança aberta no chat',\n icon: 'BadgeDollarSign',\n fields: [\n { name: 'amount', label: 'Valor', type: 'money', example: 'R$ 120,00' },\n { name: 'amount_cents', label: 'Valor em centavos', type: 'number', example: '12000' },\n { name: 'currency', label: 'Moeda', type: 'string', example: 'BRL' },\n { name: 'description', label: 'Descrição da cobrança', type: 'string', example: 'Corte + Escova' },\n { name: 'channel', label: 'Canal', type: 'string', example: 'whatsapp' },\n { name: 'provider', label: 'Provedor', type: 'string', example: 'tyxter' },\n // The subject is the BOOKING, not the charge: an automation reacting\n // to this needs the thing that was paid for, and the charge id is the\n // audit trail beside it.\n { name: 'subject_type', label: 'Tipo do registro', type: 'string', example: 'booking' },\n { name: 'subject_id', label: 'Registro', type: 'id', example: '9f1c…' },\n { name: 'payment_request_id', label: 'Cobrança', type: 'id', example: '3ab2…' },\n ],\n },\n ],\n aiTools: [\n {\n id: 'conversations.list-threads',\n name: 'listConversations',\n description: 'Lists open conversations across all channels, optionally filtered by channel.',\n icon: 'MessageCircle',\n mode: 'read' as const,\n category: 'Conversations',\n parameters: {\n type: 'object' as const,\n properties: {\n channel: {\n type: 'string' as const,\n enum: ['all', 'sms', 'whatsapp', 'instagram', 'email', 'webchat'],\n },\n },\n },\n suggestions: [\n { label: 'Show unread conversations' },\n { label: 'Any new WhatsApp messages?' },\n ],\n permission: { feature: 'conversations', action: 'read' as const },\n },\n {\n id: 'conversations.send-message',\n name: 'sendMessage',\n description: 'Sends a reply in a conversation thread.',\n icon: 'Send',\n mode: 'persist' as const,\n category: 'Conversations',\n parameters: {\n type: 'object' as const,\n properties: {\n conversationId: { type: 'string' as const, description: 'Conversation id' },\n body: { type: 'string' as const, description: 'Message body' },\n },\n required: ['conversationId', 'body'],\n },\n permission: { feature: 'conversations', action: 'create' as const },\n },\n ],\n // One baseline per unit since #338 — read from the generated barrel\n // rather than listed here, so regenerating the SQL never needs a\n // manifest edit to match.\n migrations: MIGRATIONS.map((entry) => ({\n id: `conversations-${entry.id}`,\n version: '1.0.0',\n sql: entry.sql,\n description:\n 'The whole schema this plugin provisions, as one file: every table, view, function, policy and grant, plus the reference rows it seeds. Replaced the per-change chain in the 1.0.0 squash (#338); the files it collapsed are kept, unapplied, in src/migrations.archive/.',\n })),\n functions: [\n {\n slug: 'tyxter-webhook',\n // PUBLIC, and the only function in this plugin that is. The caller is\n // Tyxter's infrastructure, which has no Supabase session and never\n // will — so the gateway check is off and the HMAC signature over\n // `${timestamp}.${rawBody}` IS the authentication.\n ingress: 'none',\n verifyJwt: false,\n // NAMES only. TYXTER_WEBHOOK_SECRET is the endpoint signing secret,\n // returned exactly ONCE by POST /v1/webhook-endpoints and stored as a\n // pool secret by the provisioning script — it is per-endpoint, which is\n // why it can live in the pool while the API key cannot.\n secrets: [\n 'TYXTER_WEBHOOK_SECRET',\n 'TYXTER_API_BASE_URL',\n 'FAYZ_API_URL',\n 'FAYZ_PROJECT_ID',\n 'CONNECTOR_REDEMPTION_SECRET',\n ],\n },\n {\n slug: 'tyxter-template-sync',\n // Two callers, neither of them the outside world: an operator with a\n // Supabase JWT, and an ops script with TYXTER_SYNC_SECRET. The second\n // cannot present a JWT, so the gateway check has to be off and the\n // function authenticates itself.\n ingress: 'none',\n verifyJwt: false,\n // NAMES only. The Tyxter key is NOT here and could not be: a Supabase\n // secret is per-POOL and the key belongs to the platform's project — it\n // is redeemed per run from the vault, which is what the last three are for.\n secrets: [\n 'TYXTER_API_BASE_URL',\n 'TYXTER_SYNC_SECRET',\n 'FAYZ_API_URL',\n 'FAYZ_PROJECT_ID',\n 'CONNECTOR_REDEMPTION_SECRET',\n ],\n },\n {\n slug: 'tyxter-payment-preflight',\n // verify_jwt TRUE — the only function in this wave with a human caller\n // and nothing else. An operator looking at the connector panel has a\n // Supabase session; there is no clock and no ops script here, so the\n // gateway's own check is left on and membership is checked on top of it.\n ingress: 'none',\n verifyJwt: true,\n // NAMES only. The browser cannot ask Tyxter anything — the key is the\n // platform's — which is the whole reason this function exists.\n secrets: [\n 'TYXTER_API_BASE_URL',\n 'FAYZ_API_URL',\n 'FAYZ_PROJECT_ID',\n 'CONNECTOR_REDEMPTION_SECRET',\n ],\n },\n ],\n // The settings screen exists so the connector below can be REACHED: the\n // Integrations tab is drawn by PluginSettingsPanel, so a plugin with a\n // connector and no settings route ships a connector nobody can open.\n settings: [\n {\n id: 'conversations',\n label: 'Conversas',\n icon: 'MessageCircle',\n component: ConversationsSettingsTab as React.ComponentType<unknown>,\n order: 30,\n permission: { feature: 'conversations', action: 'read' as const },\n },\n ],\n onboarding: { program: buildConversationsOnboarding() },\n // WhatsApp through Tyxter. Declared here because this plugin IS the inbox\n // the messages land in — the connector configures inside Conversas.\n connectors: [tyxterConnectorDef],\n locales: conversationsLocales,\n }\n}\n\nexport type { ConversationsProvider } from './data/types'\nexport type { Conversation, Message, Channel, MessageDirection, CreateConversationInput } from './types'\nexport { isWaitingOnUs } from './types'\nexport { createMockConversationsProvider, type MockConversationsConfig } from './data/mock'\nexport {\n createSupabaseConversationsProvider,\n type SupabaseConversationsConfig,\n} from './data/supabase'\n// The automations template editor gates its `pay` action on this, so it has to\n// leave the plugin — see PaymentReadiness for why `ready` alone is not the test.\nexport {\n readPaymentReadiness,\n openPaymentSetupSession,\n canOfferPayment,\n PAYMENT_PREFLIGHT_FUNCTION,\n type PaymentReadiness,\n type PaymentActiveMode,\n type PaymentSetupSession,\n} from './data/payment-preflight'\nexport {\n listMessagingChannels,\n type MessagingChannelAccount,\n type MessagingChannelKind,\n type MessagingChannelStatus,\n type ListMessagingChannelsOptions,\n} from './data/channels'\n// Exported so a bespoke admin can render the connector without mounting the\n// whole plugin — the same reason plugin-shop exports its definitions.\nexport {\n tyxterConnectorDef,\n TYXTER_CONNECTOR_ID,\n TYXTER_LATENCY_BUDGET_MS,\n} from './integrations/tyxter/connectorDef'\n\n// A campanha e os provedores, exportados para que outro plugin possa criar uma\n// campanha sem montar a caixa de entrada inteira. É o que deixa a lista de\n// leads do CRM oferecer \"Enviar WhatsApp\" carregando este módulo sob demanda:\n// um import estático faria a lista quebrar num app onde este plugin não está.\nexport { campaignsApi } from './data/campaigns'\nexport type { CampaignProgress, CampaignTemplate, DispatchResult } from './data/campaigns'\nexport { providersApi } from './data/providers'\nexport type { ProviderId, ProviderRow, ProviderNumber, ProviderTemplate, ProviderCapabilities } from './data/providers'\nexport { CampaignsView } from './views/CampaignsView'\nexport { TemplatesView } from './views/TemplatesView'\nexport { WhatsAppProviders } from './settings/WhatsAppProviders'\n"]}
|