80cce772db2a70209b59b79c6e4226637bdd9461
19 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
4da0bc2e11 |
HANDOFF + log: C12 deferred (UX iterar) · testando C13
C12 fluxo critico OK no DB (antecipar/revogar/re-antecipar/realizada
detecta paid). 5 bugs corrigidos no caminho: re-antecipar nao reusa
cancelled, popover watch sync com lookup virtual->materializada,
normalizeForMelissa expoe owner_id, etc.
User adiou C12 pra iterar UX depois (pos-Rail/Clinica). Salvo em
memoria project_c12_antecipar_iterar.
C13 prep: lock "edit cobrada" ja implementado na Fase 6 (commit
|
||
|
|
9ead3fdc42 |
HANDOFF + log: C11 fechado · 4/4 sub-testes OK · proximo C12
Cenario 11 completo. 5 bugs descobertos+corrigidos durante a bateria (UI confusa, gotcha billing_contracts.updated_at, reverse transitions, lock sessao encerrada, label/badge pacote-aware). Reverse trava antecipada de pos-C13 pra ja (user hit pra valer). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
1e74a115de |
HANDOFF: C10 fechado · 5/5 sub-testes OK · proximo C11
Cenario 10 (status change avulsa) completo: - A: Realizada sem markPaid (record pending preservado) - A2: Realizada + markPaid maquininha (paid + paid_at + payment_method) - B: Faltou + multa fixed R$ 30 (original cancelled + nova multa) - C: Cancelado >2h (original cancelled, sem multa) - C2: Cancelado tardio <2h, full charge (original cancelled + nova taxa) Bugs descobertos + corrigidos durante a bateria: cobranca dupla na multa (cancela original agora), _reloadRange not defined no escopo de _buildHandlers, badge $ amber em sessao encerrada, paymentLabel usando ev.price em vez de paymentAmount pra pending, popover permitindo emissao de fatura em sessao cancelada. 3 pendencias pos-C13 mapeadas em memoria + addendum HTML do doc. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
d6423da9b4 |
agenda: pre-C10 fix _applyStatusDecisions cancela pendingRecord
Bug: ao mudar status pra faltou/cancelado com multa configurada
em financial_exceptions, _applyStatusDecisions INSERIA o novo
record da multa MAS deixava o pendingRecord original em pending.
Resultado: cobranca dupla (R$ 200 original + R$ 30 multa = R$ 230).
Fix em useMelissaAgenda.js:1450-1505:
- applyFine agora carrega data da sessao na description ("Multa
por falta - sessao dd/mm/aa") pro paciente identificar na fatura.
- Novo bloco 2b: cancela ctx.pendingRecord quando faltou/cancelado,
com nota de auditoria appendada em notes ("[YYYY-MM-DD] Cancelada
- substituida por multa de no-show" ou similar). Vale tanto pra
caso com multa quanto sem (status mudado sem aplicar multa).
Fix dormente em useAgendaFinanceiro.js:59 ('fixed' -> 'fixed_fee')
- charge_mode no schema eh 'fixed_fee' mas calcChargeAmount usava
'fixed' silenciosamente caia no fallback. Path nao exercitado na
Melissa (usa _applyStatusDecisions, nao handleStatusChange), mas
iria quebrar se algum dia fosse.
Pre-teste C10: financial_exceptions seedadas no DB para tenant
Bruno Terapeuta / owner Leonardo:
- patient_no_show: fixed_fee R$ 30
- patient_cancellation: full, min_hours_notice=2, default_consume_on_miss=true
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
ec0a24f5c8 |
agenda: C9 OK + rowGroup por paciente em /financeiro + bubble cobranca-atualizada
Cenário 9 (Per-session — Michael Balint 12 × R$ 150)
- Testado e passou. 1 rule + 12 agenda_eventos materializadas + 12
financial_records pending. Sem billing_contract. Badge $ em todas as
12 sessões. Conforme esperado.
/melissa/financeiro-lancamentos: agrupado por paciente
- DataTable com rowGroupMode='subheader' + groupRowsBy='patient_id'
- Header de grupo com avatar + nome + badge "N lançamento(s)"
- expandableRowGroups + v-model:expandedRowGroups; watcher popula
todos os grupos da página atual como expandidos (sempre que
recordsGrouped muda — refletindo paginação/filtros)
- Sort outer por nome do paciente, preserva inner order
(pai → filhos de multas/taxas via mesmo agenda_evento_id)
Bubble-up @cobranca-atualizada → M.refetch
- Antes: ao marcar como pago no dialog, o card no FC ficava stale
até trocar de view. AgendaEventoFinanceiroPanel emitia
cobranca-atualizada mas só o loadOccFinancialRecord do dialog
escutava; o _paymentStateMap da agenda nao re-rodava.
- Fix: AgendaEventDialog ganhou _onCobrancaAtualizada que dispara
loadOccFinancialRecord() E emit('cobranca-atualizada') pra cima.
MelissaLayout escuta nos 2 dialogs e chama M.refetch() +
refetchEventosHoje(). Card passa pra borda verde na hora.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
fad1f4ebd4 |
agenda: C8 OK + Usar/Revogar pacote saldo + UI de contract + ajustes UX
Cenário 8 (Pacote SALDO — Otávio Souza Ferreira 12 × R$ 50)
- Testado e passou. DB: 1 rule, 0 events, 1 contract (saldo), 0 records.
Visual: 12 virtuais limpas no calendário.
UI de pacote (saldo + upfront)
- _ruleContractMap em useMelissaAgenda: bulk-load popula contract info
(id, style, totalSessions, sessionsUsed, packagePrice) por
recurrence_id. Query recurrence_rules.patient_id como fonte
autoritativa — cobre saldo sem materializadas (sem isso, ruleToPatient
via records vinha vazio pra saldo)
- normalize injeta `contract` no evento via ruleContractMap
- MelissaEventoPanel: nova linha colorida (violeta saldo, verde upfront)
com "Pacote saldo · N/M usadas" ou "Pacote · N/M realizadas"
- AgendaEventDialog: info card mt-4 com header+body+hint explicando
modelo, gateado por occFinancialLoading (spinner durante carga
pra evitar piscar entre Usar/Revogar)
Handlers Usar/Revogar atômicos
- onUsarSessao em MelissaLayout: materializa virtual (preserva
determined_commitment_id da regra) → status=realizado +
billing_contract_id → create_financial_record_for_session →
sessions_used++ → (se atingiu total) contract.status=completed
- onRevogarSessao: cancela record + sessions_used-- + reativa contract
se estava completed + status=agendado. Bloqueia se record paid
(precisa estorno formal pelo Financeiro)
- Ambos aceitam payload {eventRow, contract} do dialog OU fallback
pra eventoSelecionado do popover
- Botão "Usar" verde no popover (paymentState=none) substituído por
"Revogar" vermelho (paymentState=pending). Equivalente "Usar agora"/
"Revogar uso" no info card do dialog
Fix enum status_evento_agenda
- 'realizada' não existe no enum — DB exige 'realizado' (masculino).
Corrigido em todas as ocorrências do handler
Fix campo "Título" indevido em sessão
- Sessão sem determined_commitment_id → selectedCommitment=null →
isSessionEvent=false → mostra campo Título (que é só pra não-sessão)
- Fix: materialize do Usar inclui determined_commitment_id (insert
path); update path backfilla via query da rule se NULL; Revogar
também backfilla pra consistência
Fix "Gerar fatura" não cabe em saldo
- Botão "Gerar fatura" do popover hide quando há contractInfo. Em
saldo, gerar fatura solta criaria cobrança duplicada sem incrementar
sessions_used. Fluxo correto: "Usar"
Recorrências Aplicadas — UI
- Header stats coloridos: total **azul**, realizadas **verde**,
faltaram **amber**, canceladas **cinza**, remarcadas **violeta**
- Pills com badge sólido por status (emerald-600 realizado, amber-600
faltou, stone-500 cancelado, violet-600 remarcado)
Race condition no dialog
- AgendaEventDialog mostrava botões Usar/Revogar baseado em
occFinancialRecord async; durante ~500ms de load, botão errado
podia piscar. Fix: spinner "Verificando estado…" enquanto
occFinancialLoading=true; botões só renderizam após
- Popover não fixado (race window pequena, fechar/reabrir resolve)
3 decisões UX confirmadas antes de codar
- Editar serviço pago → NÃO (cobrança fiscal imutável)
- Alternar Particular/Convênio/Gratuito em série cobrada → NÃO
- Gerar fatura individual em pacote upfront → NÃO (duplicação)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
1feb7112ff |
agenda: C7 OK + Fase 6 lock-edit ativada em Melissa + cross-week payment propagation
Cenário 7 (Pacote UPFRONT — Ana Souza Ferreira 4×R$ 200 = R$ 800)
- Testado e passou. User criou Ana, pagou os R$ 800 em dinheiro pelo
Financeiro. Borda verde + popover "Pago R$ 800" funcionando.
Fase 6 (lock-edit cobrada) ativada em Melissa
- Removido guard `if (!props.occurrenceMode) return;` em
loadOccFinancialRecord (useAgendaEventLifecycle.js:217+). Agora ele
carrega em ambos modos (Rail/Clínica E Melissa)
- loadOccFinancialRecord SINTETIZA record paid/pending pra siblings de
contrato upfront ativo — assim TODAS as ocorrências da série mostram
"Cobrança paga R$ 800 do pacote" no AgendaEventDialog
- AgendaEventDialog card Sessão/Honorários (flow Melissa) ganhou lock
template: Tag em vez de Select billingType quando occFinancialRecord
existe; Message com cadeado "Cobrança de R$ X já emitida"
- AgendaEventoFinanceiroPanel só renderiza dentro do lock quando record
é REAL (não sintetizado) — evita "Gerar cobrança" indevido em sibling
- paymentSummary do Resumo lateral unificado pra usar occFinancialRecord
(em vez do sessionPaymentRecord paralelo de antes)
Cross-week propagation de pacote upfront
- BUG: ao navegar pra semana só com virtuais (sem reais), bulk-load
caía no else `_rulePaymentMap.value = {}` — virtuais perdiam estado
paid herdado
- FIX em useMelissaAgenda._reloadRange:
* Maps (payment/amount/rule) inicializados SEMPRE no início
* Propagação roda independente de realIds.length (depende só de
ruleIdsInView.size>0, considera reais E virtuais com recurrence_id)
* Query cross-week: pra cada rule em view, busca QUALQUER evento
sibling em qualquer semana + seus records pra determinar estado do
contrato. Encontra o record do pacote mesmo em outra semana
- Saldo NÃO propaga (filter: charging_style='upfront' || NULL); cada
sessão de saldo gera cobrança individual ao realizar
- Memória durável: memory/project_cross_week_propagation.md
Visualização de virtuais cobertas
- MelissaEventoPanel.showPaymentRow: virtuais só escondem quando state
='none'. Com paid/pending herdado, exibem linha colorida
- MelissaAgenda fcEvents: isPaidSession e badge $ pendente removeram
exigência de !is_occurrence. Virtuais herdadas via propagação mostram
borda verde / badge amber
Atalho "Gerar fatura" no popover
- Pill amber pequeno ao lado de "A cobrar R$ X" quando paymentVariant
='none' && !is_occurrence. Click → gerarCobrancaManual direto, fecha
popover pra impedir double-click. Tooltip: "Gerar fatura agora"
- Wire em MelissaLayout via novo emit gerar-cobranca + handler
onGerarCobrancaQuick
Info de pacote no popover
- Header agora mostra "Sessão · Pacote · N sessões" (computed
seriesLabel lê de _raw do rule)
Botão "Excluir série inteira"
- Novo emit delete-series em MelissaEventoPanel + botão ao lado de
"Excluir sessão" quando evento tem recurrence_id
- Handler onDeleteSeries em MelissaLayout: hard delete em 3 etapas
(financial_records pendentes → agenda_eventos materializados →
recurrence_rules CASCADE leva exceptions). Bloqueia se algum record
paid (estorno via Financeiro primeiro)
cancel_session some da agenda
- useRecurrence.expandRules agora pula occurrence com exception.type=
'cancel_session' (era visível com status cancelado; doc dizia que
some). patient_missed/therapist_canceled/holiday_block permanecem
como histórico
recurrence_exceptions cancel idempotente
- MelissaLayout onDeleteEvento usa upsert com onConflict pra exception
cancel — não quebra mais com unique violation em re-cancel
billing_contract_id na 1ª materializada
- _createPackageContract agora .select() o contrato após insert e seta
billing_contract_id no insert da 1ª agenda_eventos materializada
onVerLancamentos cobre virtual de upfront
- Antes virtual sempre toast "Sem lançamentos". Agora busca records via
siblings da série pra encontrar o do pacote. Saldo/sem pacote continua
com toast
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
c23d0a574f |
agenda: C5+C6 testes OK + atalho Gerar fatura + RPC idempotência fix
DB - migration 20260519000001: create_financial_record_for_session passa a ignorar status='cancelled' na idempotência (era bug — cancelar e tentar regerar travava silencioso retornando o cancelado) Cenário 5 (convênio) — fixes pra save + visualização - Convênio: amount lia 'price' (null) → agora detecta via insurance_plan_id e usa insurance_value. payment_method forçado 'convenio' (era 'asaas') - Popover: ev.price era null em convênio → normalize expõe insurance_value e paymentLabel faz fallback. Linha mostra "A receber R$ X" corretamente - /financeiro: branch novo pra payment_method='convenio' → pill violeta com pi-id-card (antes ficava sem indicador, igual particular) Cenário 6 (recorrente sem pacote, Maria Magali) — materialização - chargeMode='none' não materializava a 1ª (todas viravam virtuais, sem badge $). Agora materializa a 1ª no fluxo de criação recorrente - Bug intermediário: usei 'paciente_id' (Portuguese) mas agendaRepository dropa esse campo. Corrigido pra 'patient_id' (English DB column) Atalho "Gerar fatura" no popover - Pill amber pequeno ao lado de "A cobrar R$ X" no popover (paymentVariant ='none' + sessão materializada) - Wire em MelissaLayout via emit gerar-cobranca + handler onGerarCobrancaQuick (chama gerarCobrancaManual, fecha popover pra impedir double-click) - Bulk-load do useMelissaAgenda e fetchRecord do AgendaEventoFinanceiroPanel agora filtram status='cancelled' (resolve badge $ residual + botão sumido) Header do popover: info de pacote/série - "Sessão · Pacote · N sessões" ou "Sessão X de Y" abaixo do tipo (computed seriesLabel lê do _raw da rule) Título do dialog "Sessão do Pacote · Sessão" - Quando commitment name é "Sessão" (default), drop pra evitar duplicação - Outros nomes (Avaliação, etc) permanecem com forma completa Excluir série inteira (popover) - Novo botão "Excluir série" no popover quando evento pertence a recorrência - Hard delete: financial_records pendentes → agenda_eventos materializados → recurrence_rules (CASCADE leva exceptions + rule_services) - Bloqueia se algum record tem status='paid' (estornar primeiro) cancel_session some da agenda - useRecurrence.expandRules agora pula occurrence com exception type= 'cancel_session' (era visível com status cancelado; doc dizia "some da agenda" mas código mantinha. Honra a promessa do diálogo) - patient_missed / therapist_canceled / holiday_block permanecem visíveis como histórico UX outros - "+ Novo convênio" toolbar em ConfiguracoesConveniosPage (botão faltava — empty state mandava clicar em botão inexistente) - InsurancePlanServiceQuickCreateDialog: cadastrar procedimento POR CIMA do AgendaEventDialog sem sair da agenda. Auto-seleciona quando nada estava selecionado antes - Hint contextual abaixo do card Sessão/Honorários: convênio = "Nº guia opcional"; gratuito = "sem cobrança". Particular sem hint - recurrence_exceptions cancel agora usa upsert com onConflict (idempotente, não quebra com unique violation em re-cancel) - goToConveniosConfig removida (dead code após quick-create inline) CSS - .aed-row-50 perdeu margin-bottom (user request) - .field-card.mb-4 ganhou margin-top: 1rem (scoped a composer wrappers) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
e95ed9b585 |
agenda: Fase 5 (status change/edit cobrada) + indicadores visuais + UX convenio
DB
- drop agenda_excecoes (substituida por financial_exceptions + lock-edit
baseado em financial_records)
- financial_records.payment_link (Asaas + link compartilhavel)
- financial_exceptions.consume_on_miss (rotular nao-show consome ou nao)
- billing_contracts.charging_style (upfront/saldo/per_session)
Payment refactor
- paymentSettlement -> paymentMethod (string) + markPaidNow (bool).
Handler aplica payment_method sempre; status='paid'+paid_at apenas
quando markPaidNow=true && method != 'link'. Asaas (link) sempre
liquida via webhook, nunca nasce paid.
- create_financial_record_for_session com pos-RPC patch pra payment_method
e (opcional) status='paid' quando user marca "ja recebi".
Indicadores visuais (3 canais distintos por estado)
- Paid: barra esquerda emerald-500 4px na agenda (MelissaAgenda),
pi-check-circle no popover/Resumo.
- Pending: badge \$ amber canto direito (mantido); linha amber no popover/
Resumo "A receber R\$ X (cobranca pendente)".
- Neutro: sem badge nem barra (compromisso pessoal, bloqueio, ou
ocorrencia virtual de pacote upfront/saldo).
- Bulk-load de paymentState em _reloadRange etapa 4 (1 query unica em
financial_records mapeada por agenda_evento_id).
- AgendaEventDialog Resumo lateral ganha linha entre pi-clock e
pi-map-marker via novo sessionPaymentRecord (sem guard de
occurrenceMode, contrario ao occFinancialRecord que continua so pra
Rail/Clinica). 5 estados: paid+paid_at, overdue+venceu, pending+vence,
sem cobranca c/ valor, sem cobranca s/ valor.
UX de convenio
- InsurancePlanServiceQuickCreateDialog novo: cadastra procedimento
POR CIMA do AgendaEventDialog sem sair da agenda. Auto-seleciona
novo procedimento so quando nada estava selecionado antes.
- Caixa cinza "Cadastrar procedimento" sempre visivel quando convenio
selecionado, com copy variavel (0 procedimentos: chamada urgente;
1+: "se quiser adicionar mais").
- "+ Novo convenio" toolbar em ConfiguracoesConveniosPage (botao
estava faltando, empty state mandava clicar em botao inexistente).
- Hint contextual abaixo do card Sessao/Honorarios: convenio = "N da
guia eh opcional", gratuito = "sem cobranca", particular = sem hint.
Label "N da Guia" tambem ganhou "(opcional)" no service-picker dialog.
Bug fixes
- pickDbFields whitelist faltando 'modalidade' (useMelissaAgenda.js:74)
— sessoes avulsas eram salvas como presencial independente da
escolha visual. Adicionado.
- goToConveniosConfig removida — fazia router.push("/therapist/
configuracoes/convenios") mas /configuracoes/* eh rota raiz, nao
filha. Substituida pelo quick-create inline (#1).
- bloqueioCobrindo + dialogBlockOverlap passados via deps em
_buildHandlers (refs do useMelissaAgenda nao sao acessiveis no
escopo de _buildHandlers).
Fase 5 (status change + edit cobrada)
- AgendaStatusChangeConfirmDialog: confirm dialog quando user muda
status pra realizada/faltou/cancelado, com opcoes de markPaid ou
gerar cobranca conforme o caso.
- useAgendaBloqueios novo composable: extrai logica de bloqueios
cinza (background events) do MelissaAgenda.
Doc viva
- src/docs/agenda-compromisso-financeiro-cenarios.html: 13 cenarios
de teste manual. C1-C4 ja validados. Cada teste validado vira parte
da doc final pra area de ajuda (pos-Fase 9).
Wiki/handoff
- agenda-compromisso-fluxo e agenda-billing-pesquisa-mercado (decisoes
arquiteturais sobre billing).
- HANDOFF.md atualizado.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
646cec5833 |
HANDOFF: estado completo MelissaPaciente Fases 1-8 + iteracao pos-Fase 8
Reescreve HANDOFF.md com: - Status final: 24 commits no branch, working tree limpa - Historico completo dos commits (mais recente -> mais antigo) - Lista de arquivos novos/modificados (composables, utils, paginas) - Pendentes pra proxima sessao - 5 decisoes arquiteturais documentadas - Hotspots de drift no AgendaEventDialog - Comandos uteis pra retomar Adiciona entry no log.md descrevendo a iteracao pos-Fase 8 (16 commits de UX/funcionalidades novas + debugging do AgendaEventDialog reuse). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
97b0ec1ec5 |
HANDOFF + log atualizados pra sessao 2026-05-06
- HANDOFF.md reescrito refletindo estado atual: working tree limpa, 5 commits criados na sessao, resumo do que foi feito (6 Melissa Pages blueprint + dialogs harmonizados + ConversationDrawer WhatsApp + bug fix de cores no MelissaPacientes), e o que continua pendente (A66 V2 design aguardando feedback + restore na PatientsListPage) - Obsidian/Brain/log.md: entrada da sessao 05-06 anexada com detalhes e referencias dos 5 commits Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
15103eded5 |
Cleanup: backups antigos removidos + dashboard config + HANDOFF/log
- Remove database-novo/backups/2026-03-27 e 2026-03-29 (deveriam estar no gitignore, mas haviam sido tracked antes) - Atualiza db.config.json + generate-dashboard.cjs + dashboard.html - HANDOFF.md atualizado com estado de 05-05 (sprint blueprint tabular + arquivamento de pacientes) - Obsidian/Brain/log.md: entrada da sessao 05-05 adicionada Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
86311ef305 |
Melissa: hub Configuracoes + Embed + 9 Pages novas + dialog blueprint dark
Sprints 04-29 + 04-30 acumuladas. - MelissaConfiguracoes: hub 2-col com 6 grupos (Layout/Conta/Agenda/ Financeiro/WhatsApp/Sistema), tudo embedado via MelissaEmbed. - MelissaEmbed: wrapper generico que injeta layout-variant=melissa e remove cromos pra reaproveitar Pages tradicionais. - 9 Melissa Pages novas: CadastrosRecebidos, Compromissos, Configuracoes, Conversas, Embed, Grupos, Medicos, Recorrencias, Tags. - Dialog blueprint atualizado: bg-gray-100 (hardcoded light) -> bg-[var(--surface-ground)] (tema-aware). 22 dialogs migrados em 9 arquivos. Anti-pattern documentado. - PatientsCadastroPage: bug fix dropdown Grupo (optionLabel nome->name), toggle vertical/abas com persist localStorage, sticky margin-top. - Surface picker no popover do MelissaLayout (8 swatches). - useTopbarPlanMenu, useMelissaWhatsapp, useMelissaPacientesAside novos. - Migration: status agenda remarcado/confirmado. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
ff3695fbb1 |
HANDOFF 2026-04-27: bug Teleport + B1 toolbar + B2 stats; B3 pendente teste
Sessão de domingo curta. Bug do chip resolvido pela manhã, polimento da Agenda à tarde (toolbar + stats interativos), à noite extração do MelissaEventoPanel novo (não testado em browser, fica pra amanhã). Working tree não commitado: B3 (MelissaEventoPanel novo + handlers no MelissaLayout + patient_id no normalize + defineExpose). Ver seção "PENDENTE DE TESTE" no HANDOFF pra plano de validação. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
f2b15ce0f7 |
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> |
||
|
|
463d71ce44 | HANDOFF 2026-04-23: resumo da sessão + roteiro de testes pra amanhã | ||
|
|
2644e60bb6 |
CRM WhatsApp Grupo 3 completo + Marco A/B (Asaas) + admin SaaS + refactors polimórficos
Sessão 11+: fechamento do CRM de WhatsApp com dois providers (Evolution/Twilio),
sistema de créditos com Asaas/PIX, polimorfismo de telefones/emails, e integração
admin SaaS no /saas/addons existente.
═══════════════════════════════════════════════════════════════════════════
GRUPO 3 — WORKFLOW / CRM (completo)
═══════════════════════════════════════════════════════════════════════════
3.1 Tags · migration conversation_tags + seed de 5 system tags · composable
useConversationTags.js · popover + pills no drawer e nos cards do Kanban.
3.2 Atribuição de conversa a terapeuta · migration 20260421000012 com PK
(tenant_id, thread_key), UPSERT, RLS que valida assignee como membro ativo
do mesmo tenant · view conversation_threads expandida com assigned_to +
assigned_at · composable useConversationAssignment.js · drawer com Select
filtrável + botão "Assumir" · inbox com filtro aside (Todas/Minhas/Não
atribuídas) e chip do responsável em cada card (destaca "Eu" em azul).
3.3 Notas internas · migration conversation_notes · composable + seção
colapsável no drawer · apenas o criador pode editar/apagar (RLS).
3.5 Converter desconhecido em paciente · botão + dialog quick-cadastro ·
"Vincular existente" com Select filter de até 500 pacientes · cria
telefone WhatsApp (vinculado) via upsertWhatsappForExisting.
3.6 Histórico de conversa no prontuário · nova aba "Conversas" em
PatientProntuario.vue · PatientConversationsTab.vue com stats (total /
recebidas / enviadas / primeira / última), SelectButton de filtro, timeline
com bolhas por direção, mídia inline (imagem/áudio/vídeo/doc via signed
URL), indicadores ✓ ✓✓ de delivery, botão "Abrir no CRM".
═══════════════════════════════════════════════════════════════════════════
MARCO A — UNIFICAÇÃO WHATSAPP (dois providers mutuamente exclusivos)
═══════════════════════════════════════════════════════════════════════════
- Página chooser ConfiguracoesWhatsappChooserPage.vue com 2 cards (Pessoal/
Oficial), deactivate via edge function deactivate-notification-channel
- send-whatsapp-message refatorada com roteamento por provider; Twilio deduz
1 crédito antes do envio e refunda em falha
- Paridade Twilio (novo): módulo compartilhado supabase/functions/_shared/
whatsapp-hooks.ts com lógica provider-agnóstica (opt-in, opt-out, auto-
reply, schedule helpers em TZ São Paulo, makeTwilioCreditedSendFn que
envolve envio em dedução atômica + rollback). Consumido por Evolution E
Twilio inbound. Evolution refatorado (~290 linhas duplicadas removidas).
- Bucket privado whatsapp-media · decrypt via Evolution getBase64From
MediaMessage · upload com path tenant/yyyy/mm · signed URLs on-demand
═══════════════════════════════════════════════════════════════════════════
MARCO B — SISTEMA DE CRÉDITOS WHATSAPP + ASAAS
═══════════════════════════════════════════════════════════════════════════
Banco:
- Migration 20260421000007_whatsapp_credits (4 tabelas: balance,
transactions, packages, purchases) + RPCs add_whatsapp_credits e
deduct_whatsapp_credits (atômicas com SELECT FOR UPDATE)
- Migration 20260421000013_tenant_cpf_cnpj (coluna em tenants com CHECK
de 11 ou 14 dígitos)
Edge functions:
- create-whatsapp-credit-charge · Asaas v3 (sandbox + prod) · PIX com
QR code · getOrCreateAsaasCustomer patcha customer existente com CPF
quando está faltando
- asaas-webhook · recebe PAYMENT_RECEIVED/CONFIRMED e credita balance
Frontend (tenant):
- Página /configuracoes/creditos-whatsapp com saldo + loja + histórico
- Dialog de confirmação com CPF/CNPJ (validação via isValidCPF/CNPJ de
utils/validators, formatação on-blur, pré-fill de tenants.cpf_cnpj,
persiste no primeiro uso) · fallback sandbox 24971563792 REMOVIDO
- Composable useWhatsappCredits extrai erros amigáveis via
error.context.json()
Frontend (SaaS admin):
- Em /saas/addons (reuso do pattern existente, não criou página paralela):
- Aba 4 "Pacotes WhatsApp" — CRUD whatsapp_credit_packages com DataTable,
toggle is_active inline, dialog de edição com validação
- Aba 5 "Topup WhatsApp" — tenant Select 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 · 3 modos de schedule (agenda das regras semanais, business_hours
custom, custom_window) · cooldown por thread · respeita opt-out · agora
funciona em Evolution E Twilio (hooks compartilhados)
2.4 Lembretes de sessão · conversation_session_reminders_settings +
_logs · edge send-session-reminders (cron) · janelas 24h e 2h antes ·
Twilio deduz crédito com rollback em falha
═══════════════════════════════════════════════════════════════════════════
GRUPO 5 — COMPLIANCE (LGPD Art. 18 §2)
═══════════════════════════════════════════════════════════════════════════
5.2 Opt-out · conversation_optouts + conversation_optout_keywords (10 system
seed + custom por tenant) · detecção por regex word-boundary e normalização
(lowercase + strip acentos + pontuação) · ack automático (deduz crédito em
Twilio) · opt-in via "voltar", "retornar", "reativar", "restart" ·
página /configuracoes/conversas-optouts com CRUD de keywords
═══════════════════════════════════════════════════════════════════════════
REFACTOR POLIMÓRFICO — TELEFONES + EMAILS
═══════════════════════════════════════════════════════════════════════════
- contact_types + contact_phones (entity_type + entity_id) — migration
20260421000008 · contact_email_types + contact_emails — 20260421000011
- Componentes ContactPhonesEditor.vue e ContactEmailsEditor.vue (add/edit/
remove com confirm, primary selector, WhatsApp linked badge)
- Composables useContactPhones.js + useContactEmails.js com
unsetOtherPrimaries() e validação
- Trocado em PatientsCadastroPage.vue e MedicosPage.vue (removidos campos
legados telefone/telefone_alternativo e email_principal/email_alternativo)
- Migration retroativa v2 (20260421000010) detecta conversation_messages
e cria/atualiza phone como WhatsApp vinculado
═══════════════════════════════════════════════════════════════════════════
POLIMENTO VISUAL + INFRA
═══════════════════════════════════════════════════════════════════════════
- Skeletons simplificados no dashboard do terapeuta
- Animações fade-up com stagger via [--delay:Xms] (fix specificity sobre
.dash-card box-shadow transition)
- ConfirmDialog com group="conversation-drawer" (evita montagem duplicada)
- Image preview PrimeVue com botão de download injetado via MutationObserver
(fetch + blob para funcionar cross-origin)
- Áudio/vídeo com preload="metadata" e controles de velocidade do browser
- friendlySendError() mapeia códigos do edge pra mensagens pt-BR via
error.context.json()
- Teleport #cfg-page-actions para ações globais de Configurações
- Brotli/Gzip + auto-import Vue/PrimeVue + bundle analyzer
- AppLayout consolidado (removidas duplicatas por área) + RouterPassthrough
- Removido console.trace debug que estava em watch de router e queries
Supabase (degradava perf pra todos)
- Realtime em conversation_messages via publication supabase_realtime
- Notifier global flutuante com beep + toggle mute (4 camadas: badge +
sino + popup + browser notification)
═══════════════════════════════════════════════════════════════════════════
MIGRATIONS NOVAS (13)
═══════════════════════════════════════════════════════════════════════════
20260420000001_patient_intake_invite_info_rpc
20260420000002_audit_logs_lgpd
20260420000003_audit_logs_unified_view
20260420000004_lgpd_export_patient_rpc
20260420000005_conversation_messages
20260420000005_search_global_rpc
20260420000006_conv_messages_notifications
20260420000007_notif_channels_saas_admin_insert
20260420000008_conv_messages_realtime
20260420000009_conv_messages_delivery_status
20260421000001_whatsapp_media_bucket
20260421000002_conversation_notes
20260421000003_conversation_tags
20260421000004_conversation_autoreply
20260421000005_conversation_optouts
20260421000006_session_reminders
20260421000007_whatsapp_credits
20260421000008_contact_phones
20260421000009_retroactive_whatsapp_link
20260421000010_retroactive_whatsapp_link_v2
20260421000011_contact_emails
20260421000012_conversation_assignments
20260421000013_tenant_cpf_cnpj
═══════════════════════════════════════════════════════════════════════════
EDGE FUNCTIONS NOVAS / MODIFICADAS
═══════════════════════════════════════════════════════════════════════════
Novas:
- _shared/whatsapp-hooks.ts (módulo compartilhado)
- asaas-webhook
- create-whatsapp-credit-charge
- deactivate-notification-channel
- evolution-webhook-provision
- evolution-whatsapp-inbound
- get-intake-invite-info
- notification-webhook
- send-session-reminders
- send-whatsapp-message
- submit-patient-intake
- twilio-whatsapp-inbound
═══════════════════════════════════════════════════════════════════════════
FRONTEND — RESUMO
═══════════════════════════════════════════════════════════════════════════
Composables novos: useAddonExtrato, useAuditoria, useAutoReplySettings,
useClinicKPIs, useContactEmails, useContactPhones, useConversationAssignment,
useConversationNotes, useConversationOptouts, useConversationTags,
useConversations, useLgpdExport, useSessionReminders, useWhatsappCredits
Stores: conversationDrawerStore
Componentes novos: ConversationDrawer, GlobalInboundNotifier, GlobalSearch,
ContactEmailsEditor, ContactPhonesEditor
Páginas novas: CRMConversasPage, PatientConversationsTab, AddonsExtratoPage,
AuditoriaPage, NotificationsHistoryPage, ConfiguracoesWhatsappChooserPage,
ConfiguracoesConversasAutoreplyPage, ConfiguracoesConversasOptoutsPage,
ConfiguracoesConversasTagsPage, ConfiguracoesCreditosWhatsappPage,
ConfiguracoesLembretesSessaoPage
Utils novos: addonExtratoExport, auditoriaExport, excelExport,
lgpdExportFormats
Páginas existentes alteradas: ClinicDashboard, PatientsCadastroPage,
PatientCadastroDialog, PatientsListPage, MedicosPage, PatientProntuario,
ConfiguracoesWhatsappPage, SaasWhatsappPage, ConfiguracoesRecursosExtrasPage,
ConfiguracoesPage, AgendaTerapeutaPage, AgendaClinicaPage, NotificationItem,
NotificationDrawer, AppLayout, AppTopbar, useMenuBadges,
patientsRepository, SaasAddonsPage (aba 4 + 5 WhatsApp)
Routes: routes.clinic, routes.configs, routes.therapist atualizados
Menus: clinic.menu, therapist.menu, saas.menu atualizados
═══════════════════════════════════════════════════════════════════════════
NOTAS
- Após subir, rodar supabase functions serve --no-verify-jwt
--env-file supabase/functions/.env pra carregar o módulo _shared
- WHATSAPP_SETUP.md reescrito (~400 linhas) com setup completo dos 3
providers + troubleshooting + LGPD
- HANDOFF.md atualizado com estado atual e próximos passos
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
037ba3721f |
HANDOFF.md atualizado para Sessoes 1-10 + proxima sessao A#31-rev
Documento "ler primeiro ao voltar" reflete estado atual: 15 areas auditadas, zero critico/alto aberto, A#31 reformulada como "Preparacao pra deploy" (MVP nao tem cloud Supabase nem secrets reais). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
7c20b518d4 |
Sessoes 1-6 acumuladas: hardening B2, defesa em camadas, +192 testes
Repositorio estava ha ~5 sessoes sem commit. Consolida tudo desde
|