HANDOFF + cleanup: bug Teleport resolvido, backups antigos removidos

- HANDOFF.md atualizado: bug do chip do cronômetro resolvido em 2026-04-27.
  Causa-raiz documentada (múltiplos Teleports compartilhando target +
  Transition>Element v-if gera comment placeholder VNode → emitsOptions:null
  no shouldUpdateComponent) e fix oficial (Transition envolvendo Teleport).
- Backups locais 2026-03-23 removidos do índice (já estavam .gitignored,
  apenas saneamento).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Leonardo
2026-04-26 08:11:28 -03:00
parent 1bcb969f72
commit f2b15ce0f7
4 changed files with 173 additions and 41341 deletions
+173 -366
View File
@@ -1,399 +1,206 @@
# HANDOFF — 2026-04-23 (fim do dia) # HANDOFF — 2026-04-26 (continuação 2026-04-27: bug Teleport resolvido)
Documento de continuidade. **Quando voltar, comece lendo esta página.** Documento de continuidade. **Quando voltar, comece lendo esta página.**
Todo o estado vive no banco (`/saas/desenvolvimento` → Auditoria/Verificações/Testes). Sessão inteira polindo o Layout Melissa: light mode, integração no /profile,
Agenda fullscreen, dock taskbar, cadastro de paciente, ações contextuais.
--- ---
## 📊 Estado atual ## ✅ BUG RESOLVIDO — chip do cronômetro (2026-04-27)
| | | **Era:** dois `<Teleport to=".melissa-dock">` (chip cronômetro + dock contextual)
|---|---| com `<Transition>` interno + `v-if` interno apontando pro mesmo target. O slot
| **🔴 Críticos** | **0** ✅ | do `<Transition>` colapsava pra comment placeholder VNode quando falsy, e
| **🟠 Altos** | **0** ✅ | esses placeholders ficavam intercalados no array de children do target —
| Vitest | 208/208 | ao patch/reorder, `shouldUpdateComponent` lia `.component.emitsOptions` em
| SQL integration | 33/33 | nó cujo `.component` foi anulado pelo unmount irmão. Daí `null`.
| E2E (Playwright) | 5/5 |
| Migrations totais | **47** (36 → 47) | **Fix (pattern oficial Vue):** `<Transition>` envolvendo `<Teleport>`, não o
| Edge functions | **25** (20 → 25) | contrário. Assim o Teleport some/aparece como unidade, sem deixar placeholder
| Cron jobs ativos | 2 (heartbeat 2min + SLA 5min) | no target compartilhado.
| Commits hoje | **18** (de `f76a2e3` a `f1c97ee`) |
```vue
<!-- ANTES -->
<Teleport to=".melissa-dock">
<Transition name="...">
<Element v-if="cond" />
</Transition>
</Teleport>
<!-- DEPOIS -->
<Transition name="...">
<Teleport v-if="cond" to=".melissa-dock">
<Element />
</Teleport>
</Transition>
```
Aplicado em `MelissaCronometro.vue:322` e `MelissaAgenda.vue:842`.
**Lição persistida:** quando múltiplos Teleports compartilham target, evitar
`v-if` dentro do Teleport — coloca o `v-if` no próprio Teleport.
--- ---
## 🎯 O que rolou hoje (2026-04-23) ## O que aconteceu hoje (cronologia)
### ✅ Admin SaaS: ajuste manual de créditos WhatsApp (f76a2e3) ### Manhã — Light mode + /profile
- **Light bloom no MelissaLayout** — gradiente Bloom usa CSS vars (`--bloom-c1/c2/base-1/base-2`) que flipam dark/light. Dark: 400/300/950. Light: 200/100/0. Removido `background-image: none !important`. Corrigido branco-em-branco do clock + ψ + psi-kbd. Panels light com 92% opacity. Classe `win11-has-photo` quando upload de foto.
- **Layout Melissa em /profile** — 3º card na seção 06 com badge "Em construção" + listras diagonais. Aceita `'melissa'` em `setVariant`/`_loadVariant`/bootstrap. Migration `20260425000001_user_settings_layout_variant_melissa.sql` (já aplicada). **Switch real do app fica pra Fase 5 (A#32 no dev_auditoria_items).**
- **Cronômetro/Personalização em light** — trocados `color: white` scoped → `var(--m-text)` em `.mc-select`, `.mc-display`, `.mc-btn--secondary`, `.mc-chip`, `.settings-select`, `.settings-test-btn`. Pílula `psi-kbd` ganhou tokens `--m-kbd-bg/--m-kbd-text` (era `rgba(0,0,0,.65)` hardcoded — em light virava preto sobre preto). Shadow do `.psi-btn` suavizada em light.
- **Ícones tons claros** — bloco de overrides em light mapeia `text-{cor}-200/300/400``text-{cor}-600` (17 cores Tailwind: emerald, green, amber, purple, blue, sky, cyan, teal, indigo, violet, pink, rose, red, orange, yellow, fuchsia, lime).
- RPC `admin_adjust_whatsapp_credits` (+/-) com `|amount| ≤ 1000` por operação ### Tarde — Agenda fullscreen
- Remoção só afeta pool cortesia (topup/adjustment/refund) — compras `purchase` são intocáveis (FIFO cortesia primeiro) - **Cores light na MelissaAgenda** — 12 substituições de `color: white``var(--m-text)` semântico. Mantido white em `.mini-cal__day.is-hoje` (bg accent saturado). Avatar do paciente: bg `--m-accent-strong` (translúcido, invisível em light) → `--m-accent` (saturado). Tweaks light: shadow suave na `.ma-page`, eventos do FC com `saturate(140%)` + box-shadow leve, cursor "agora" com glow vermelho.
- Helper RPC `get_whatsapp_removable_balance` pra UI mostrar breakdown - **Mini-cal funcionalidades** — click no dia pula o FC (`fcApi().gotoDate`), range visível do FC destacado (`is-in-range` via `viewStart/viewEnd`), dots por evento real do mês (`useMelissaEventosRange(miniRangeStart, miniRangeEnd)` separado), até 3 dots por dia.
- UI em `/saas/addons`: SelectButton Adicionar/Remover, ConfirmDialog com impactados ao desativar pacote - **Feriados** — `useFeriados` integrado. Cores: nacional=rose, municipal=amber, personalizado=violet. Tooltip combina nome do feriado + count de eventos. Hoje + feriado: ring externo na cor do feriado. Recarrega ao mudar de ano via watch.
- **Dias fechados** — `useAgendaSettings.workRules` carregado. `workDowSet` computed. Dias fora da jornada → `is-fechado` (cinza apagado). Mutex com feriado (feriado vence cinza). Fallback seg-sex se sem regras.
- **FC central com feriados** — `fcEvents` agora `[...eventosSemana, ...feriadoFcEvents]` (display:'background' amber suave).
### ✅ Heartbeat + Reconnect (6.1 + 6.3) — e1f756e / 4e4bac6 ### Tarde 2 — Layout estrutural da Agenda
- **Card `Hoje` mesclado** — antes 2 cards (stats + sessões). Agora 1 só: header + 4 stats + divider + lista de sessões. Count à direita do header.
- **Card Hoje movido pra esquerda** — sai da col 3 (widgets), vai pro topo da col 1 (sidebar pacientes). Modificador `.ma-w--side` com margem + max-height 50vh + lista interna scrollable.
- **ProximosFeriadosCard reaproveitado** — entre mini-cal e Hoje na col 3. Override visual (`bg-soft`, `border-color`, `border-radius: 12px`) pra encaixar no glass.
- Tabela `whatsapp_connection_incidents` com UNIQUE parcial (1 aberto por channel) ### Noite 1 — Dock + cadastro de paciente
- RPCs `whatsapp_heartbeat_open_incident/resolve/mark_notified` - **Dock taskbar** (`.melissa-dock`) — faixa fixa 76px no bottom, transparente, `position:fixed`. ψ desceu pra `bottom: 10px` (centralizado vertical na faixa). Token `--m-dock-h: 76px`. Páginas reservam space via `inset` (`.ma-page` ajustada). **CSS no `<style>` global** pra evitar perda de scoped em static-hoisted nodes.
- Edge `whatsapp-heartbeat-check` com threshold configurável (padrão 5min) - **Chip cronômetro teleportado** pro dock. Substituído `position:fixed top:1.5rem left:1.5rem` por `position:relative` (filho flex). Aparece após ψ, com gap.
- **Reconnect automático** (6.3): antes de abrir incident tenta `POST /instance/restart`, espera 3s, re-checa state. Se voltou → resolve. Cooldown 10min/channel. - **Animação minimize macOS-style** — `isMinimizing` flag + computed `dialogTransitionName` ('minimize' vs 'lift' default). CSS `transform-origin: 96px calc(100% - 38px)` (posição do chip no dock). Scale 1→0.05 + opacity em 340ms cubic-bezier ease-in. Chip-pop com `transition-delay: 120ms` no enter pra dar sensação macOS.
- UI em `/configuracoes/whatsapp-pessoal` ganhou card "Monitoramento de conexão" (toggles alerts/reconnect + threshold + histórico 7d) - **Botão fake "Adicionar paciente"** no topo da lista — tracejado com `+`. Click abre `PatientCreatePopover` (3 opções: rápido/completo/link de cadastro com URL+copy). Reaproveita `PatientCadastroDialog` + `ComponentCadastroRapido`. Emit `patient-created` → MelissaLayout faz `refetchPacientes`.
- Painel SaaS `/saas/whatsapp` mostra badge de incidents + "Verificar tudo agora" - **Pacientes novos no topo** — composable agora retorna `created_at`. Threshold 7 dias. Computed `pacientesFiltrados` particiona em [novos sort desc, resto alfabético]. Visual: `is-novo` bg `--m-accent-soft` + badge "novo" pílula primary.
- Cron `*/2 * * * *` ativo (job 5)
### ✅ SLA de conversas (3.4) — 771b636 ### Noite 2 — Dock contextual ⚠️ INTRODUZIU O BUG PENDENTE
- **Imports adicionados** no MelissaAgenda: `PatientProntuario`, `useConversationDrawerStore`, `getSessionCounts`. Removido depois: `import Menu from 'primevue/menu'` (auto-import).
- **State + funções** pra dock contextual: `pacienteSelecionado` computed, `sessionCountsMap` cache, `prontuarioOpen`/`prontuarioPatient`, handlers `abrirSessoes/Whatsapp/Prontuario`, `editarPacienteSelecionado`, `desselecionarPaciente`, `toggleKebab`/`kebabItems`.
- **Template** — `<button>` do paciente virou `<div>` (não pode ter button-dentro-de-button por causa do kebab `.ma-pat__kebab`). `<Teleport to=".melissa-dock">` com `<Transition name="ma-dock-pop">` mostrando avatar + nome + count + 4 ações + close. `<Menu>` PrimeVue popup. `<PatientProntuario v-if=...>`.
- **CSS** — `.ma-dock-actions` glass aesthetic (espelha .mc-chip), responsivo (some em ≤ 1024px). `.ma-pat__kebab` só visível em mobile.
- `conversation_sla_rules` (config 1/tenant, threshold 1-1440 min, horário comercial opcional, escopo assigned_only|all) **Estado pós-implementação:** funcionou inicialmente. Depois de algum HMR, o chip do cronômetro deixou de aparecer. Erro `emitsOptions: null` no console. Tentei 3 fixes sem sucesso. Investigar amanhã.
- `conversation_sla_breaches` com UNIQUE parcial 1 aberto/thread
- Trigger `trg_sla_resolve_on_outbound` resolve breach automaticamente quando chega outbound
- Edge `conversation-sla-check` calcula `businessMinutesElapsed` em TS
- UI `/configuracoes/conversas-sla` (config + histórico 7d)
- Cron `*/5 * * * *` ativo (job 6)
### ✅ Saldo baixo WhatsApp — e409ba6
- Trigger `fn_whatsapp_low_balance_notify` BEFORE UPDATE em `whatsapp_credits_balance`
- Dispara quando saldo cruza threshold + anti-spam via `low_balance_alerted_at`
- Reset automático quando `add_whatsapp_credits` recredita
### ✅ Pipeline de alertas robusto — 881fa16 / 5c50db6 / 6db06ab / 4441661 / 36fbc02 / 5f51bc0 / f646efe / 4026415 / 64e7634
Múltiplos fixes e melhorias do pipeline `system_alert`:
- Toast vermelho **sticky** com botão de ação (deeplink ou "Abrir conversa")
- Polling a cada 60s + catch-up no `visibilitychange` como fallback pro Realtime
- Agrega múltiplas pendentes no catch-up (mostra só mais recente + `+N outros no sino`)
- Não redispara toast pra `system_alert` já existentes no mount (F5 limpo)
- Sistema de aliases `/conversas``/therapist/conversas` ou `/admin/conversas` por role
- Browser notification do Chrome/Windows agora leva pro drawer da conversa ao clicar
- NotificationItem no sino ganhou botões inline "💬 Conversa" / "Abrir →" + fix NotFound
- Notifica **owner_id do channel + admins** (deduplicado)
### ✅ Analytics 7.1 — adf9208
- Helper interno `_first_response_runs` identifica "runs" de inbound (sequências do paciente) + delta até próxima outbound
- RPCs: `first_response_stats`, `first_response_by_therapist`, `first_response_evolution`
- Card `FirstResponseCard.vue` no Clinic e Therapist Dashboards com 3 KPIs + sparkline + ranking
### ✅ Fluxo de reativação de canal — 881fa16
- Edge `reactivate-notification-channel` (espelho da deactivate)
- SaasWhatsappPage detecta canal soft-deleted e reativa ao salvar
- ConfiguracoesWhatsappPage (tenant) mostra card "Reativar WhatsApp Pessoal"
- ChooserPage intercepta clique e reativa antes de ir pro setup
- Migration RLS `notification_channels` permite ler soft-deleted (donos/saas_admin/membros)
### ✅ Bot auto-triagem (3.7) — c2c42a1
- Tabelas `conversation_bots` + `conversation_bot_sessions`
- Helper `maybeProcessBot` em `_shared/whatsapp-hooks.ts`
- Integrado em `evolution-whatsapp-inbound` E `twilio-whatsapp-inbound`
- Página `/configuracoes/conversas-bots` com editor de steps, trigger, keywords, opt-out
- Ao terminar: closing + `conversation_notes` com resumo das respostas
### ✅ Grupo 8 completo — b8ea292
- **8.2** Botão "Lembrar paciente" no `AgendaEventDialog` — edge `send-session-reminder-manual`
- **8.3** Trigger DB em `agenda_eventos` dispara edge `send-session-status-notification` (cancelado/remarcado/confirmado)
- **8.4** Intake abandonado: coluna `last_progress_at`, edges `save-intake-progress` + `convert-abandoned-intakes`, RPC `convert_abandoned_intake_to_lead`, autosave no form público
### ✅ Dashboard SaaS receita créditos — f1c97ee
- 4 RPCs (`saas_wa_credits_revenue_stats/top_packages/usage_summary/revenue_evolution`) — saas_admin only
- Card `SaasCreditsRevenueCard.vue` com 4 KPIs (receita, compras, créditos, consumo) + sparkline + top pacotes
- Integrado em `/saas` (SaasDashboard)
### ✅ Fix lateral — 0f64381
`send-session-reminders` comparava `provider='evolution'` mas DB tem `'evolution_api'` — caía em `unknown_provider`. Corrigido e validado end-to-end (lembrete chegou pro paciente André Green no celular +55 16 98828 0038).
--- ---
## 🧪 ROTEIRO DE TESTES PRA AMANHÃ ## 📋 Pendências do dia (não commitado)
Ordem sugerida (3h estimado com tudo). Cada seção é independente — pode testar em qualquer ordem depois de **0**. ```
M HANDOFF.md
M src/router/routes.misc.js (rota /preview/melissa — do dia 24)
M src/views/pages/saas/SaasAddonsPage.vue (do dia 24, ainda não commitado)
M src/views/pages/account/ProfilePage.vue (card Melissa)
M src/layout/composables/layout.js (variant aceita 'melissa')
M src/app/bootstrapUserSettings.js (bootstrap aceita 'melissa')
M src/layout/melissa/MelissaLayout.vue (light bloom + dock + tokens)
M src/layout/melissa/MelissaCronometro.vue (light + teleport chip + animação minimize)
M src/layout/melissa/MelissaAgenda.vue (light + mini-cal + feriados + cadastro + dock contextual)
M src/layout/melissa/composables/useMelissaPacientes.js (created_at)
?? src/layout/melissa/ (todo o trabalho do dia 24-26)
?? layout-scratchs/ (rascunhos design Direção A)
?? database-novo/migrations/20260425000001_user_settings_layout_variant_melissa.sql
```
### 0. Pré-requisitos (5 min) **Sugestão de commits separados (ordem):**
1. **Fix admin adjust créditos (do dia 24, ainda pendente):**
```
Fix admin adjust créditos: clamp silencioso vira erro vermelho
```
2. **Layout Melissa — preview funcional (dia 24):**
```
Layout Melissa (Direção B) — preview em /preview/melissa
```
3. **Layout Melissa — Profile + light mode + ícones tons claros (dia 25-26):**
```
Melissa: light bloom + opção em /account/profile (Em construção)
- Light mode usa CSS vars --bloom-* (tons 200/100/surface-0)
- Card "Melissa" em /account/profile com badge Em construção
- Migration: layout_variant aceita 'melissa'
- Cores light em cronômetro/personalização (color: white → var(--m-text))
- Ícones text-X-200/300/400 → text-X-600 em light (17 cores)
- Switch real do app: A#32 (Fase 5)
```
4. **Melissa Agenda — cores light + mini-cal funcional + feriados (dia 26):**
```
Melissa Agenda: light + mini-cal completo + feriados + dias fechados
- 12 cores white → var(--m-text) na .ma-page e widgets
- Mini-cal: click pula FC, range visível destacado, dots reais por mês
- Feriados nacional/municipal/personalizado (rose/amber/violet)
- Dias fechados (workRules) cinza apagado
- FC central com feriados como background events
```
5. **Melissa Agenda — layout estrutural (dia 26):**
```
Melissa Agenda: card Hoje na sidebar + ProximosFeriadosCard
- Card Hoje (stats+sessões) mesclados em um, movidos pra col esquerda
- ProximosFeriadosCard reaproveitado entre mini-cal e Hoje
```
6. **Melissa — dock + cadastro paciente (dia 26):**
```
Melissa: dock taskbar Win11 + cadastro paciente via PatientCreatePopover
- .melissa-dock fixed bottom 76px receptor de items teleportados
- Chip cronômetro teleportado pro dock + animação minimize macOS-style
- ψ centralizado vertical na faixa (bottom:10px)
- Botão fake "+" no topo da lista de pacientes abre popover
- Pacientes novos (7d) no topo + badge "novo"
```
7. **✅ Dock contextual com ações de paciente (bug resolvido 2026-04-27):**
- Mudanças do MelissaAgenda relacionadas a `.ma-dock-actions`, `Menu kebab`, `PatientProntuario` integration
- Bug do chip resolvido (Transition envolvendo Teleport)
```
Melissa Agenda: dock contextual com ações do paciente selecionado
```
---
## 📚 Tracking persistente
- **A#32** — Fase 5 router wire-up (ainda pendente, sessão dedicada)
- **Memória atualizada:** `project_layout_melissa.md` — adicionar nota sobre bug pendente + estado da sessão
- Adicionar **A#33** amanhã pro bug do chip do cronômetro pendente, depois fechar quando resolvido
---
## 📦 Setup pra retomar
```bash ```bash
# Reiniciar Supabase functions serve pra carregar 5 edges novas/alteradas # Terminal 1 — functions
supabase functions serve --no-verify-jwt --env-file supabase/functions/.env supabase functions serve --no-verify-jwt --env-file supabase/functions/.env
```
Confirma no output que aparecem: # Terminal 2 — vite
- `reactivate-notification-channel` npm run dev
- `whatsapp-heartbeat-check`
- `conversation-sla-check`
- `send-session-reminder-manual`
- `send-session-status-notification`
- `save-intake-progress`
- `convert-abandoned-intakes`
Também confirme crons: # Browser
```bash http://localhost:5173/preview/melissa # ← Melissa preview
docker exec -i supabase_db_agenciapsi-primesakai psql -U postgres -d postgres -c "SELECT jobid, schedule, jobname, active FROM cron.job WHERE active=true;" http://localhost:5173/account/profile # ← /profile com card Melissa
``` http://localhost:5173/auth/login # ← Login (dados reais)
Esperado: 2 jobs ativos (5 e 6).
---
### 1. Admin adjust créditos (~10 min)
Login como **saas_admin**`/saas/addons` → aba **Topup WhatsApp**.
1. Selecionar tenant Bruno Terapeuta
2. Card "Breakdown do saldo" mostra removível/protegido
3. Tentar **adicionar 1500** → deve recusar (máx 1000)
4. Adicionar **500** → ok
5. Mudar pra **Remover** → deve respeitar limite removível
6. Testar confirmação antes de remover
Aba **Pacotes WhatsApp**: desativar um pacote → confirmação com N compras / M tenants distintos.
---
### 2. Heartbeat + Reconnect (~10 min)
**2.1. Baseline:** `/saas/whatsapp` → "Verificar tudo agora" → 1 canal, status ok.
**2.2. Simular queda com Evolution respondendo:**
```bash
# Faz logout da instância no Evolution (Evolution API continua de pé)
curl -X POST http://localhost:8080/instance/logout/agenciapsi-teste -H "apikey: <APIKEY>"
```
Clica "Verificar tudo agora" 2 vezes. Na segunda, o heartbeat tenta **restart automático** e conecta de novo (precisa escanear QR, mas connection_status volta). Summary deve ter `auto_reconnected: 1`.
**2.3. Simular queda total (Evolution offline):**
```bash
docker stop evolution-api # ou o nome do container
```
Baixa threshold pra 1 min em `/configuracoes/whatsapp-pessoal`. Clica "Verificar tudo agora", espera 1 min, clica de novo. Agora abre incident + toast vermelho + notificação.
Resubir o container + clica verificar → breach fica "Resolvido".
---
### 3. SLA de conversas (~15 min)
`/configuracoes/conversas-sla` → ativa + threshold 1 min + escopo "Todas as conversas" + notify admin ON.
Apague breaches antigos e dispare manual:
```bash
docker exec -i supabase_db_agenciapsi-primesakai psql -U postgres -d postgres -c "DELETE FROM conversation_sla_breaches WHERE resolved_at IS NULL;"
SERVICE_KEY=$(supabase status -o env 2>/dev/null | grep SERVICE_ROLE | cut -d'"' -f2)
curl -s -X POST http://localhost:54321/functions/v1/conversation-sla-check -H "Authorization: Bearer $SERVICE_KEY" -d '{}'
```
Esperado: abre breaches pras threads inbound sem resposta. Notifica.
Responda uma das threads no CRM → trigger fecha o breach automaticamente (vê card "Resolvido" em `/configuracoes/conversas-sla`).
---
### 4. Bot de triagem (~15 min)
`/configuracoes/conversas-bots` → ligar + salvar com 4 perguntas default.
De **outro celular que não seja paciente cadastrado**, manda "Oi" pro WhatsApp conectado.
Esperado:
- Bot responde saudação + primeira pergunta (nome)
- Cada resposta avança 1 pergunta
- Ao final: closing + nota interna em `conversation_notes` com resumo
Conferir:
```bash
docker exec -i supabase_db_agenciapsi-primesakai psql -U postgres -d postgres -c "
SELECT current_step, status, collected_data FROM conversation_bot_sessions ORDER BY started_at DESC LIMIT 3;"
```
Abra o CRM na thread desse número → no drawer, na aba notas, deve ter o resumo.
---
### 5. Botão "Lembrar paciente" na agenda (8.2) (~5 min)
Agenda → abrir evento existente (Edit) com paciente que tenha telefone. Footer tem botão verde de WhatsApp.
Clicar → confirmação → toast sucesso → mensagem chega no celular do paciente.
Teste erros: paciente sem telefone → mensagem clara.
---
### 6. Status sessão dispara mensagem (8.3) (~10 min)
**Antes:** criar templates custom (ou deixar sem — skip silencioso) pra `cancelamento_sessao` / `remarcacao_sessao` / `confirmacao_sessao` em `/configuracoes/whatsapp` → aba Templates.
No dialog de evento existente, mudar status pra **"Cancelado"** → Salvar.
Trigger DB chama edge, edge resolve template + envia. Conferir:
```bash
docker exec -i supabase_db_agenciapsi-primesakai psql -U postgres -d postgres -c "
SELECT direction, provider, provider_raw, substring(body from 1 for 80) FROM conversation_messages
WHERE provider_raw->>'status_change' = 'true' ORDER BY created_at DESC LIMIT 3;"
```
Se não tem template configurado, a edge retorna `skipped: template_not_found` (silencioso).
---
### 7. Intake abandonado → lead (8.4) (~15 min)
1. Gere um link de convite em `/admin/agendamentos-recebidos` ou similar
2. Abra o link anônimo (incognito) → form público
3. Preencha **só nome + telefone** (espera 1.5s pra autosave rodar)
4. Feche a aba (não submete)
Conferir que o intake ficou `in_progress`:
```bash
docker exec -i supabase_db_agenciapsi-primesakai psql -U postgres -d postgres -c "
SELECT status, nome_completo, telefone, last_progress_at FROM patient_intake_requests ORDER BY updated_at DESC LIMIT 3;"
```
Forçar conversão (sem esperar 30 min):
```bash
SERVICE_KEY=$(supabase status -o env 2>/dev/null | grep SERVICE_ROLE | cut -d'"' -f2)
curl -s -X POST http://localhost:54321/functions/v1/convert-abandoned-intakes \
-H "Authorization: Bearer $SERVICE_KEY" -d '{"idle_minutes": 0}'
```
Esperado: `converted: 1`. Abre o CRM → thread nova com o telefone + nota interna com dados coletados.
**Ativar cron** (opcional, a cada 15 min):
```sql
SELECT cron.schedule('convert-abandoned-intakes-every-15min', '*/15 * * * *', $$
SELECT net.http_post(
url := current_setting('app.settings.supabase_url') || '/functions/v1/convert-abandoned-intakes',
headers := jsonb_build_object('Authorization', 'Bearer ' || current_setting('app.settings.service_role_key'), 'Content-Type', 'application/json'),
body := '{}'::jsonb
);
$$);
``` ```
--- ---
### 8. Saldo baixo WhatsApp (~5 min) ## 📌 Próximas opções (escolha amanhã)
Login como terapeuta → `/configuracoes/creditos-whatsapp` → threshold em 20. **A. Resolver bug do chip do cronômetro** ⚠️ prioridade — bloqueia commit do dock contextual
- Ver hipóteses na seção "BUG PENDENTE" no topo
- Estratégia recomendada: comentar o `<PatientProntuario>` primeiro, hard refresh, ver se chip volta. Se sim, é o componente. Se não, comentar `<Menu>` kebab. Se não, comentar `<Teleport>` do `.ma-dock-actions`. Isolar a causa.
```bash **B. Continuar polindo Melissa**
docker exec -i supabase_db_agenciapsi-primesakai psql -U postgres -d postgres -c " - Toolbar custom da Agenda (revisar visual)
UPDATE whatsapp_credits_balance SET balance=100, low_balance_alerted_at=NULL WHERE tenant_id='bbbbbbbb-0002-0002-0002-000000000002'; - Stats hoje (interatividade — click filtra?)
UPDATE whatsapp_credits_balance SET balance=10 WHERE tenant_id='bbbbbbbb-0002-0002-0002-000000000002';" - Evento panel (visual + actions)
``` - Drag/resize no FC com persist (Fase 2 do roadmap)
- Recorrências virtuais (RRULE → ocorrências)
Toast vermelho "Saldo baixo" aparece + botão "Ir pra loja →". **C. Fase 5 (A#32)** — wire-up router pra Layout Melissa virar real
- Alto risco — sessão dedicada
- Precisa de Fase 3 antes (split MelissaLayout/MelissaResumo)
--- **D. Voltar pro QA** (Seção 3.4 do roteiro de testes)
- SLA de conversas + Bot triagem + Lembrar paciente + ...
### 9. Analytics 1ª resposta (~3 min)
`/admin` (ClinicDashboard) → card "Tempo de 1ª resposta" com sparkline + ranking terapeutas.
`/therapist` (TherapistDashboard) → card filtrado pelo user logado.
---
### 10. Dashboard SaaS receita (~3 min)
Login como saas_admin → `/saas` → seção nova "Receita de créditos WhatsApp":
- KPIs receita/compras/créditos/consumo
- Sparkline de evolução
- Ranking top 5 pacotes
Mudar período (30d/90d/6m/12m) → recarrega.
---
## 🎯 Próxima sessão (se tudo der ok no teste)
### Items do backlog original que ficaram
- **5.4 Export LGPD de conversas** — incluir conversas no export de paciente (que já existe)
- **Tour guiado / onboarding wizard** — refino UX
- **Rotação de credenciais Twilio** — se subconta vazar, precisa de flow pra regenerar
- **Retention 5.1** — apagar/anonimizar conversas > X dias **(você pulou — voltar quando for beta fechado)**
### Items novos nascidos desta sessão
- **Suporte Twilio no status→msg e lembrete manual** — hoje só Evolution (Twilio retorna `provider_not_supported_yet`)
- **Persistir user "bot" sintético** — hoje `conversation_notes.created_by` usa primeiro admin como hack
- **Autosave do form de intake mais robusto** — hoje só 4 campos (nome, telefone, email, onde_nos_conheceu); ideal seria todos os campos preenchidos até então
- **Cron de `convert-abandoned-intakes`** — template pronto, ativar quando testar o fluxo
- **Bot v2**: fallback quando paciente digita algo que não encaixa (ex: "sim" na pergunta de nome) — hoje aceita qualquer string
### Pós-beta (deixar pra depois)
- **(a)** Smoke test infra — cloud Supabase + hospedagem. ~2-3h
- **(b)** Beta fechado com clínicas
- **(c)** Ampliar analytics (conversão por terapeuta, SLA por tag, etc)
---
## 🔧 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` — sem hot reload.
---
## 📦 Commits de hoje (cronológico)
```
f76a2e3 Admin SaaS: ajuste manual de créditos WhatsApp (+/-) com proteção de compras
e1f756e Heartbeat WhatsApp Evolution (Grupo 6.1): detecção + incident + alerta admin
881fa16 Fluxo de reativação de canal WhatsApp + alerta toast sticky + notify owner
e409ba6 Saldo baixo WhatsApp: trigger dispara notificação ao cruzar threshold
5c50db6 Notifications: fallback de polling + catch-up ao focar a aba
6db06ab Toast system_alert ganha botão de ação com deeplink
4441661 Toast system_alert: agregar no catch-up pra não empilhar enxurrada
771b636 SLA de conversas WhatsApp (Grupo 3.4): config + detecção + alerta
4026415 Notifications: não redispara toast pra system_alert antigas após F5
5f51bc0 Fix deeplink /crm/conversas não existe; alias dinâmico por role
f646efe Toast SLA: botão "Abrir conversa" abre drawer direto da thread
64e7634 NotificationItem: resolve alias + botões inline "Conversa"/"Abrir"
36fbc02 Browser notification: click leva pro destino real (drawer ou rota)
adf9208 Analytics 7.1: tempo médio de 1ª resposta WhatsApp no dashboard
0f64381 Fix send-session-reminders comparava provider='evolution' mas DB guarda 'evolution_api'
4e4bac6 6.3 Reconnect automático Evolution antes de abrir incident
c2c42a1 3.7 Bot auto-triagem WhatsApp: config por tenant + hook nos inbound
b8ea292 Grupo 8: agenda ↔ WhatsApp completo (8.2 lembrar manual, 8.3 status→msg, 8.4 lead)
f1c97ee Dashboard SaaS ganha seção de receita de créditos WhatsApp (Asaas)
```
---
## 🛠️ 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`
---
## 📚 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. Amanhã, testes.
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff