Aplicado como supabase_admin (trigger functions sao owned por supabase_admin).
14 funcoes reescritas pra operar no schema do TG_TABLE_SCHEMA (set_config
search_path dinamico + tenant_id_for_schema p/ tabelas globais audit_logs):
log_audit_change, trg_fn_patient_status_history/timeline/risco,
auto_create_financial_record_from_session, fn_sla_resolve_on_outbound,
fn_clinical_note_version, fn_document_signature_timeline,
fn_documents_timeline_insert, sync_legacy_email/phone_fields,
fn_agenda_regras_semanais_no_overlap, patients_validate_member_consistency.
sync_busy_mirror_agenda_eventos: cross-tenant via tenant_schema_for +
EXECUTE format (espelha "Ocupado" nos schemas das clinicas).
financial_records_inject_tenant: obsoleto, nao anexado nos schemas.
Detach dos 14 schema-aware das tabelas tenant em public (quebrariam la);
attach_schema_aware_triggers recria 22 triggers/schema (defs reais, tenant_id
removido de WHEN/UPDATE OF). agenda_cfg_sync e trg_fn_financial_records_auto_
overdue (agnosticos) ficam em public E nos schemas.
Smoke: sessao->realizado cria financial_record (R$250) no schema + marca billed;
audit roteia tenant_id correto; patient status muda -> timeline no schema.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
attach_agnostic_triggers(schema) recria nos schemas os triggers de public cuja
funcao e provadamente schema-agnostica (so mexe em NEW/OLD): familia updated_at
(8: set_updated_at, fn_clinical_notes_updated_at, set_insurance_plans/medicos/
services_updated_at, set_updated_at_recurrence, update_payment_settings/
professional_pricing_updated_at) + prevent_promoting_to_system +
prevent_system_group_changes. Backfill dos 9 (54 triggers/schema). Smoke:
set_updated_at dispara no schema. Schema-aware vem no Lote B; wiring no clone
no fim da F6.2
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- F6.0 (migration): clone_tenant_template pros 9 tenants existentes (schemas
vazios; dispara trigger F5 -> expostos no PostgREST)
- F6.1 (manual supabase_admin): copia dados public -> schemas com
session_replication_role=replica (desabilita FK check, so supabase_admin).
Tabelas com tenant_id por filtro; 3 filhas sem tenant_id (commitment_services,
insurance_plan_services, recurrence_rule_services) por JOIN no pai; exclui
colunas GENERATED (net_amount, margin_brl); reset de 4 sequences; ON CONFLICT
DO NOTHING (idempotente). Dados continuam em public (DROP so na F6.3)
- Verificado: contagens public vs schemas batem (35 patients, 37 eventos,
355 mensagens, 54 financeiro...); seeds default replicados por schema
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- manual/f5_pgrst_refresh_schemas.supabase_admin.sql: refresh_pgrst_schemas()
(owned supabase_admin, postgres nao e superuser neste stack) seta
pgrst.db_schemas in-database na role authenticator a partir de tenant_schemas
+ NOTIFY pgrst reload config/schema. Expoe/retira schema SEM restart; GUC
persiste entre stop/start
- migration 20260613000002: trigger em tenant_schemas dispara o refresh a cada
clone/drop (clone/drop nao precisam ser tocados)
- config.toml: baseline public,graphql_public + comentario explicando que a
config in-db supersede em runtime
- E2E testado via HTTP: clone -> pgrst.db_schemas inclui tenant_x -> REST
Accept-Profile retorna 200 (vs 406 schema inexistente); drop -> volta 406.
Sem restart de container
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Fluxos anon identificam tenant por token/slug e nao resolvem o schema fisico.
Decisao (opcao C): manter em public com RLS por token. Volta a global:
patient_intake_requests, patient_invites, patient_invite_attempts,
document_share_links, agendador_configuracoes, agendador_solicitacoes.
- migration 20260613000001_f1b: remove as 6 do _tenant_template (template v2,
78 tabelas). Smoke: clone gera 78, zero tabelas anon no schema, drop limpo
- frontend: 38 cadeias em 14 arquivos revertidas tenantDb().from() ->
supabase.from() com tenant_id/owner_id restaurado (via comparacao com main)
- edge: convert-abandoned-intakes restaurada do main (SELECT global)
- save-intake-progress: ja usava public, sem mudanca
- doc F0 atualizado: 78 tenant + 59 global
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- _shared/tenant.ts: helper (adminClient, tenantDbForId, schemaForTenant,
listTenantSchemas, resolveTenantByChannel, tenantSchemaName)
- _shared/whatsapp-hooks.ts: hooks de tabela tenant recebem tdb; RPCs de
credito (deduct/add_whatsapp_credits) e tenant_members seguem em supa+p_tenant_id
- inbound (twilio/evolution): tenant_id da URL -> tdb pra conversation_messages
e notification_channels
- crons de fila (process-notification/email/sms/whatsapp-queue): varrem
listTenantSchemas e drenam a fila de cada schema (Q3: filas sao per-tenant);
modo single-tenant se body.tenant_id vier
- crons reminders/checks (send-session-reminders, conversation-sla-check,
whatsapp-heartbeat-check, convert-abandoned-intakes, sync-email-templates):
loop por tenant
- routing por tenant_id (send-whatsapp-message, send-session-reminder-manual,
twilio-provision, de/reactivate-channel, twilio-webhook): tenantDbForId;
channel-actions sem tenant_id varrem schemas por channel_id
- asaas-*: tenant_id do body -> tdb; asaas-webhook fica global (whatsapp_credit_purchases)
- notification-webhook (Meta): resolve tenant via channel_routing por phone_number_id,
fan-out por message_id quando nao resolve
- caller send-session-reminder-manual passa tenant_id (evento vive no schema)
Pendente: save-intake-progress e fluxos anon por token (decisao de roteamento)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sessao completa de ~14 commits. 2 grandes blocos:
BLOCO 1 — Melissa UI overhaul: tray bottom-right (substitui topbar
band), mobile collapse parcial em <md, busca global unificada
(MelissaBusca ganha "Ir para [data]", popover da agenda deletado),
dock com 4 builtins, hero resumo com cancelado/remarcado, settings+
ajuda click-outside, cronometro evento-aware (botao ⏱ na timeline +
sessionPlan + confirm fechar), documents edit in-place via
document_generated.documento_id, wire-up dos 5 botoes do preview.
BLOCO 2 — 5 docs saas novas (03-07 em development/saas-docs/) +
SQL imports + 60 FAQs total. Cobertura: aba Documentos paciente,
pagina Templates, Assinatura eletronica, Emissao de recibo
profissional, Relatorios + 3 formatos de export.
Memorias adicionadas:
- feedback_tailwind_utility_load_order (hidden perde pra CSS base
do componente por ordem de carga Vite)
- project_documents_reedit_in_place (linkage documento_id + editingDocId)
PROXIMA SESSAO (23/05): Fase 6 restante (C12 antecipar UX iter —
unico item de codigo da lista de ontem), Fase 7 restante (regressao
Agenda C7-C13, validacao manual). Antes/depois: panorama MVP no
ROADMAP canonico — ainda restam #12 papel timbrado, #15 NFS-e,
§1.5 Sentry, Asaas Fase B, M4 cutover, validacao centralizada
de forms.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Doc 07 cobrindo a pagina de Relatorios + 3 formatos de exportacao:
- Layout 2-col (sidebar stats+filtros, main grafico+tabela)
- 4 periodos (semana/mes/3m/6m), agrupamento auto (dia vs week ISO
vs month ISO)
- 5 KPIs clicaveis como filtros (total/realizadas/faltas/canc/remarc)
- Grafico Chart.js com cores por status
- DataTable paginada + status com tag colorida
- Export PDF (HTML->PDF A4, KPIs + tabela)
- Export Excel XLSX (exceljs dinamico, frozen header, alternating
rows, branded, formatos data+currency)
- Export CSV (vanilla, BOM UTF-8, separador ; pt-BR)
- Filtros aplicados na tela respeitados na exportacao
- Nome do arquivo com timestamp pra evitar overwrite
- Notas dev: reportExport.service.js, pdf.service, exceljs lazy load
12 FAQs: como ver, periodos disponiveis, exportar PDF/Excel/CSV,
quando usar qual formato, filtros respeitados, formulas, agrupamento
do grafico, filtrar por paciente, ver outro terapeuta, nome do
arquivo, exportacao agendada (pendente).
categoria='Relatórios', pagina_path='/melissa/relatorios', ordem=7.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Doc 06 cobrindo o quick path de emissao de recibo:
- Quando o botao aparece (AgendaEventoFinanceiroPanel com record
status=paid)
- O que vem auto-preenchido (paciente, sessao, valor, forma pgto,
terapeuta+registro formatado, clinica+CNPJ, data)
- Registro profissional generico — CRP/CRM/CRFa/CREFITO/CRESS/CRN/
Outro (variavel terapeuta_registro auto-formata)
- Valor por extenso (helper valorExtenso.js, ate 999 milhoes)
- Onde fica salvo (download + aba Documentos categoria 'Recibo')
- Quick path emitirReciboParaSessao() vs flow manual de Gerar
- Notas dev: service, helper, mapping, migration do template,
localizacao do botao
12 FAQs cobrindo casos comuns: emitir recibo de sessao paga, por
que botao nao aparece, valor por extenso correto, suporte multi-
conselho, onde salva, recibo avulso, CRP vazio, CNPJ formatado,
corrigir valor, enviar pra assinar, data sessao vs emissao,
reemitir.
categoria='Financeiro', pagina_path='/melissa/agenda', ordem=6.
SQL import em database-novo/tmp/import-doc-recibo-profissional.sql.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Doc 04 cobrindo a pagina de gestao de templates:
- Globais (sistema, read-only) vs Tenant (seus, editaveis)
- Lista em grid com cards + badge "padrao" pros globais
- Preview de template global (iframe sandbox A4) + botao Duplicar
- Criar novo template (nome/tipo/desc/cabecalho/corpo/rodape)
- Editor rich-text com menu de variaveis (insere {{nome_var}})
- Lista de variaveis disponiveis (paciente/terapeuta/clinica/sessao/geral)
- Mobile drawer pros templates
- Duplicar (cria copia em "Seus templates" com sufixo "(copia)")
- Desativar (soft-delete, docs antigos continuam acessiveis)
- Mapeamento tipo template -> categoria do doc gerado
12 FAQs: pra que serve, por que nao edita padroes, como usar variavel,
quais variaveis, recuperar desativado, duplicar pra personalizar,
global vs tenant, imagens (logo/assinatura), cabecalho/rodape em todas
as paginas, variaveis obrigatorias, limites, compartilhamento entre
terapeutas do mesmo tenant.
categoria='Documentos', pagina_path='/melissa/documentos-templates',
ordem=4. SQL import em database-novo/tmp/import-doc-documentos-
templates.sql.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
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>
Script usado pra importar a doc 02-cronometro-melissa.json
diretamente no banco via psql (mesmo padrao da doc Busca global).
DO block com dollar quoting ($HTML$ e $FAQ$) pra evitar escape hell
no HTML conteudo + nos FAQs.
Importacao executada em 2026-05-22. Doc id=e87d4d33-7f5c-454e-a2ff-
0f92505b7c3c + 12 FAQ itens vinculados.
Path: database-novo/tmp/import-doc-cronometro.sql — pasta tmp pra
artefatos de operacao (nao parte do schema canonico).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Doc JSON com 10 secoes cobrindo: 3 jeitos de abrir (hero, timeline,
card proximo paciente), pre-selecao + autostart via evento, exibicao
de programado/atraso (sessionPlan), anatomia do dialog, minimizar
(chip no dock), parar (salva DB) vs fechar (descarta com confirm),
toque no fim, persistencia localStorage, regra "um cronometro por
vez", atividade livre sem paciente, mobile (chip sem nome).
12 FAQs incluindo o caso de uso central (Larissa chegou agora),
comportamento do X com sessao rodando, cronometro multi-aba,
significado do badge 'atrasada Xmin', etc.
categoria='Sessão', pagina_path='/melissa', ordem=2 (busca era 1).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Tray no canto inferior direito (substitui o topbar band do topo):
busca + plan-DEV + bell + ajuda + cog. Sibling de .melissa-dock
(fora de .win11-summary) pra ficar sempre interativo mesmo com
secao aberta (que aplica blur+pointer-none). z-index 66 (acima
do dock=65). Em <md (768px) collapse parcial — bell/help/cog/
plan-DEV somem e viram popup vertical no botao ⋮; dot vermelho
no ⋮ quando ha notificacoes nao lidas. Search sempre visivel.
Dock: 4 builtins na ordem Agenda · Pacientes · WhatsApp · Financeiro
(antes so Agenda+WhatsApp). MRU (max 3) ganha @media (max-width:
767px) display:none — utility 'hidden' do Tailwind perdia pro
.dock-pin{display:grid} por ordem de carga. Divisor entre builtins
e pins user some em mobile se so houver MRU (que ja esta oculto).
Wire-ups das commits anteriores:
- ref melissaBuscaRef + provide('openMelissaBusca') pra acoes
contextuais futuras (botao tray chama direto via ref)
- @goto-date no <MelissaBusca> -> onBuscaGotoDate via _callOnAgenda
- @iniciar-cronometro no <MelissaTimelineHoje> -> handler que abre
o cronometro com sessionPlan + autostart; opcao (b) "ja ativo"
mostra toast warn sem trocar paciente
- Card "Proximo paciente" troca CTA pra "Iniciar cronometro" quando
emCurso E tem patient_id; @open chama o mesmo handler do timeline
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
MelissaCronometro.abrir() agora aceita opts { pacienteId, autostart,
sessionPlan }. Retorna { opened, alreadyRunning, samePaciente, ... }
pra caller decidir o feedback. Estado sessionPlan { startH, endH }
exibe "Programado: HH:MM – HH:MM" sob o select + badge laranja
"atrasada Xmin" quando hNow > startH. Cronometro NAO auto-ajusta —
analista decide quando comecar/parar. Tick a 30s atualiza atraso.
sessionPlan persiste no localStorage junto com o snapshot.
X agora dispara confirmarFechar(): pede ConfirmDialog quando ha
sessao em andamento OU tempo decorrido nao salvo; fecha direto se
clean. Tooltip mudou pra "Encerrar sem salvar".
Chip minimizado: nome do paciente fica display:none em <md (mobile)
pra nao estourar largura do dock — icone + timer cobrem o essencial.
MelissaTimelineHoje: botao ⏱ overlay no canto sup. direito das pills
(horizontal + vertical) quando ev esta em curso E tem patient_id.
Pulso emerald sutil pra chamar atencao; @click.stop pra nao abrir
o evento. Novo emit iniciar-cronometro(ev).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
MelissaBusca ganha parser de data ('hoje', 'amanha', 'ontem',
DD/MM/YYYY) e card destacado azul "Ir para [data]" como primeiro
item do flatList. Quando query parseia como data, pula a RPC
search_global (nao busca paciente com nome '20/06'). Enter sem
selecao explicita pega o primeiro item — UX spotlight padrao.
Novo emit goto-date(date) capturado em MelissaLayout via helper
_callOnAgenda que abre a agenda se fechada e chama gotoDate exposto
pela MelissaAgenda (alias pro onBuscaGotoDate existente).
MelissaAgenda perde o popover proprio (MelissaAgendaSearchPopover
deletado), o ref searchPopover, o hotkey Ctrl+K local e
onBuscaSelectEvento. Ctrl+K agora vive so na MelissaBusca — evita
dois listeners no mesmo atalho. MelissaBusca expoe openDialog via
defineExpose pra a lupa do tray chamar.
MelissaPacientes: comment update mencionando o tray.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Acrescenta sufixo "(x foi cancelado, x foi remarcado)" depois do chip
de atendimentos quando ha sessoes nesses status em eventosHojeReais.
Sufixo nao-clicavel, peso menor pra nao competir com o link do total.
Pluralizacao gramatical (1 foi / 2+ foram).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Popover Personalizar (cog) e drawer de Ajuda agora fecham quando o
user clica em qualquer lugar fora do panel. Listener mousedown em
capture, watch em open pra anexar/desanexar; ignora o proprio botao
trigger (data-ajuda-toggle pro ajuda; cogBtnEl ref pro settings) pra
nao fazer close+reopen. Tambem flipa o panel do settings de top-12
pra bottom-12 (cog agora vive no bottom da .melissa-tray).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 commits em paciente.documentos e documents/generate. Bug raiz dos
"campos vem vazios": isFinite(null) global retorna true, null.toFixed
crashava em loadAllVariables. Trocado por Number.isFinite (strict).
Proxima sessao retoma de Fase 2 (2.7-2.9 gerar PDF dentro da aba
Documentos do paciente).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
loadVariables falhava com TypeError quando nao havia sessao
vinculada (agendaEventoId=null) E o user nao passava extras.valor.
Stack: 'Cannot read properties of null (reading toFixed)'.
Causa: usei isFinite() global em vez de Number.isFinite():
isFinite(null) => true (coerce: Number(null) === 0)
Number.isFinite(null) => false
Como isFinite(null) retorna true, o codigo entrava no branch
`valorNum.toFixed(2)` e crashava. Com isso, loadAllVariables
inteiro estourava e variables.value zerava — explicando os
inputs todos vazios mesmo com paciente/perfil/clinica preenchidos.
Fix: trocar isFinite por Number.isFinite (versao strict, nao
coerce null/undefined/string).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
User reportou que mesmo com profile/paciente/clinica preenchidos
os campos do dialog continuam vazios. Pra diagnosticar:
- Promise.all -> Promise.allSettled: nao mascara falha individual
- console.error por source que falhou (patient/session/therapist/clinic)
- console.log com payload completo em dev mode (ownerId, tenantId,
patientId, agendaEventoId, valores carregados, errors)
Depois de identificar a causa esses logs ficam ou viram telemetria
estruturada.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
Drawer teleportado pro body perdia as vars --m-* (definidas em
.win11-root no MelissaLayout), caia nos fallbacks hardcoded (#1a1d2e)
e ficava mais escuro que o resto do tema.
Fix:
- Wrapper .mpd-drawer-portal recebe class win11-root pra trazer as
vars --m-* pro escopo teleportado.
- Vars locais --mpd-bg/--mpd-border/--mpd-text com cascata:
--m-* (win11-root) -> --p-* (PrimeVue global) -> hardcoded.
Respeita dark/light automaticamente.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bug: drawer abria mas travava — sidebar interna nao aparecia.
Duas causas combinadas:
1. position:fixed preso em stacking context: o MelissaPaciente
tem transform/filter num ancestral, fazendo o fixed virar
relativo ao pai em vez da viewport.
2. Styles scoped: ate corrigir o stacking context, ao teleportar
pro body os data-v scoped attrs sumiriam e o CSS nao aplicaria.
Fix:
- <Teleport to="body"> wrap nos elementos drawer + backdrop. Saem
da arvore do componente e ficam no body raiz.
- Styles do drawer movidos pra um <style> NAO-scoped no fim do
arquivo. Classes globais .mpd-mobile-drawer* garantem que
aplique nos elementos teleportados (que perdem data-v).
- Fallbacks adicionados nas vars CSS (--m-bg-medium, --m-border,
--m-text) caso o body nao tenha o tema melissa carregado.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Antes: <DocumentsListPage embedded /> reusava o componente do
Rail/Classic em modo embed — visual conflitava com o padrao
Melissa, sem agrupamento por tipo, scroll inconsistente.
Novo: MelissaPatientDocuments.vue (componente nativo 2-col
seguindo MelissaDocumentosTemplates):
- Sidebar esquerda: tipos de documento com contadores
(Todos, Laudo, Receita, Exame, Termo assinado, Relatorio
externo, Identidade, Convenio, Declaracao, Atestado,
Recibo, Outro). Item ativo destaca primary; vazios em
opacity 50%.
- Main direita: header com titulo do tipo + count, DataView
com cards (DocumentCard reusado), paginacao automatica >12,
empty states distintos (global vs filtrado).
- Header da pagina: botoes Refresh / Gerar / Upload (primary
outlined no dark-friendly).
- Mobile <1024px: sidebar vira drawer com botao "Tipos" no
header (espelha padrao MelissaBloqueios/Templates).
Reaproveita do features/documents:
- useDocuments composable
- DocumentCard, DocumentUploadDialog, DocumentPreviewDialog,
DocumentGenerateDialog, DocumentSignatureDialog,
DocumentShareDialog
MelissaPaciente.vue: import DocumentsListPage -> Melissa
PatientDocuments + uso na aba.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bug: no melissa, salvar paciente -> "Salvar e ver pacientes" caia
em /pages/access. Causa: patientsListRoute() so tinha branches
/therapist e /admin, jogava na rota errada que o guard rejeita
no contexto melissa.
Fix:
1. PatientCadastroDialog + ComponentCadastroRapido — funcao
renomeada pra patientViewRoute(patientId). Branch /melissa
redireciona pra /melissa/paciente?id=<id> (prontuario individual)
quando ha id, ou /melissa/pacientes (lista) sem id.
2. Botao "Salvar e ver pacientes" -> "Salvar e ver paciente"
(singular). Reflete a navegacao real: vai pro proprio paciente
que acabou de salvar, nao pra lista.
3. onCreated pega data?.id || props.patientId pra montar a rota.
Comportamento melissa: salvar paciente -> abre /melissa/paciente
?id=<id> (prontuario). Therapist/admin segue indo pra lista
(comportamento pre-existente).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Badges .mdt-section__count.is-info e .is-accent tinham o mesmo
problema do botao primary: bg solido com texto branco/cor primary
quebrava o contraste no modo escuro (texto sumia).
Trocados pelo .mdt-page__count (mesmo estilo do badge no header
da pagina) — usa var(--m-accent-soft) que adapta ao tema.
Tambem removido o CSS .mdt-section__count (e .is-info / .is-accent)
que ficou orfao.
Visual: numero do contador (17 globais, N tenant) com o mesmo
estilo do "17" no header — consistencia visual + dark mode safe.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bug: no modo escuro o bg primary do botao --primary tornava o
texto branco ilegivel — cor primary clara contra fundo claro.
Fix: estilo outlined em vez de filled:
- background transparente
- border-color: var(--p-primary-color)
- color: var(--p-primary-color)
- hover: bg sutil 10% mix com primary
Mantem hierarquia visual (a borda destacada sinaliza acao primaria)
mas sem o conflito de contraste. Funciona em ambos os temas.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
.mdt-card__name: -webkit-line-clamp 1 -> 3 + word-break:break-word
+ line-height 1.3. Nomes longos (ex: "Termo de Consentimento Livre
e Esclarecido para Atendimento Online") cabem inteiros em ate 3
linhas, com elipses no final se passar.
.mdt-card max-height: 200px -> 240px pra acomodar o titulo mais
alto + tipo + descricao (2 linhas) + footer com variaveis.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bug: duplicar template disparava toast 2x e criava 2 copias.
Causa: MelissaLayout ja monta <ConfirmDialog /> global. Quando
MelissaDocumentosTemplates tambem montava, o confirm.require()
do PrimeVue dispara em TODOS os ConfirmDialog ativos -> callback
do accept executa 2x.
Fix: remove o <ConfirmDialog /> local de MelissaDocumentosTemplates.
O global do MelissaLayout cobre tudo.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
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>
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>
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>
Mobile (<1024px) agora segue padrao MelissaBloqueios:
- Coluna esquerda (Templates do sistema) eh teleportada pra um
drawer fixed que abre via botao "Templates do sistema" no header.
- Botao .mdt-menu-btn--mobile-only substitui o titulo no mobile
(mais legivel + acao clara).
- Backdrop escuro com blur fecha o drawer ao clicar fora.
- Auto-fecha quando o user seleciona um template (libera viewport
pra ver o preview no main).
Script:
- drawerOpen + isMobile refs + matchMedia listener
- toggleDrawer/fecharDrawer helpers
- onMounted setup + onBeforeUnmount cleanup
Template:
- <Transition name="mdt-drawer-fade"> wrap (slide horizontal +
fade do backdrop)
- <Teleport to="#mdt-mobile-drawer-target" :disabled="!isMobile">
envolvendo a <aside class="mdt-side">
- Botao "Menu" no header com class mdt-menu-btn--mobile-only
CSS:
- .mdt-mobile-drawer fixed left, transform translateX, 250ms cubic
- .mdt-mobile-drawer__backdrop overlay com blur
- @media (max-width: 1023px): cols vira 1-col, sidebar inline some,
botao menu aparece, titulo canonico some, acções viram icone-only
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Substitui o <div class="mdt-grid"> v-for simples por <DataView>
do PrimeVue na coluna "Seus documentos".
Beneficios:
- Paginacao automatica quando passa de 12 templates (era scroll
infinito virando lento)
- Slot #grid permite manter o layout de cards atual
- Footer com paginator integrado ao design (border-top + bg
transparente)
CSS:
- .mdt-dataview flex column ocupando o main
- :deep(.p-dataview-content) flex 1 + overflow auto = scroll
interno dos cards
- :deep(.p-dataview-paginator-bottom) flex-shrink 0 = paginator
sempre visivel no fundo
- .mdt-main .mdt-grid passa a ter padding 12 e gap 10 (era
herdado do .mdt-grid global)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
Botoes da toolbar do corpo que nao tinham comportamento esperado:
- hr (linha horizontal)
- eraser (apagar formatacao)
- source (alternar HTML)
Removidos do array bodyButtons. layoutButtons (header/footer) ja
nao tinha esses 3 (era enxuta).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>