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>
AgendaTerapeutaPage (Rail) ganha o fluxo de status change do
Melissa via novo composable useAgendaStatusChange (reusable
wrapper sobre agendaBilling.service).
src/features/agenda/composables/useAgendaStatusChange.js (novo):
- Composable Tipo A pra qualquer page que precise do flow
load context -> dialog se necessario -> apply decisoes
- Mantem state do dialog + resolver promise
- Expoe applyStatusChange(eventoId, row, novoStatus)
- Resolve ownerId via supabase.auth + tenantId via tenantStore
AgendaTerapeutaPage:
- onUpdateSeriesEvent refatorado: materializa virtual se preciso ->
update status -> applyStatusChange (load ctx + dialog + apply)
- AgendaStatusChangeConfirmDialog plugado no template
Antes: Rail fazia so update(id, { status }) cru — sem multa,
sem pacote, sem reverse, sem nada de C7-C13. Era a versao
primitiva do status change.
Depois: Rail tem feature parity com Melissa pra status change.
Multa por falta, taxa de cancelamento tardio, consumir saldo do
pacote, gerar cobranca de pacote saldo, reverse transition trava
— tudo via mesmo agendaBilling.service.
Pendente Fase C: indicadores visuais (3 canais) + antecipar
pagamento (popover-specific, depende refactor maior do
AgendaEventDialog ou criar Rail popover). Fica pra iter
incremental.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Registra a decomposicao end-to-end (A+B1+B2) totalizando -991L
no useMelissaAgenda. 3 layouts podem agora compartilhar o billing
core.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Continua decomposicao da agenda. Extrai 3 mutations:
- applyStatusDecisions (~330L — reverse, consume saldo,
multa, mark paid, generate package
charge, antecipated payment)
- createPackageContract (~140L — upfront ou saldo)
- materializeAndChargePerSession (~90L — N events + N records)
Padrao das assinaturas:
- supabase como dep explicita (em vez de closure)
- toast OPCIONAL (callsite fora de UI pode passar null;
applyStatusDecisions ramifica via `if (toast?.add)`)
- ownerId/tenantId como args (em vez de capturar refs)
createPackageContract + materializeAndChargePerSession ja retornavam
{ toast: {...} } pra caller mostrar — pattern preservado.
useMelissaAgenda.js: 2593L -> 2042L (-551L). 3 wrappers finos
injetam supabase/toast/refs do escopo do composable. Comportamento
identico — codigo movido linha-a-linha, so refactor de signature.
TOTAL nas fases A+B1+B2: -1525L extraidas do useMelissaAgenda
(de 3033L original pra 2042L atual). Tres pages (Melissa/Rail/
Clinica) agora podem reusar mesmo billing core.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Continua decomposicao da agenda (apos Fase A utils). Extrai pro
service os componentes read-only / pure:
- computeSeriePrice (puro)
- generateOccurrenceDates (puro)
- loadStatusChangeContext (read-only DB — assina supabase,
ownerId, tenantId, row, eventoId,
status)
- needsStatusConfirmDialog (puro — depende so do ctx)
useMelissaAgenda.js: 2792L -> 2593L (-199L). _loadStatusChangeContext
agora e wrapper fino que injeta supabase/ownerId/tenantId do
composable scope. _needsConfirmDialog vira alias direto.
_computeSeriePrice/_generateOccurrenceDates importados direto.
Fase B1 deixa Rail/Clínica capazes de reusar TODA a logica
read-only de status change. Mutations (applyStatusDecisions,
createPackageContract, materializeAndChargePerSession) ficam pra
Fase B2.
Risco: zero comportamental — toda chamada produz o mesmo ctx
de antes. Codigo movido sem mudancas de logica.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Registra os 3 commits da sessao (C12 trocar metodo, C12 filtro
cancelled, Fase A utils extract). Memoria
project_c12_antecipar_iterar atualizada pra refletir patterns
prontos pra Rail/Clinica.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Decomposicao da agenda em prep pra replicar Rail/Clinica.
4 arquivos novos em src/features/agenda/utils/:
- eventoTipo.js -> EVENTO_TIPO + normalize/derive + MAX_SESSION_MINUTES
- dbFields.js -> pickDbFields whitelist (memoria pickdbfields_whitelist)
- timeHelpers.js -> isUuid + addMinutesToTime + isoToDecimalHour + dateToISO
- colors.js -> pickColor (status+tipo+isOccurrence)
useMelissaAgenda.js (2863L -> 2792L): removeu definicoes locais
(83 linhas), passou a importar dos utils. Aliases _addMinutesToTime
e _dateToISO mantidos no escopo via import "as" pra nao mexer
em 70+ callsites internos.
Fase A = baseline zero-comportamental pra Rail/Clinica adotarem
os mesmos helpers. Fase B (service de billing — applyStatusDecisions,
createPackageContract, materializeAndCharge) vem em seguida.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Iteracao UX #2 do C12: records cancelled (do ciclo Revogar+Antecipar
e tambem das multas) poluiam o dialog "Lancamentos da sessao",
escondendo o que importa (ativos).
lancamentosShowHistory ref (default false) + lancamentosFiltered
computed filtra status !== 'cancelled'. lancamentosCancelledCount
computa contagem pra feedback.
UI:
- Dialog abre limpo (sempre lancamentosShowHistory=false em
onVerLancamentos)
- Quando ha cancelled e existe ativo: linha acima da lista com
"{N} cancelado(s) ocultos" + botao toggle "Mostrar/Ocultar
historico"
- Quando todos sao cancelled: empty state especial "Sem
lancamentos ativos. {N} cancelado(s) no historico" + botao
pra expandir
- Cards cancelled atenuados (opacity 0.55, border-dashed,
background sutil, description com line-through) — claramente
audit trail, nao-ativo
Combina com "Trocar metodo" (commit anterior) — agora o caso 99%
do tempo ele ve so o record ativo, nao precisa nem expandir
historico.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Iteracao UX do C12 (antecipar pagamento) — antes user que queria
trocar PIX por dinheiro precisava Revogar (cancela record) +
Antecipar de novo (cria record novo), acumulando lixo no audit
trail (memoria project_c12_antecipar_iterar: ciclos longos chegaram
a 5+ records cancelled num mesmo evento).
MelissaEventoPanel ganha 3 botoes quando isAntecipacaoAtiva:
- "Trocar metodo" (default, icone pi-sync)
- "Revogar pagamento" (danger, icone pi-times-circle)
Antes mostrava so "Revogar".
MelissaLayout:
- anteciparMode ref ('create' | 'update') + onTrocarMetodoAntecipacao
pre-seleciona o metodo atual lendo o paid record antes de abrir
o dialog
- confirmAnteciparPagamento ramifica: mode='update' faz UPDATE no
paid existente (payment_method + paid_at + notes audit "metodo
trocado: X -> Y"). Sem cancel cycle, sem record novo.
- Dialog header/labels/CTA dinamicos por mode
Result: ciclo trocar metodo agora gera 0 records cancelled (so
update + nota auditoria). Revogar continua disponivel pra quando
realmente precisar cancelar o pagamento.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Atualiza PADRONIZACAO.md marcando §1.3 UX como 3 de 4 fechados.
#12 papel timbrado documentado como bloqueado em codigo externo
do UniaoApp.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ROADMAP item #1.3 #13. exceljs e jspdf ja estavam no package.json
mas as paginas de relatorio so renderizavam UI — zero export.
src/services/reportExport.service.js (novo) com 3 funcoes:
- exportSessionsToPDF: layout HTML→PDF via pdf.service.js (header
com branding tenant, KPI grid, tabela A4 com striping)
- exportSessionsToXLSX: ExcelJS workbook formatado (titulo + subtitle
+ KPIs inline + tabela com header escuro + alternating row + frozen
header). Import dinamico — exceljs e pesado, so carrega no click.
- exportSessionsToCSV: vanilla (sem deps) com BOM UTF-8 + separador
';' (Excel-friendly em pt-BR)
3 botoes em ambas paginas:
- RelatoriosPage.vue (/therapist/relatorios): icones pi-file-pdf +
pi-file-excel + pi-table no header (rounded), tooltip, disabled
quando total=0 ou loading, toast de sucesso/erro
- MelissaRelatorios.vue (Melissa secao): mesma logica, botoes nativos
.mr-head-btn no padrao Melissa
Filtro de status da tabela e respeitado no export (exporta o que
o usuario esta vendo). KPIs incluidos no PDF e XLSX.
§1.3 UX = 3/4 fechado: #10 (busca global) + #11 (recently viewed) +
#13 (relatorios export). #12 (papel timbrado) bloqueado em codigo
externo do UniaoApp.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ROADMAP item #1.3 #11. localStorage por user_id pra isolar sessoes
diferentes no mesmo browser. ROADMAP sugeria localStorage OU tabela
user_recent_access — escolhi localStorage por simplicidade (sem
migration adicional + zero round-trip por visita).
composables/useRecentPatients.js:
- useRecentPatients() — composable reativo Tipo A: items + hasItems
+ addVisit + remove + clear + refresh
- registerPatientVisit(patient) — helper stateless pra usar fora
de setup (ex: navigation guards, action handlers)
- Sincroniza entre instancias na mesma aba via CustomEvent + 'storage'
- Max 5 items. Dedup por id, novo no topo.
Wire-up de visita (registra ao carregar prontuario):
- MelissaPaciente.vue: registerPatientVisit no loadAll apos detail.load
- PatientProntuario.vue: registerPatientVisit em loadDetail apos p resolved
Wire-up de visualizacao (mostra quando query vazia):
- GlobalSearch.vue: grupo "Acessados recentemente" antes dos Atalhos.
goTo("recent") navega pra /therapist/patients/:id.
- MelissaBusca.vue: grupo "Acessados recentemente". emit('paciente')
reusando a logica do MelissaLayout que ja navega pra
/melissa/paciente?id=X.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Fecha ROADMAP #1.3 #10 (busca global topbar). GlobalSearch.vue
classic+rail ja usava RPC. MelissaBusca era client-side preview com
fallback nas props (pacientes+eventos do dia) — agora consulta a
mesma RPC search_global com debounce 200ms + searchSeq pra descartar
respostas obsoletas.
3 grupos novos exibidos quando RPC retorna:
- rpc-appointments -> sessoes qualquer data (alem de "hoje")
- rpc-documents -> documentos por nome/tipo
- rpc-intakes -> cadastros recebidos
Pacientes mescla: RPC tem prioridade (todos os pacientes); props
mantida como fallback rapido (digitacao curta antes do debounce).
Emits estendidos: novos 'documento' + 'intake' alem dos existentes
'acao' + 'paciente' + 'evento'.
MelissaLayout atualizado:
- @paciente agora navega pra /melissa/paciente?id=X (antes ignorava
payload e so abria secao generica — bug existente)
- @documento abre prontuario do paciente com tab=documentos
- @intake abre /melissa cadastros-recebidos
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Marca ROADMAP #1.4 #14 done em PADRONIZACAO.md (Fase 3 Gaps de MVP)
e adiciona entrada no log.md.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
Atualiza PADRONIZACAO.md (Fase 3 marca CFP completo, todos os 5
itens #5/#6/#7/#8/#9 done) e adiciona entrada no log.md detalhando
os 5 commits do dia + arquitetura end-to-end + proximos passos.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
Registra cronologia da leva noturna 20/05 evening -> 21/05 01:06:
Fase 0+0.5 sweep foundation, M1 Home/Components, M2 Pacientes batch,
M3+M4+M5+M6 foundation em batch, M5 quick wins, Fase 2 Graphify
hotspots, Asaas Gateway Tier 1 Fase A, Compliance CFP #5/#8/#9.
8 entradas no log.md.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
DESIGN_ASAAS_GATEWAY.md documenta arquitetura. Schema novo: 2
migrations (tables + RLS) cobrindo asaas_customers + asaas_payments
+ asaas_webhook_events. Client service asaasGatewayService.js no
features/financeiro. 3 Edge Function stubs (create-payment-record,
cancel-payment, sync-payment) — webhook financial_records eh Fase B.
Bloqueadores Fase B (implementacao real): user precisa criar conta
Asaas, gerar API keys, configurar webhook, setar ENV vars no
Supabase. Decisao modelo de negocio (A/B/C) tambem pendente.
Stops marcados claramente no DESIGN.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Modulo 6 da Fase 1. noticesSelects.js extrai os 2 selects do
noticeService (GLOBAL_NOTICE_SELECT, NOTICE_DISMISSAL_SELECT) +
noticeService passa a usa-los (zero select inline). Conversations
ganha foundation: 3 services (_tenantGuards, conversationsSelects,
conversationsRepository). Channel factory (WhatsApp/SMS/Email) e
composables ficam pra sessao dedicada — escopo M6 era so destravar
o supabase.from() inline.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Modulo 5 da Fase 1 + quick wins fechados. features/tenantship/ com
2 services + 2 composables (members + invites). MembersPage.vue
nova em views/pages/admin/ + rota /admin/members em routes.clinic.
Migration 20260520000005 cria RPC accept_tenant_invite (SECURITY
DEFINER + lock FOR UPDATE) — tenantInvitesRepository.acceptInvite
agora chama RPC real (nao mais stub). SaasTenantFeaturesPage
refatorada pra usar novo tenantFeatureAdminService. SetupWizardPage
2648 linhas deferido pra sessao dedicada.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Modulo 4 da Fase 1. 9 arquivos novos em features/financeiro/:
4 services (_tenantGuards, financialSelects, financialRecords
Repository, financialExceptionsRepository, billingContractsRepository)
+ 4 composables (useFinancialRecords, useFinancialExceptions,
useBillingContracts, useBillingOrchestrator). Old composables ainda
em paralelo — Fase C (cutover) bloqueada pelas decisoes #2/#3/#6
de billing (memoria agenda_billing_decisoes).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Modulo 2 da Fase 1 de padronizacao em batch unico. patientsSelects.js
nova com 11 constantes de select. patientsRepository.js estendido com
~15 funcoes novas (markIntakeConverted, list/get/update por
contexto, etc). 8 composables refatorados em paralelo (usePatients,
useDetail, Financial, Sessions, Messages, Documents, Recurrences,
SupportContacts) — zero supabase.from() em qualquer composable de
patients. _lastPatientId movido pra DENTRO das functions nos 3
composables que tinham. CadastrosRecebidosPage + MelissaCadastros
Recebidos pegam carona dos selects. Aguarda teste batch consolidado.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Modulo 1 da Fase 1 de padronizacao. Novos features/medicos (services
+ composable useMedicos) e features/insurance (idem). 3 cadastros
rapidos (medicos, convenios, ComponentCadastroRapido + Insurance
PlanQuickCreateDialog) migrados pra usar os composables novos —
zero supabase.from() em UI components. TEST_ACCOUNTS extraido pra
src/config/devTestAccounts.js. Topbar ganhou switcher de layout
+ atalhos M1 via novo useTopbarDevMenuExtras. M1.6 MelissaLayout
90 imports deferida pra sessao dedicada (memoria padronizacao_sweep).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Relatorio standalone HTML (com print CSS otimizado pra PDF export):
- 10 paginas estruturadas
- Sumario executivo + metricas + pontos fortes
- 10 codes smells / dividas tecnicas detalhadas
- 8 issues de UX
- 7 riscos arquiteturais
- 15 recomendacoes priorizadas (P0-P3) com esforco e impacto
- Roadmap proposto em 3 horizontes
- Apendices: 14 bugs do dia, pendencias, commits, status dos cenarios
Visao senior eng: arquitetura solida em conceito, divida tecnica
em execucao. Top 5 achados:
1. 3 hotspots >2.8k LOC cada (AgendaEventDialog 6k, MelissaLayout 4.3k)
2. Logica de status change triplicada (Melissa/Rail/Clinica)
3. billing_contracts.updated_at gotcha
4. Snapshot stale popover (mitigado mas estrutural)
5. Audit trail acumulando ruido
Recomendacao chave: extrair status change orchestrator pra composable
shared ANTES da replicacao Rail/Clinica. Senao replica os mesmos
14 bugs vezes 2.
Para PDF: abrir relatorios/RELATORIO-AGENDA-2026-05-20.html no
browser e Ctrl+P -> Salvar como PDF.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>