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>
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>
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>
1. Icone "eye" do header de Preview -> cor primary (classe
mdt-main__title-icon-eye).
2. Icone "ellipsis-v" (3 pontos) dos cards do tenant -> cor
primary via :deep(.p-button-icon) selector.
3. Variaveis do card: formato "< 12 variaveis >" (entities
HTML </>) em font monospace + cor primary + bold.
Removido o icone pi-code (a propria notacao < > sinaliza).
4. .mdt-card max-height: 200px + overflow hidden. Foot agora
tem justify-content: center + margin-top: auto pra grudar
no fundo do card.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Erro: \"Unexpected token, expected }\" em \`{{ array.map(v => \`{{...${v}}}...\`) }}\`.
Vue parser confunde os \`{{\` da template string aninhada com os
delimitadores de interpolacao Vue, abortando parse.
Fix: extrai pra helper externo formatVarsPreview(vars, max) que
monta as chaves via concatenacao de strings (open + open + v +
close + close) — sem template literal com \`{{\` literal.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Refatora MelissaDocumentosTemplates seguindo padrao do
MelissaAgendaConfig (2-col com sidebar). Dois ajustes pedidos:
1. Layout 2-col (mdt-cols grid 360px + 1fr):
- COL 1 (sidebar): "Templates do sistema" — lista vertical
compacta com nome/tipo/descricao. Click abre preview.
- COL 2 (main): "Seus documentos" + subtitulo + grid de cards
dos templates do tenant.
- Empty states distintos por coluna.
- Mobile (<900px): empilha 1-col.
2. Preview antes de duplicar:
- View 'preview' nova (alem de list/create/edit).
- Click num template do sistema -> view='preview' (substitui
"Seus documentos" no main, sidebar permanece pra navegar).
- Header da main muda: nome do template + tipo/desc + 2 botoes
(Voltar / Duplicar).
- Iframe sandbox=allow-same-origin renderiza HTML completo
(cabecalho+corpo+rodape com CSS basico A4-like).
- Footer com lista de variaveis {{...}} do template (5 +N).
- Item ativo na sidebar destaca borda primary + opacity 1 no
icone de visualizar.
- Pos-duplicar: volta pra view='list' pra mostrar o novo
template no main.
UX result: user le antes de copiar (evita lixo em "Seus documentos"
de copias que nao queria).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Templates de documentos sao "setup", nao operacao diaria — deveriam
viver em Configuracoes, nao no menu de Documentos do paciente.
Mudancas:
1. Melissa — melissaConfigGrupos.js ganha grupo "Documentos" com
1 item "Modelos de documentos" -> slug `documentos-templates`
(pagina nativa MelissaDocumentosTemplates ja existe + ja esta
wired no MelissaLayout linha 2896).
2. Rail/Classic — routes.configs.js ganha rota
/configuracoes/documentos/templates (name=ConfiguracoesDocumentos
Templates) apontando pro mesmo DocumentTemplatesPage.vue.
3. Rotas antigas removidas — routes.therapist.js e routes.clinic.js
nao tem mais /documents/templates nem nomes de rota
therapist-documents-templates / admin-documents-templates.
URLs antigas dao 404 (decisao do user — limpa).
4. ConfiguracoesPage (sidebar Rail/Classic) ganha grupo
"Documentos" antes do "Empresa & Plataforma" com item "Modelos
de documentos".
5. Menus de pacientes (therapist.menu + clinic.menu) NAO tem mais
"Templates" — caminho de acesso e Configuracoes.
6. pagesIndex.js (busca global) atualizado: novo path, novos
keywords (recibo, atestado, laudo, tcle, lgpd, consent), roles
['therapist','admin'].
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Script usado pra importar a doc 01-busca-global-melissa.json
diretamente no banco via psql (sem passar pelo botao "Importar
JSON" da UI). DO block com dollar quoting pra evitar escape hell
no HTML conteudo + nos FAQs (que contem aspas, kbd, etc).
Importacao executada. Doc id=d9d2e431-0bd7-4883-9cfa-3a1a3228c295
+ 12 FAQ itens vinculados.
Path: database-novo/tmp/import-doc-busca.sql — pasta tmp pra
artefatos de operacao (nao parte do schema canonico).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Primeira doc gerada do plano de testes layout Melissa. Segue o
template do prompt em SaasDocsPage.vue:
- titulo, conteudo HTML rico (cards reproduzidos visualmente),
categoria=Navegacao, pagina_path=/melissa
- 12 FAQs cobrindo: atalho Ctrl+K, busca por telefone/CPF, lista
acessados recentemente, privacidade local-only, threshold 2
chars, cores semanticas por categoria, navegacao por teclado,
documentos por nome paciente, limpar localStorage, sessoes
passadas/futuras
- Nota pro dev: componente MelissaBusca.vue nao tem id= em nenhum
elemento — sugestoes de IDs pra adicionar quando ativar
data-highlight links.
Path: development/saas-docs/01-busca-global-melissa.json
Pronto pra importar via /saas/docs "Importar JSON".
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Refatora MelissaBusca pra usar PrimeVue Dialog em vez de popover
absolute. Resolve definitivamente o bug do panel estourar viewport
quando ha muitos resultados.
Mudancas:
1. Trigger no dock: input -> <button> com aparencia de input. Clica
ou Ctrl+K abre Dialog. Mantem placeholder + Ctrl+K kbd hint.
2. Dialog Spotlight: 640px max-width, posicionado 10vh do topo
(estilo Spotlight macOS / Linear / GitHub). Backdrop blur escuro,
dismissable mask, sem header, sem closable button (Esc cobre).
3. Input REAL dentro do Dialog: autofocus on open via nextTick.
Mantem v-model="query" + @keydown="onKeydown" (Arrow/Enter).
4. Panel de resultados: era position:absolute com max-height:60vh
(estourava em layouts com input perto do bottom). Agora vive
DENTRO do Dialog (flex:1, max-height:70vh no content), scroll
interno garantido por design — conteudo NUNCA passa do bottom
da pagina.
5. Remove: onClickOutside (dismissableMask cobre), Transition
mb-fade (Dialog tem sua animacao).
Comportamento end-user identico (Ctrl+K, navegacao com setas, Enter
seleciona, Esc fecha) mas visual + manutencao muito melhor.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Texto branco hardcoded ficava ilegivel no tema claro do PrimeVue
(branco em branco). Tema escuro funcionava ok pq fundo era escuro.
Fix: troca cores hardcoded por CSS tokens do tema:
- mb-panel background: var(--surface-card)
- mb-panel border: var(--surface-border)
- mb-item color: var(--text-color)
- mb-item__sub color: var(--text-color-secondary)
- mb-group__title color: var(--text-color-secondary) com opacity
- mb-item hover: color-mix com p-primary-color 8%
Icones semanticos (patient pink, sessao indigo, doc sky, intake
orange) ficam mais saturados no tema claro e suavizados no escuro
via :root.app-dark selectors.
Input field do search bar mantem fallback `white` — ele fica no
shell escuro do Melissa (lockscreen-style), nao depende do tema
PrimeVue.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
3 fixes pedidos no teste manual:
1. Shape errado da RPC: search_global retorna { id, label, sublabel,
deeplink } pra TODOS os tipos, mas o codigo lia campos diretos
(nome_completo, paciente_nome, inicio_em, nome_original etc) que
nao existem -> resultados saiam "(sem nome)", sem datas.
Fix: filteredPacientes + rpcAppointments + rpcDocuments + rpcIntakes
agora usam label/sublabel direto. selectEntry extrai patient_id da
deeplink quando precisa.
2. Cores ilegiveis: fundo do panel transparente demais (var(--m-bg-medium)
nao tinha contraste em alguns temas). Fix: fundo solido rgba(20,22,32,
0.92), border 14% white, text 96% white pra label, 65% pra sub
(sobe pra 78% no hover/active). Group title 50% + bold pra hierarquia
clara.
3. Cor das sessoes: grupo "Sessoes" tinha icone cinza generico. Fix:
classes .mb-item__icon--{patient,sessao,doc,intake} com paleta
espelhando a agenda — sessao = indigo-500 (#a5b4fc texto +
rgba(99,102,241,0.20) bg, mesma cor do pickColor() padrao);
patient = pink-400; doc = sky-500; intake = orange-400.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Quando o profissional seleciona "Outro" no Tipo de registro, agora
aparece um campo adicional pra informar o nome do conselho/instituicao
livre (ex: APM, ABRAP, conselhos nao-listados).
Migration 20260521000009 adiciona profiles.professional_registration_
type_other (text livre). Aplicada e marcada no _db_migrations.
ProfilePage e MelissaPerfil:
- form.professional_registration_type_other no reactive
- SELECT/UPDATE inclui a nova coluna
- UI condicional: campo aparece SOMENTE quando type === 'outro'
- Preview ao vivo usa type_other no lugar de 'outro' quando aplicavel
- Save limpa type_other automaticamente quando troca pra outro tipo
DocumentGenerate.service.loadTherapistData puxa type_other da query.
Quando profile.type='outro', terapeuta_registro_tipo recebe o valor
livre (ex: 'APM 12345/SP' em vez de 'outro 12345/SP'). terapeuta_crp
(legacy compat) continua so preenchido quando type RAW = 'CRP'.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Problema: overlay "Sem conexao" aparecia toda hora em dev. Causa:
fetch('/favicon.ico') com timeout 4s + poll a cada 10s + sem retry.
Qualquer slow request (vite HMR rebuild, DNS, network blip) marcava
offline imediato.
Fixes:
1. Confia em navigator.onLine PRIMEIRO. Se browser ja sinaliza
offline (wifi caiu, modo aviao), pula o fetch — fonte 100%
autoritativa.
2. Threshold de 2 falhas consecutivas. Antes 1 falha = offline.
Agora precisa 2 consecutivas, descarta blips esporadicos.
Reset pra 0 a cada success.
3. Timeout fetch 4s -> 8s. Mais tolerante a slow requests.
4. Poll 10s -> 30s (prod) ou 60s (dev). Reduz pressao no Vite HMR
sem perder detectividade. Eventos offline/online do browser
continuam capturando mudancas reais instantaneamente.
5. Em DEV, polling 60s (vs 30s prod). HMR rebuilds podem demorar;
queremos minimizar fetch concorrente.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 fixes pedidos no teste manual:
1. Card "Registro profissional" movido pra apos Identidade (em vez
de antes do Layout). Faz sentido contextual — dados pessoais
profissionais ficam juntos.
2. Inputs do Registro convertidos pra FloatLabel variant="on"
(padrao Melissa do resto da tela). Tres campos: tipo, numero, uf
+ preview box.
3. Card "Preferencias" tema agora em 1 linha (grid 2-col fixo,
classe .mpr-theme-row). Antes podia quebrar em 2 linhas via
flex-wrap.
4. "Trocar senha" navega pra /melissa/seguranca (rota nativa
Melissa, MelissaSeguranca.vue ja existente) em vez de
/account/security (que sairia do shell Melissa). Nao vaza mais
pro layout classico.
Styles novos extraidos do inline pro <style scoped>: mpr-preview-box,
mpr-theme-row, mpr-theme-card, mpr-info-row, mpr-action-card.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Espelha as melhorias do ProfilePage no perfil nativo Melissa
(/melissa/perfil), com 4 changes:
1. Card "Registro Profissional" (id=mpr-sec-registro, antes do
card Layout): Select tipo + Number + Select UF + preview ao vivo
"Aparecera nos documentos como: CRP 06/12345/SP". 3 colunas de
migration 20260521000003 wire-up no load + save.
2. Card "Layout" — sub do Rail atualizado pra mensagem solicitada:
"Icones no canto esquerdo + painel expansivel. Disponivel apenas
no desktop."
3. Card "Preferencias" (id=mpr-sec-preferencias, depois do Layout):
toggle Tema Claro vs Escuro com cards visuais + sun/moon icons.
Usa isDarkTheme + toggleDarkMode do useLayout.
4. Card "Seguranca" (id=mpr-sec-seguranca, ultimo): mostra e-mail
atual readonly + botao "Trocar senha" que navega pra
/account/security.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Gap detectado em teste manual: migration 20260521000003 adicionou
as 3 colunas (professional_registration_type/_number/_uf) e o
DocumentGenerate.service.loadTherapistData ja le delas, mas a UI
de edicao nao foi criada.
ProfilePage.vue ganha novo card "Registro Profissional" (id=
registro-profissional, cor #0ea5e9 ciano, antes do card de Redes
Sociais):
- Select tipo (CRP/CRM/CRFa/CREFITO/CRESS/CRN/RMS/outro — mesmas
opcoes do CHECK constraint)
- InputText numero
- Select UF (27 estados, filterable)
- Preview: "Aparecera nos documentos como: CRP 06/12345/SP"
- Numero e UF disabled enquanto tipo nao escolhido
Wire-up: SELECT/UPDATE do profile agora incluem as 3 colunas.
form.* tem defaults vazios.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Aplica no banco local todas as migrations pendentes do dia (clinical
notes, accept_invite RPC, asaas tables/rls, profiles registration,
specialties, document_templates consent types, sign_document RPCs,
list_my_signatures, recibo amend) e os 3 seeds novos (clinical note
templates, specialties, consent forms LGPD/Gravacao).
db.config.json estendido com os 3 seeds novos (system group) pra
setup do zero rodar tudo.
Gotcha re-validado: migration 20260521000005 (CHECK constraint
em document_templates) silenciosamente falhou via db.cjs porque
postgres nao e owner da tabela (owned por supabase_admin). Detectado
quando seed_060 falhou com violates check constraint. Re-rodada
via TCP 127.0.0.1 trust com `psql -U supabase_admin`. Memoria
project_supabase_admin_gotcha atualizada com o metodo correto.
Sanity check pos-aplicacao:
- 5 RPCs novas + 8 tabelas novas
- 17 document_templates global (15 + 2 LGPD/Gravacao)
- 34 specialties + 6 clinical_note_templates
- Backup automatico em backups/2026-05-21/
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Atualiza PADRONIZACAO.md marcando Fase 4 da agenda em sua maior
parte fechada: popover snapshot + reverse transition (ja feitos
em C11) + decomposicao A+B1+B2 (-991L useMelissaAgenda) + Fases
C+D (Rail/Clinica adotam billing core via useAgendaStatusChange) +
C12 UX iter.
Pendente: indicadores visuais 3 canais em Rail/Clinica + popover
Rail antecipar/revogar/trocar metodo + doc de ajuda.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
AgendaClinicaPage espelha Fase C: useAgendaStatusChange composable
+ AgendaStatusChangeConfirmDialog plugado.
onUpdateSeriesEvent reescrito:
- Materializa virtual se preciso (via createClinic com status='agendado'
+ tenantId)
- updateClinic({ status }) no DB
- applyStatusChange(eventoId, row, novoStatus) ramifica via dialog
quando preciso
- loadClinicRange() refetch apos applied
Mesma feature parity de Melissa pra status change na Clinica:
multa, taxa cancelamento tardio, consumir saldo, gerar cobranca
pacote saldo, reverse transition trava — tudo via agendaBilling.service.
Fase C (Rail) + Fase D (Clinica) fechadas. Os 3 layouts (Melissa/
Rail/Clinica) agora compartilham o billing core do agendaBilling.
service via composable useAgendaStatusChange.
Pendente (residual incremental):
- Indicadores visuais (3 canais) nos 3 layouts
- Antecipar/Revogar pagamento no popover de Rail (Rail nao tem
popover separado — usa AgendaEventDialog direto; precisa
refactor maior)
- Doc de ajuda
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>