701d9f4fcc78b27ab29b12ab0002edbdeac20e0c
17 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
89bf181742 |
melissa/paciente-docs: wire-up preview actions + Editar abre dialog em modo edicao
DocumentPreviewDialog emitia @download/@edit/@share/@sign/@delete que o MelissaPatientDocuments nao ouvia — os 5 botoes da sidebar do preview caiam no vazio. Adicionado wire-up roteando pros mesmos handlers do card (onDownload, onEdit, onShare, onSign, onDelete). Share/sign/delete fecham o preview antes de abrir o proprio dialog pra UX limpa; download mantem preview aberto (acao instantanea). DocumentGenerateDialog ganha prop editing-doc-id. Quando setado: - Busca template_id + dados_preenchidos via loadGeneratedFromDocId - Pre-seleciona template, popula vars (sobrescreve auto-loaded vars com dados_preenchidos pra preservar customizacao anterior) - Pula direto pra step 'edit' - Save vira UPDATE in-place (preserva documents.id e audit trail) - Header muda pra "Editar documento" + icone pi-pencil amber - Botao final vira "Substituir documento" - Doc sem registro generated (legado): toast info + flow normal de select template; ao salvar, cria o registro generated linkado. MelissaPatientDocuments: - onEdit substituido (era shortcut pra onPreview): abre generate dialog com editing-doc-id setado. - Novo ref editingDoc dedicado (separado do selectedDoc que serve preview/share/sign/delete) pra evitar vazar "edit state" pro botao "Gerar" do header quando user so abre preview e fecha. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
342defecde |
documents/generate: suporte a edicao in-place + linkage documento_id
document_generated.documento_id (FK pra documents) estava sempre NULL no INSERT — sem isso nao da pra rastrear qual generated belongs to qual documents row, impossibilitando re-edicao. DocumentGenerate.service saveGeneratedDocument: - Modo create (default): INSERT em documents PRIMEIRO pra capturar doc.id, depois INSERT em document_generated com documento_id setado. - Modo edit (editingDocId param novo): UPDATE in-place — substitui PDF no Storage (novo path), atualiza bucket_path/tamanho/nome em documents (preserva id+audit), atualiza dados_preenchidos+pdf_path em document_generated. Se nao houver registro generated (doc legado), INSERT vinculando ao documents.id. Cleanup best-effort do PDF antigo. - Nova fn loadGeneratedFromDocId(documentoId): busca template_id + dados_preenchidos pra pre-popular o dialog de edicao. useDocumentGenerate.generateAndSave: ganha 2o param editingDocId que passa pro service. Backfill SQL pra docs antigos: match dg.pdf_path = d.bucket_path + tenant/patient guard. 3 docs linkados no DB local, 5 ficaram orfaos (paths que nao existem mais em documents — cleanup antigo). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
61bb0d9c26 |
documents/generate: FloatLabel + map de origem nos inputs
Dois problemas reportados no dialog "Gerar documento":
1. Inputs usavam <label> + <InputText> simples, fora do padrao
FloatLabel adotado no resto do app.
2. Quando o auto-preenchimento vinha vazio o user nao tinha onde
ir cadastrar o dado.
Mudancas:
- TEMPLATE_VARIABLES ganha campo `source` em cada entrada com a
descricao de onde o dado eh cadastrado (ex: "Perfil -> Registro
Profissional"). Map canonico no DocumentTemplates.service.js.
- DocumentGenerateDialog refatorado:
* FloatLabel variant="on" em todos os inputs
* Banner no topo com contagem "X de Y preenchidos" (verde se 100%,
amber se faltam dados)
* Hint (`pi pi-link` + texto source) embaixo de cada campo vazio
apontando onde cadastrar
* Erro de carregamento renderizado dentro do step edit
* Input ganha `invalid` quando vazio (borda destaque)
- useDocumentGenerate.loadVariables:
* console.error em caso de excecao (era engolido em silencio)
* mensagem amigavel quando loadAllVariables retorna tudo vazio
(caso comum quando paciente/perfil/clinica estao incompletos)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
79425a3c9a |
templates/editor: variavel mobile insere na posicao do cursor
Antes: variavel inseria sempre no fim do texto no mobile.
Causa: usavamos append direto no form (form[field] += tag) porque
o foco estava no drawer e Jodit.insertHTML travava.
Fix: capturar selection ANTES do drawer abrir, restaurar antes de
inserir.
JoditEmailEditor expose API estendida:
- saveSelection() -> retorna markers (jodit.selection.save())
- restoreSelection(markers) -> re-foca editor + restaura markers
- focus() -> foca o editor
DocumentTemplateEditor:
- ref savedSelection capturada em openDrawer('vars'): snapshot dos
markers do Jodit no momento (cursor original)
- insertVariable mobile: setTimeout 280ms apos fechar drawer ->
restaura markers -> insertHTML (cursor volta pra onde estava ->
variavel aparece no ponto exato)
- Fallback append no form se restore falhar
- savedSelection limpa em fecharDrawer + apos insert
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
87a1ac1358 |
templates/editor: defer insercao pos-transicao + pointer-events na saida
Trava persistia mesmo apos fix anterior. Causa raiz: append no form ocorria durante a transicao CSS do drawer (250ms slide out). Vue reagia ao v-model -> Jodit re-renderia HTML com nova variavel -> concorrencia entre repaint do drawer saindo + reflow do Jodit mobile = trava. Fix: 1. setTimeout(280ms) — append no form so executa DEPOIS que a transicao do drawer terminou. Drawer sai limpo, depois Jodit re-renderiza isolado. 2. CSS: .dte-mobile-drawer:not(.is-open) ganha pointer-events:none durante saida. Evita captura de touch/click "perdidos" que tentavam triggerar handlers no drawer ja saindo. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
6860628087 |
templates/editor: fix trava do drawer ao inserir variavel no mobile
Bug: ao clicar numa variavel no drawer mobile, a variavel era inserida mas o drawer travava/bugava. Causa: editor.insertHTML(tag) do Jodit tenta resolver selection/cursor — no mobile, foco esta nos botoes do drawer, nao no editor, entao Jodit fica em loop tentando encontrar posicao. Fix: - Detecta isMobile e usa append direto via v-model (form.value[field] += tag) em vez de editor.insertHTML - Fecha o drawer ANTES da insercao pra Jodit reconciliar com v-model na proxima tick - No desktop, comportamento original (insertHTML mantem posicao do cursor) permanece Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
134f562a1f |
templates/editor: drawer mobile com form + variaveis (tabs)
Mobile (<1024px): so o editor (col 2) fica visivel. Form de
metadados (col 1) e variaveis (col 3) viram tabs dentro de um
drawer fixed que abre pela esquerda.
Padrao espelhado de MelissaBloqueios/MelissaDocumentosTemplates,
com adaptacoes pra ser autocontido (sem dependencia do componente
pai).
Script:
- drawerOpen + drawerTab ('form' | 'vars') + isMobile refs
- _mqMobile matchMedia listener (onMounted setup +
onBeforeUnmount cleanup)
- openDrawer(tab) / fecharDrawer helpers
- insertVariable agora fecha o drawer no mobile apos inserir
Template:
- Drawer wrap no inicio: tabs (Identificacao / Variaveis) +
botao close + 2 panes (#dte-mobile-drawer-form e
#dte-mobile-drawer-vars)
- Backdrop overlay com blur fecha o drawer
- Toolbar do editor ganha 2 botoes mobile-only (Identificacao /
Variaveis) com classe dte-toolbar__mobile-actions
- <Teleport to="#dte-mobile-drawer-form" :disabled="!isMobile">
envolvendo a <aside class="dte-side">
- <Teleport to="#dte-mobile-drawer-vars" :disabled="!isMobile">
envolvendo a <aside class="dte-vars">
CSS:
- .dte-mobile-drawer fixed left, transform translateX, 250ms
- 2 panes scroll interno separado
- @media (max-width:1023px): cols vira 1-col, side/vars inline
somem, botoes mobile aparecem, titulo canonico some
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
c9afe8f009 |
templates/preview: background do papel cobre 100% da altura
Bug: container .dte-preview era display:flex + justify-content:center. Em alguns navegadores, o flex limitava a altura intrinseca do .dte-preview__doc (papel) quando o conteudo crescia — background branco ficava com a altura do menor item e o conteudo "vazava". Fix: container vira block normal com overflow-y:auto. Doc centralizado via margin:0 auto (em vez de justify-content). Adiciona box-sizing:border-box + height:auto + overflow:visible no doc pra garantir que o background cresce com o conteudo. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
0aabea7753 |
templates/editor: corpo do editor ocupa 100% da altura disponivel
Antes: minHeight 450 em pixel fixo do Jodit limitava o corpo — sobrava area vazia abaixo do editor. Fix CSS-only (sem mexer no JoditEmailEditor compartilhado): - .dte-main__editor: overflow hidden + flex column (era overflow-y auto). O scroll passa pra dentro do Jodit (workplace). - .dte-editor-wrap: flex 1 + min-height 450 (preserva minimo). - :deep(.jodit-container/workplace/wysiwyg) force flex + height 100% + min-height 0/100% pra anular o height: 450px que o Jodit seta inline. Resultado: editor sempre preenche toda area disponivel da COL 2, expande/contrai com a janela, e o scroll do conteudo fica dentro do proprio editor (jodit-wysiwyg). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
80cce772db |
templates/editor: layout 3-col + tabs cabecalho/corpo/rodape
Refatora DocumentTemplateEditor em 3 colunas seguindo padrao
MelissaAgendaConfig:
- COL 1 (esquerda, 240-280px): form de metadados — nome, tipo,
descricao (Textarea com autoResize), URL do logo
- COL 2 (centro, flex 1): sub-tabs Cabecalho/Corpo/Rodape, 1
editor visivel por vez. Cada editor com minHeight: 450px (era
120/350/120). Tab ativa destacada com border-bottom primary +
background sutil.
- COL 3 (direita, 220-260px): variaveis agrupadas por categoria,
hint dinamico mostrando qual sub-tab esta ativa ("Clique para
inserir no Cabecalho/Corpo/Rodape"). Botoes com {{ }} braces
em monospace + cor primary.
Scroll interno:
- .dte-page flex column, gap 12, min-height 0, padding 12
- Cada coluna eh card (border + radius) com header sticky + body
scrollable interno (overflow-y: auto, scrollbar-width: thin)
- Variaveis com max-height proprio + scroll interno
Mobile (<1024px):
- 3-col vira 1-col stacked
- Container do .dte-cols ganha overflow-y auto (scroll da pagina
inteira em vez de scroll interno em cada coluna)
- Variaveis ganha max-height 320px pra nao ocupar a tela toda
Preview (toggle no topo):
- Documento A4-like centralizado (max-width 794px ≈ 96dpi)
- Padding 48/56px, shadow sutil
- Mobile: padding reduzido pra max width disponivel
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
6ae651a8ae |
roadmap #14: recibo profissional PDF — gerador + quick path da agenda
ROADMAP item #1.4 #14. Fecha Fase 1.4 Fiscal minimo (parcial — #15 NFS-e fica pra depois). DocumentGenerate.service estendido: - loadTherapistData puxa registro profissional (#5 migration) e expoe terapeuta_registro auto-formatado ("CRP 12345/SP", "CRM 98765/RJ"). terapeuta_crp legacy mantido por compat — preenche somente quando tipo=CRP. - loadClinicData formata tenants.cpf_cnpj (11 ou 14 digitos) em CPF (XXX.XXX.XXX-XX) ou CNPJ (XX.XXX.XXX/XXXX-XX). - loadAllVariables aceita {extras} (valor, formaPagamento) e computa valor_extenso via novo helper utils/valorExtenso.js (pt-BR completo ate 999 milhoes). - saveGeneratedDocument ganha templateTipo + usa TEMPLATE_TYPE_TO_DOC_TYPE mapping (recibo_pagamento -> 'recibo', laudo -> 'laudo', atestado -> 'atestado' etc) em vez de hardcoded 'laudo'. - emitirReciboParaSessao(eventoId, opts) — quick path one-call: busca template recibo_pagamento global, carrega variaveis, gera PDF blob, salva no Storage + documents + document_generated, dispara download. Migration 20260521000008 substitui no template recibo_pagamento "Psicologo(a) - CRP {{terapeuta_crp}}" por "{{terapeuta_registro}}" e atualiza variaveis[]. Universal — funciona com qualquer conselho (CRP/CRM/CRFa/CREFITO/CRESS/CRN). DocumentTemplates.service.TEMPLATE_VARIABLES ganha terapeuta_ registro + _tipo + _numero + _uf (terapeuta_crp marcado legacy). useDocumentGenerate.generateAndSave passa templateTipo no save. AgendaEventoFinanceiroPanel ganha botao "Emitir recibo" (icon pi-file-pdf, outlined, full width) que aparece SOMENTE quando record.status === 'paid'. Toast de sucesso/erro. Loading state. Fluxo end-to-end: terapeuta marca sessao como paga -> botao "Emitir recibo" aparece -> click -> PDF baixado + aparece em /clinic/documents/templates do paciente como tipo 'recibo'. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
19caa42f3b |
compliance CFP #7: dialog gera share_link junto com signature
DocumentSignatureDialog (terapeuta-side) ja existia com fluxo de
add signatarios. Estendido pra:
- Checkbox "Gerar link publico para assinatura" (default ON)
- Select de validade (24h/3d/7d/30d, default 7d)
- Apos submit: alem de createSignatureRequests chama createShareLink
e exibe o URL gerado num bloco emerald com botao Copy
- Dialog fica aberto se gerou link (terapeuta copia/envia); fecha
se nao gerou
Fluxo end-to-end agora funcional: terapeuta clica "Solicitar
assinatura" no DocumentsListPage > preenche signatarios > submit
gera signature requests + share_link > copia URL > envia via WA/
email > paciente abre /shared/document/:token > assina via fluxo
publico (RPC sign_document_by_token capturando IP/UA server-side).
Fecha ROADMAP #1.2 #6/#7 — Compliance basico BR completo (#5/#6/#7/
#8/#9 todos verdes, #6 com TCLE + Telehealth + TCLE menores + termo
sigilo + LGPD + autorizacao gravacao).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
4e42881d5e |
compliance CFP #7: portal + fluxo de assinatura no SharedDocumentPage
ROADMAP #1.2 #7 — Assinatura eletronica no portal. Migration 20260521000007 cria RPC list_my_signatures (SECURITY DEFINER) que cruza auth.uid() por 3 caminhos (signatario_id, signatario_email, patient.user_id) e devolve solicitacoes pendentes + share_token pra link de assinatura. service.listMySignatures wrappa a RPC. Composable useDocumentSignatures ganha loadMine(). PortalDocumentos.vue (nova) — lista signatures do paciente logado com KPIs (total/pendentes/assinados/recusados), filtro, e botao "Assinar agora" que navega pra /shared/document/:token. Item no portal.menu "Documentos > Para assinar". SharedDocumentPage.vue estendida: agora chama getSignableDocumentBy Token primeiro (RPC nova). Quando o documento tem signatures pendentes, mostra painel azul abaixo do preview com: - Aviso LGPD/CFP explicando o que sera registrado (IP/UA/timestamp/hash) - Checkbox aceite obrigatorio - Selecao de signatario quando multi-signatario - Botoes Assinar/Recusar com loading state - Computacao SHA-256 server-fetched antes do click Fluxo: terapeuta gera doc -> cria signature + share_link -> link e listado em /portal/documentos -> paciente clica -> /shared/document/ :token mostra doc + painel -> aceite -> assinatura registrada via RPC sign_document_by_token (IP/UA capturados server-side). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
934c620295 |
compliance CFP #7: RPCs de assinatura + service ext + composable
Backend foundation pra assinatura eletronica (ROADMAP #1.2 #7). Migration 20260521000006 cria 3 RPCs: - sign_document_by_signature_id (paciente logado, SECURITY INVOKER) - sign_document_by_token (terceiro via share link, SECURITY DEFINER) - get_signable_document_by_token (preview pre-assinatura) IP + user-agent capturados SERVER-SIDE via inet_client_addr() e current_setting('request.headers'). Hash SHA-256 vem do cliente pra integridade. Token via share link incrementa usos no UPDATE. DocumentSignatures.service estendido com 3 wrappers RPC: signByPortal, signByToken, getSignableDocumentByToken. useDocumentSignatures composable novo (Tipo A blueprint) expoe state reativo + acoes: fetchForDocument, requestSignatures, sign, refuse, signWithToken, loadByToken. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
957e912a7f |
Melissa polish + Prontuario Visao Geral + agenda historico
Sprints B (05-03) e C (05-04) acumulados: - NotificationDrawer/Item redesign (visual mais limpo, ações inline) - Dock pins compose (useMelissaDockPins) + cache store global (melissaCacheStore) - MelissaAgenda: timeline FullCalendar parity + cards resumo, histórico card com useMelissaAgendaHistorico, MelissaEventoPanel ajustado - useFeriados: cache opt-in pra evitar fetch redundante de feriados - PatientProntuario: aba Visão Geral nova; PatientConversationsTab polish - AgendaClinicMosaic / AgendaTerapeutaPage / useAgendaSettings: ajustes de paridade com Melissa - DocumentsListPage: pequenos ajustes - DB migration 20260504000001: fix do trigger pra status 'excluido' nas cancel_notifications Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
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>
|
||
|
|
d088a89fb7 | Documentos Pacientes, Template Documentos Pacientes Saas, Documentos prontuários, Documentos Externos, Visualização Externa, Permissão de Visualização, Render Otimização |