# HANDOFF — 2026-04-21 (CRM WhatsApp Grupo 3 + Marco B/credits + Asaas + polimento) Documento de continuidade. **Quando voltar, comece lendo esta página.** Todo o estado vive no banco (`/saas/desenvolvimento` → Auditoria/Verificações/Testes). --- ## 📊 Estado atual | | | |---|---| | **🔴 Críticos** | **0** ✅ | | **🟠 Altos** | **0** ✅ | | 🟡 Médios adiados | 8 | | 🟢 Baixos adiados | 7 | | Vitest | 208/208 | | SQL integration | 33/33 | | E2E (Playwright) | 5/5 | | Migrations totais | **36** (23 → 36) | | Edge functions | **20** | | Fases Opção C concluídas | **5 + 5b + Grupo 3 inteiro + Marco A + Marco B (Asaas) + admin SaaS** | --- ## 🎯 O que rolou hoje (2026-04-21) ### ✅ Grupo 3 completo — Workflow / CRM - **3.1 Tags** — migration `conversation_tags` + 5 system tags seed · composable `useConversationTags.js` · popover + pills no drawer · pills nos cards do Kanban - **3.2 Atribuição de conversa a terapeuta** (HOJE de tarde) — migration `conversation_assignments` (PK `(tenant_id, thread_key)`, UPSERT, RLS membro-ativo + valida assignee como membro do mesmo tenant) + view `conversation_threads` expandida com `assigned_to` · composable `useConversationAssignment.js` · drawer com Select filtrável + "Assumir" · inbox com filtro aside "Todas/Minhas/Não atribuídas" + chip no card - **3.3 Notas internas** — `conversation_notes` + composable + seção colapsável no drawer - **3.5 Converter número desconhecido em paciente** — botão + dialog quick-cadastro e "Vincular existente" com Select filter + 500 pacientes - **3.6 Histórico de conversa no prontuário** (HOJE) — nova aba "Conversas" no `PatientProntuario.vue` com `PatientConversationsTab.vue` (stats + filter + timeline + mídia + "Abrir no CRM") ### ✅ Marco A — Unificação WhatsApp (dois providers) - **Evolution (pessoal, free)** + **Twilio (AgenciaPSI Oficial, créditos)** — mutuamente exclusivos por tenant - Página chooser `ConfiguracoesWhatsappChooserPage.vue` com 2 cards + deactivate via edge `deactivate-notification-channel` - `send-whatsapp-message` refatorada — roteamento por provider; Twilio deduz crédito ANTES e refunda em falha - **Paridade Twilio** (HOJE) — módulo compartilhado `supabase/functions/_shared/whatsapp-hooks.ts` (provider-agnóstico) consumido por Evolution **e** Twilio inbound. Hooks: opt-in/opt-out/auto-reply + schedule helpers + `makeTwilioCreditedSendFn` (dedução + rollback). Evolution refatorado (~290 linhas duplicadas removidas). Twilio agora roda mesmo pipeline de hooks (antes só inseria a mensagem e saía) ### ✅ Marco B — Sistema de créditos WhatsApp + Asaas - Migration `whatsapp_credits` (4 tabelas: balance, transactions, packages, purchases) + 2 RPCs atômicas (`add_whatsapp_credits`, `deduct_whatsapp_credits`) - Edge `create-whatsapp-credit-charge` — integração Asaas v3 (PIX sandbox + prod); `getOrCreateAsaasCustomer` patcha customer existente com CPF quando falta - Edge `asaas-webhook` — recebe `PAYMENT_RECEIVED/CONFIRMED` e credita balance - Página tenant `/configuracoes/creditos-whatsapp` — saldo + loja + histórico + dialog PIX com QR code - **CPF/CNPJ no dialog de compra** (HOJE) — migration `20260421000013_tenant_cpf_cnpj.sql` (coluna + CHECK 11/14 dígitos), dialog de confirmação com validação (`isValidCPF`/`isValidCNPJ` de `utils/validators`), formatação on-blur, pré-fill de `tenants.cpf_cnpj`, persiste automaticamente no primeiro uso. Fallback sandbox removido ### ✅ Admin SaaS — gestão de créditos (HOJE) Integrado em `/saas/addons` (reuso do pattern existente, não criou página paralela): - **Aba 4 "Pacotes WhatsApp"** — CRUD `whatsapp_credit_packages` com DataTable (destaque, posição, preço + BRL/msg), toggle `is_active` inline, dialog de edição com validação - **Aba 5 "Topup WhatsApp"** — tenant Select filtrável com saldo ao vivo; RPC `add_whatsapp_credits` com `p_admin_id = auth.uid()` (auditoria); histórico das últimas 20 transações topup/adjustment/refund ### ✅ Grupo 2 Automação - **2.3 Auto-reply** — `conversation_autoreply_settings` + `conversation_autoreply_log`; schedule por modo (`agenda` / `business_hours` / `custom`); cooldown por thread; respeita opt-out; agora funciona em **ambos** providers - **2.4 Lembretes de sessão** — `conversation_session_reminders_*`; edge `send-session-reminders` (cron); já trata Twilio com dedução + rollback ### ✅ Grupo 5 Compliance (LGPD Art. 18 §2) - **5.2 Opt-out** — `conversation_optouts` + `conversation_optout_keywords` (10 keywords system + custom); detecção por regex word-boundary; ack automático; opt-in via "voltar/retornar/reativar/restart" - Página `/configuracoes/conversas-optouts` ### ✅ Refactor polimórfico — telefones + emails - `contact_types` + `contact_phones` (polimórfico: `entity_type` + `entity_id`) — migration 20260421000008 - `contact_email_types` + `contact_emails` — migration 20260421000011 - Componentes `ContactPhonesEditor.vue` + `ContactEmailsEditor.vue` - Trocado em `PatientsCadastroPage.vue`, `MedicosPage.vue` - Migration retroativa v2: detecta conversas e cria/atualiza phone como WhatsApp (vinculado) ### ✅ Polimento visual — sessão manhã - Skeletons simplificados no dashboard terapeuta · animações fade-up com stagger por `[--delay:Xms]` (fix specificity sobre `.dash-card`) - ConfirmDialog com `group="conversation-drawer"` (evita duplicata) - Image preview PrimeVue com botão **download** injetado via MutationObserver (fetch + blob pra cross-origin) - Audio/video com `preload="metadata"`, controles de velocidade herdados do browser - `friendlySendError()` no drawer store — mapeia códigos pt-BR via `error.context.json()` - Teleport `#cfg-page-actions` pra ações globais da Configurações - Brotli/Gzip + auto-import Vue/PrimeVue + bundle analyzer ### ✅ Infra geral - Bucket privado `whatsapp-media` + decrypt via Evolution `getBase64FromMediaMessage` + upload + signed URLs on-demand - Realtime em `conversation_messages` via publication `supabase_realtime` - `AppLayout` consolidado (removido duplicatas por área) + `RouterPassthrough` - `console.trace` debug removido (watch router/Supabase) que degradava perf --- ## 🎯 Próxima sessão (começar por aqui) Do CRM WhatsApp ainda restam: ### Grupo 3 (resto) - **3.4 SLA / alerta** — conversa sem resposta > X min. Trigger `conversation_sla_rules` + worker cron - **3.7 Bot auto-triagem** — pergunta nome/horário antes de sair pro humano ### Grupo 6 — Conexão resiliente - **6.1 Heartbeat** — cron verifica Evolution; dispara incident se desconectado > N min - **6.2 Alerta** quando celular desconecta (notification + e-mail admin tenant) - **6.3 Reconnect automático** (tentar re-init da instância) ### Grupo 7 — Analytics - **7.1 Tempo médio de primeira resposta** (card no ClinicDashboard + filtro por terapeuta) ### Grupo 8 — Integrações - **8.2 Botão na agenda** "Lembrar paciente da sessão" — dispara `send-whatsapp-message` com template `lembrete_sessao` - **8.3 Status sessão dispara mensagem** (ex: cancelada → aviso auto) - **8.4 Link agendador cria lead** — quando paciente preenche intake mas não finaliza, aparece no CRM como thread ### Outros blocos - **Notificação de saldo baixo WhatsApp** — trigger em `whatsapp_credits_balance` quando `balance < low_balance_threshold`; e-mail + toast - **Dashboard saas de receita créditos** — total arrecadado Asaas por mês, pacotes mais vendidos - **Retention policy 5.1** — apagar/anonimizar conversas > X dias (configurável por tenant) - **5.4** — seção de conversas no LGPD export do paciente --- ## 🔧 Setup Evolution/WhatsApp / Asaas Tudo em **`WHATSAPP_SETUP.md`**. Resumo crítico: 1. `supabase functions serve --no-verify-jwt --env-file supabase/functions/.env` em terminal separado 2. `.env` do functions tem: `SUPABASE_URL`, `SUPABASE_ANON_KEY`, `SUPABASE_SERVICE_ROLE_KEY`, `ASAAS_API_KEY`, `ASAAS_API_URL=https://api-sandbox.asaas.com/v3` 3. Evolution: `/saas/whatsapp` cadastra creds global → `/configuracoes/whatsapp-pessoal` conecta QR 4. Twilio: `/saas/twilio-whatsapp` provisiona subconta → tenant ativa em `/configuracoes/whatsapp-oficial` (usa créditos) ⚠️ Após editar qualquer `supabase/functions/**` precisa reiniciar o `supabase functions serve` — ele não tem hot reload. --- ## 🌲 Deploy options (guardadas pra depois) - **(a)** Smoke test de infra — subir pra Supabase cloud + hospedagem só pra testar sozinho. ~2-3h. - **(b)** Beta fechado com clínicas — precisa: 3.4 SLA, 6.1 heartbeat, 7.1 analytics, retention policy, tour/onboarding refinamento. - **(c)** [em andamento] Fechar gaps funcionais. --- ## 📦 Commits Tem um **`commit.txt`** pronto na raiz com mensagem consolidada pra um commit único de tudo que está pendente. `git status` mostra ~160 arquivos modificados/criados. Conferir `commit.txt` antes de usar. Se preferir quebrar em commits menores, os grupos lógicos são: 1. Migrations CRM + créditos + polimorfismo (pasta `database-novo/migrations/20260420*` + `20260421*`) 2. Edge functions (pasta `supabase/functions/`) 3. Frontend CRM (`src/components/conversations/`, `src/features/conversations/`, `src/features/patients/prontuario/PatientConversationsTab.vue`) 4. Composables novos (`src/composables/useConversation*.js`, `useWhatsappCredits.js`, `useContact*.js`, `useAutoReplySettings.js`, `useSessionReminders.js`) 5. Páginas config novas (`src/layout/configuracoes/*`) 6. Admin SaaS (`SaasAddonsPage.vue` com 2 tabs novas) 7. Refactors (PatientsCadastro/Medicos trocando pra ContactEditors; AppLayout; router) --- ## ⚠️ Pendências conhecidas - **15 V# adiados** (8 médios + 7 baixos) — sprint de polimento depois do beta - **Tour guiado / onboarding wizard** — refino deixado pro fim - **Dashboard SaaS de receita Asaas** — falta página - **Rotação de credenciais Twilio** (segurança) — se subconta vazar, precisa de flow pra regenerar --- ## 🛠️ Stack lembretes - **DB local:** `docker exec -i supabase_db_agenciapsi-primesakai psql -U postgres -d postgres` - **DB como supabase_admin (ALTER POLICY em tabelas owned):** ```bash docker exec -i -e PGPASSWORD=postgres -e PGCLIENTENCODING=UTF8 \ supabase_db_agenciapsi-primesakai \ psql -U supabase_admin -d postgres -h localhost -f migration.sql ``` - **Vitest:** `npx vitest run` - **SQL integration:** `node database-novo/tests/run.cjs` - **Edge functions serve:** `supabase functions serve --no-verify-jwt --env-file supabase/functions/.env` - **Evolution Manager:** `http://localhost:8080/manager/` - **Supabase Studio:** `http://localhost:54323` - **Asaas sandbox:** `https://sandbox.asaas.com` (login separado do prod) --- ## 📚 Memória persistente (carregada automaticamente) Já saved em `MEMORY.md`: - Project overview · MVP Assessment · Deploy options - Sanitização sempre · Priorização por severidade · Self-hosted > provider externo - Gotcha supabase_admin · Tracking dev_*_items --- ## 📌 Bom descanso, até amanhã!