dee89ccd844dd2df700739fd1dcf8aa1e4b97ae3
216 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
dee89ccd84 |
registro profissional: campo livre quando tipo='outro'
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> |
||
|
|
7516468f78 |
melissa/perfil: ajustes UX nos cards novos
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> |
||
|
|
20d2b3aee4 |
melissa/perfil: 3 cards novos — registro CFP + preferencias + seguranca
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> |
||
|
|
049dd91b9b |
agenda Fase B2: extrai mutations pro agendaBilling.service
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>
|
||
|
|
e7e3d1beb1 |
agenda Fase B1: agendaBilling.service (read-only + helpers puros)
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>
|
||
|
|
ee117eafe6 |
agenda Fase A: extrai utils puros pra features/agenda/utils
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> |
||
|
|
b7f3c23ad6 |
agenda C12 UX: filtrar cancelled do dialog Lancamentos da sessao
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> |
||
|
|
9c518a2b44 |
agenda C12 UX: "Trocar metodo" em vez de Revogar+Antecipar
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>
|
||
|
|
b1e8e010c0 |
roadmap #13: relatorios export PDF + Excel + CSV
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> |
||
|
|
2dae4a11ae |
roadmap #11: recently viewed (ultimos 5 pacientes acessados)
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> |
||
|
|
e7a9bdab5f |
roadmap #10: MelissaBusca usa RPC search_global (promovida de preview)
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> |
||
|
|
1c2a2b6e19 |
M2: patients — selects + repository + 8 composables refatorados
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> |
||
|
|
27467bbb68 |
M1: features/medicos + features/insurance + ComponentCadastroRapido refactor
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> |
||
|
|
f83315baba |
agenda: popover watch acompanha transicao virtual->materializada
Bug do "Usar sumiu apos revogar antecipacao": o watch sincronizava eventoSelecionado por id, mas quando virtual era materializada (antecipar/Usar/Realizada flow) o id mudava de rec::rule::date pra uuid real. Watch nao achava match -> popover ficava preso na versao virtual stale -> botoes refletiam estado antigo. Fix: lookup em 2 etapas: 1) match por id (caso comum) 2) match por recurrence_id+recurrence_date quando nao acha (caso virtual->materializada). Pega a versao real correspondente aquela data. Estado final do teste C12 do user: status=realizado, saldo 3/4, 1 pending + 5 cancelled (audit trail de varios ciclos antecipar/ revogar). Funcionalmente OK; com o fix, retestes ficam mais limpos. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
7d2a405d05 |
agenda: normalizeForMelissa expoe owner_id/tenant_id/contract_id
Bug introduzido pelo watch sync do popover (commit
|
||
|
|
b5e00a7022 |
agenda: popover sincroniza com eventos + antecipar nao reusa cancelled
Dois bugs descobertos no ciclo C12 antecipar -> revogar -> reantecipar:
1) confirmAnteciparPagamento reusava record cancelled:
Quando user revogava (vira cancelled) e antecipava de novo,
o existRec query pegava o cancelled e UPDATE-ava pra paid no
MESMO record id. Resultado: notes mantinham historico
"Cancelada via reversao" + "Antecipacao revogada" + record
reativo como paid, confuso pra audit trail. Fix: filtrar
.neq('status', 'cancelled') na busca de existRec — agora a
re-antecipacao via RPC cria record fresh.
2) Popover snapshot stale (pendencia documentada em
project_melissa_popover_snapshot, antecipada pra agora):
eventoSelecionado.value era snapshot do clique e nao acompanhava
updates do _paymentStateMap pos M.refetch. User antecipava, o
record paid era criado, mas o popover continuava com paymentState
antigo -> botao continuava "Antecipar pagamento" em vez de
alternar pra "Revogar pagamento". Fix: watch em M.eventos sincroniza
eventoSelecionado com a versao fresh quando id bate. flush:'post'
pra rodar apos o computed reagir.
Como o popover agora atualiza in-place, removido fecharEvento() de
confirmAnteciparPagamento e onRevogarAntecipacao — o user pode ver
o botao alternar live em vez de precisar reabrir o popover.
Cleanup do estado do Andre: deletado record orfa 3a4c79e0 pra reset
do teste C12.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
272c804335 |
agenda: revogar antecipacao de pagamento
UX gap descoberto durante teste C12: apos antecipar pagamento,
nao havia caminho via popover pra desfazer caso user tenha
errado (paciente nao pagou, errou o valor, etc).
Implementacao:
- Botao "Antecipar pagamento" agora alterna pra "Revogar
pagamento" (vermelho, --danger) quando isAntecipacaoAtiva
(status=agendado + paymentState=paid)
- Handler onRevogarAntecipacao em MelissaLayout: ConfirmDialog
vermelho + cancela record paid + nota de auditoria em notes
("[YYYY-MM-DD] Antecipacao revogada em ...") + refetch
- Apos revogar, botao volta pra "Antecipar pagamento" — user
pode antecipar de novo com valor/metodo corretos
Limites: so disponivel em status='agendado'. Apos Realizada o
paid representa pagamento real da sessao realizada, nao
antecipacao — estorno deve ir pelo /financeiro.
Sobre "Usar" desaparecer apos antecipar (questao do user): comportamento
correto. "Usar" cria record+consome saldo — duplicaria com paid
existente. Apos antecipar, fluxo correto e clicar Realizada (que
detecta paid pre-existente via fix anterior
|
||
|
|
00c4168393 |
agenda: C12 prep — detectar paid pre-existente em pacote saldo realizada
Preparacao pra teste C12 (antecipar pagamento). Fluxo: 1. User clica "Antecipar pagamento" em virtual futura -> cria record paid R$ X sem consumir saldo 2. Depois marca a sessao como Realizada -> dialog deve detectar o paid + so consumir saldo (NAO criar record novo, evitar duplicidade) Sem esse fix, marcar Realizada apos antecipar abriria o dialog "Gerar cobranca?" com default true, gerando record novo duplicado. Implementacao: - _loadStatusChangeContext: carrega ctx.existingPaidRecord (qualquer paid linkado ao evento, n=1) - Dialog: nova prop existingPaidRecord + computed showAlreadyPaid (substitui showCobrancaPacote quando paid existe) - Template: bloco "Sessao ja paga via antecipacao" com info do pagamento + preview do consumo de saldo - _applyStatusDecisions: novo branch 4-pre roda ANTES do generatePackageCharge: se realizado+pacote saldo+paid existe, roda tasks pendentes (1b amarra) + incrementa saldo sem criar record. Return cedo. Backfill: Andre 10/06 voltou pra agendado + saldo 2/4 (estado limpo pra testar C12 com a sessao 10/06 antecipando). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
5965b05378 |
agenda: link universal pacote + refresh saldo no reverse
Dois bugs descobertos durante C11/C+D: 1) Faltou+multa SEM consumeSaldo nao amarrava billing_contract_id no agenda_evento (so amarrava se consumeSaldo=true). Resultado: sessao 27/05 do Andre faltou+multa-sem-consume ficou sem rastro do contrato no DB. Reverse posterior nao detectaria saldoConsumed. Fix: bloco 1b) universal — sempre amarra quando forward (realizado/ faltou/cancelado) + tem contract + eventoId. Cobre todos os combos (multa-sem-consume, multa-com-consume, generatePackageCharge, consumeSaldo solo). 2) Reverse decrementar saldo as vezes nao persistia. Suspeita: race com ctx.billingContract.sessions_used stale do _loadStatusChangeContext quando flows rapidos sequenciais (Realizada+gerar -> Agendada imediato). Fix: refetch FRESH do billing_contracts.sessions_used direto do DB ANTES de calcular newUsed. Mais robusto contra qualquer race condition. Adicionado console.log pra futura debug. Removida duplicidade do amarra-billing_contract_id no bloco consumeSaldo (universal cobre). Backfill Andre Green: 27/05 amarrado, saldo voltou pra 2/4. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
45984e885b |
agenda: label + badge cientes de pacote em sessoes state=none
Bug descoberto durante teste C11: sessao 27/05 do Andre Green (materializada via Falta+reverse, pertence ao pacote saldo) mostrava "A cobrar R$ 40,00" no popover mesmo sem fatura ativa. Implicava que dava pra gerar cobranca avulsa solta — conflito com o flow do pacote (Usar consume saldo). Fix em paymentLabel: quando state='none' e ev.contract existe, label muda conforme estilo: - saldo: "Aguardando uso do pacote" - upfront: "Coberta pelo pacote (upfront)" Avulsa sem pacote continua mostrando "A cobrar R$ X". Simetria em MelissaAgenda.vue badge gate: nao mostra badge $ amber em sessao state=none com pacote amarrado (hasPacoteTied). Sem isso, sessao agendada de pacote saldo no calendar ficava com badge "cobranca pendente" enganoso. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
3f3f2acc70 |
agenda: consumeSaldo agora amarra billing_contract_id no evento
Bug em cascata descoberto durante C11/B com Andre Green: - User clicou Falta + Descontar (consumeSaldo) -> sessions_used: 1->2 - billing_contract_id do agenda_evento ficou NULL (omissao no flow) - User clicou Agendada (reverse) -> detector saldoConsumed em _loadStatusChangeContext checa evRow.billing_contract_id, que esta NULL -> saldoConsumed=false -> bloco "Devolver saldo" NAO aparece no dialog -> saldo NAO devolvido - Next Falta mostra "Descontar 2 para 3" em vez de "1 para 2" Fix: bloco consumeSaldo agora tambem amarra billing_contract_id no agenda_eventos. Replica o padrao que ja existe no generatePackageCharge e no onUsarSessao. Sem isso, qualquer reverse pos-consumeSaldo nao detecta o saldo consumido. Backfill manual do Andre: sessions_used voltou pra 1. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
5684297243 |
agenda: reverse transition trava (Agendada apos artefatos)
User hit pra valer a pendencia documentada (reverter realizado/faltou/cancelado pra agendado deixa records/saldo orfaos). Decidido implementar trava AGORA em vez de pos-C13. Quando user clica "Agendada" no popover/dialog em sessao que tem artefatos pendentes (cobranca pending, multa, saldo consumido em pacote saldo), abre o AgendaStatusChangeConfirmDialog com nova variante "reverse": 1. Lista records pending vinculados (descricao + valor) com radio [Cancelar (recomendado) | Manter ativa] 2. Warning textual pra records PAID (estorno e manual pelo Financeiro — sem radio, so info) 3. Saldo consumido (pacote saldo): radio [Devolver 1 sessao | Manter] No confirm: - Cancela records pending escolhidos (status='cancelled' + notes de auditoria) - Decrementa sessions_used + reativa contract se estava completed - Desamarra billing_contract_id do evento se devolveu saldo - Status muda pra agendado (ja foi aplicado pelo _applyStatusUpdateOnly) Se nao tem artefato algum (sessao agendado -> agendado, ou realizado sem records): aplica direto sem dialog (existing behavior via _needsConfirmDialog). _loadStatusChangeContext agora carrega reverseArtifacts (status anterior, records ativos, saldoConsumed) quando novoStatus=agendado. Memoria project_agenda_reverse_transitions atualizada — pendencia fechada antes da hora. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
16dfa02bd1 |
agenda pacote saldo: fix root cause + sequential awaits
ROOT CAUSE DESCOBERTO durante C11/A com Andre Green:
billing_contracts NAO tem coluna updated_at. UPDATEs em
_applyStatusDecisions passavam updated_at -> Postgres retornava
"column updated_at does not exist" -> Promise.allSettled engolia
como {status: 'rejected'} silencioso -> toast warn generico que
user nao percebia. Resultado: sessions_used nunca incrementava.
Bug existia em DOIS lugares:
1. consumeSaldo block (faltou/cancelado pacote saldo) - afetaria
C11/B, C11/C, C11/D
2. generatePackageCharge block (realizado pacote saldo) - afetou
C11/A
Em ambos: removido updated_at do patch (.update({...})).
ADICIONAL: generatePackageCharge refatorado pra usar AWAITS
SEQUENCIAIS (igual onUsarSessao do MelissaLayout que sempre
funcionou):
- 4a) UPDATE agenda_eventos.billing_contract_id (faltava!)
- 4b) RPC create_financial_record_for_session
- 4c) UPDATE billing_contracts.sessions_used + status=completed
Cada step com try/catch + console.error + toast distinto. Sem mais
falhas escondidas em Promise.allSettled paralelo.
Backfill manual do estado do Andre Green: evento 6e70476f agora
amarrado ao contract 691118da com sessions_used=1.
Memoria nova: project_billing_contracts_no_updated_at.md pra evitar
o gotcha no futuro.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
079509e001 |
agenda: dialog pacote saldo realizada — 2 sub-questions claras
Antes (UX confusa): bloco "Gerar cobranca no pacote?" tinha so um Select "Como cobrar?" com options mixadas: - "Enviar link de pagamento (Asaas)" - "Ja recebi - PIX" - "Ja recebi - Dinheiro" - etc User selecionou "Ja recebi - PIX" pensando que era "cobrar via PIX" durante teste C11/A com Andre Green. Resultado: fatura virou paid sem o user ter recebido de verdade. Ambiguidade entre "como cobrar" (header) e "ja recebi" (options). Refactor: espelhar o padrao da avulsa (showRegistrarPagto): 1. Sub-question "A sessao ja foi paga?" radio Sim/Nao (default Nao) 2. Se Nao -> Select "Como vai cobrar?" [Apenas registrar pendente | Enviar link de pagamento (Asaas)] 3. Se Sim -> Select "Como recebeu?" [PIX | Dinheiro | Deposito | Maquininha] (sem prefixo "Ja recebi" — header ja deixa claro) Defaults safer: markPaid=false em ambos contextos (avulsa e pacote) pra evitar marcar paid sem querer. paymentMethod='pending' inicial. Handler em useMelissaAgenda._applyStatusDecisions: pos-processamento agora usa decision.markPaid explicito no caso pacote saldo: - markPaid=true -> record vira paid + payment_method=X - markPaid=false + paymentMethod='link' -> pending + payment_method='asaas' - markPaid=false + paymentMethod='pending' -> pending sem metodo Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
753182cfad |
agenda: C10 pos-test fixes + lock sessao encerrada + addendum doc
Bugs descobertos durante testes C10/A2/B/C com user:
1) _reloadRange not defined: _buildHandlers nao destruturava
_reloadRange do deps (passava mas nao desempacotava). Toast
ReferenceError ao tentar reload pos-status change. Fix em
useMelissaAgenda.js:_buildHandlers.
2) Badge $ amber em sessao cancelada: MelissaAgenda.vue badge gate
ignorava status. Cancelado+state=none (records cancelled
filtrados) ainda recebia badge "cobranca pendente". Fix: gate
sessaoEncerrada (cancelado/faltou) -> sem badge nunca.
3) Botao "Gerar cobranca" em sessao encerrada: AgendaEventoFinanceiro
Panel mostrava botao mesmo em cancelado/faltou -> user podia
emitir fatura nova em sessao que nao aconteceu. Fix: v-if
!isSessaoEncerrada + label muda pra "Sessao cancelada · sem
cobranca ativa".
4) paymentLabel usava ev.price em vez de paymentAmount pra state
'pending': caso multa R$ 30 mostrava R$ 150 (ev.price original).
Fix: usar paymentAmount tambem em pending.
5) Lock total em sessao encerrada (cancelado/faltou):
- "Editar sessao" SOME do popover
- Realizada/Falta/Reagendar/Cancelar disabled com tooltip
- Apenas "Agendada" continua funcional (caminho explicito de
recuperacao). Single path de saida do estado encerrado.
Adicoes UX em AgendaStatusChangeConfirmDialog:
- Hint contextual sobre min_hours_notice explicando POR QUE multa
veio (des)marcada por padrao: "Cancelou 18.5h antes da sessao.
Regra: multa apenas quando cancelamento <2h -> sem multa por
padrao." Terapeuta ve a razao e pode inverter conscientemente.
Adicoes UX em MelissaEventoPanel:
- Botao "Agendada" (variante --info azul cyan) no grupo status
pra reset/recuperacao. CSS .evento-act--info hover + is-current.
Doc:
- Addendum C10 no topo de src/docs/agenda-compromisso-financeiro
-cenarios.html capturando todas as divergencias/melhorias vs
mockup original + 3 pendencias pos-C13 (reverse transitions,
popover snapshot, A2 markPaid stale).
Pendencias salvas em memoria pra puxar pos-C13.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
3caf5792f8 |
agenda popover: botao Agendada + fixes pos-C10/B
Adicoes (durante teste C10/A2): - Botao "Agendada" no popover (pi-calendar, variante --info azul cyan) pra permitir reset de status realizado/faltou/cancelado voltando pra agendado sem precisar abrir o AgendaEventDialog. Wire-up: emit 'agendar' -> onAgendar -> updateEventoStatus. - CSS .evento-act--info: hover + is-current com tom cyan (#38bdf8 do domainColors da agenda). Highlight generico rgba(255,255,255,0.12) era invisivel em light mode. Bug fixes durante teste C10/B com Otto Rank: - MelissaEventoPanel paymentLabel: usar paymentAmount tambem pra state='pending' (antes so 'paid' usava; pending caia em ev.price e mostrava R$ 150 original quando o pendente real era R$ 30 da multa). - useMelissaAgenda onUpdateSeriesEvent: chamar _reloadRange() apos _applyStatusDecisions. Sem isso o paymentStateMap+amountMap nao re-populavam apos status change com multa -> FullCalendar e popover ficavam stale ate F5/troca de view. Pendencia salva em memoria: travas em reverse transitions (faltou->agendado deixa multa orfa). User hit pra valer com Otto durante teste, R$ 30 limpo manualmente no DB. Implementar pos-C13. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
d6423da9b4 |
agenda: pre-C10 fix _applyStatusDecisions cancela pendingRecord
Bug: ao mudar status pra faltou/cancelado com multa configurada
em financial_exceptions, _applyStatusDecisions INSERIA o novo
record da multa MAS deixava o pendingRecord original em pending.
Resultado: cobranca dupla (R$ 200 original + R$ 30 multa = R$ 230).
Fix em useMelissaAgenda.js:1450-1505:
- applyFine agora carrega data da sessao na description ("Multa
por falta - sessao dd/mm/aa") pro paciente identificar na fatura.
- Novo bloco 2b: cancela ctx.pendingRecord quando faltou/cancelado,
com nota de auditoria appendada em notes ("[YYYY-MM-DD] Cancelada
- substituida por multa de no-show" ou similar). Vale tanto pra
caso com multa quanto sem (status mudado sem aplicar multa).
Fix dormente em useAgendaFinanceiro.js:59 ('fixed' -> 'fixed_fee')
- charge_mode no schema eh 'fixed_fee' mas calcChargeAmount usava
'fixed' silenciosamente caia no fallback. Path nao exercitado na
Melissa (usa _applyStatusDecisions, nao handleStatusChange), mas
iria quebrar se algum dia fosse.
Pre-teste C10: financial_exceptions seedadas no DB para tenant
Bruno Terapeuta / owner Leonardo:
- patient_no_show: fixed_fee R$ 30
- patient_cancellation: full, min_hours_notice=2, default_consume_on_miss=true
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
ec0a24f5c8 |
agenda: C9 OK + rowGroup por paciente em /financeiro + bubble cobranca-atualizada
Cenário 9 (Per-session — Michael Balint 12 × R$ 150)
- Testado e passou. 1 rule + 12 agenda_eventos materializadas + 12
financial_records pending. Sem billing_contract. Badge $ em todas as
12 sessões. Conforme esperado.
/melissa/financeiro-lancamentos: agrupado por paciente
- DataTable com rowGroupMode='subheader' + groupRowsBy='patient_id'
- Header de grupo com avatar + nome + badge "N lançamento(s)"
- expandableRowGroups + v-model:expandedRowGroups; watcher popula
todos os grupos da página atual como expandidos (sempre que
recordsGrouped muda — refletindo paginação/filtros)
- Sort outer por nome do paciente, preserva inner order
(pai → filhos de multas/taxas via mesmo agenda_evento_id)
Bubble-up @cobranca-atualizada → M.refetch
- Antes: ao marcar como pago no dialog, o card no FC ficava stale
até trocar de view. AgendaEventoFinanceiroPanel emitia
cobranca-atualizada mas só o loadOccFinancialRecord do dialog
escutava; o _paymentStateMap da agenda nao re-rodava.
- Fix: AgendaEventDialog ganhou _onCobrancaAtualizada que dispara
loadOccFinancialRecord() E emit('cobranca-atualizada') pra cima.
MelissaLayout escuta nos 2 dialogs e chama M.refetch() +
refetchEventosHoje(). Card passa pra borda verde na hora.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
fad1f4ebd4 |
agenda: C8 OK + Usar/Revogar pacote saldo + UI de contract + ajustes UX
Cenário 8 (Pacote SALDO — Otávio Souza Ferreira 12 × R$ 50)
- Testado e passou. DB: 1 rule, 0 events, 1 contract (saldo), 0 records.
Visual: 12 virtuais limpas no calendário.
UI de pacote (saldo + upfront)
- _ruleContractMap em useMelissaAgenda: bulk-load popula contract info
(id, style, totalSessions, sessionsUsed, packagePrice) por
recurrence_id. Query recurrence_rules.patient_id como fonte
autoritativa — cobre saldo sem materializadas (sem isso, ruleToPatient
via records vinha vazio pra saldo)
- normalize injeta `contract` no evento via ruleContractMap
- MelissaEventoPanel: nova linha colorida (violeta saldo, verde upfront)
com "Pacote saldo · N/M usadas" ou "Pacote · N/M realizadas"
- AgendaEventDialog: info card mt-4 com header+body+hint explicando
modelo, gateado por occFinancialLoading (spinner durante carga
pra evitar piscar entre Usar/Revogar)
Handlers Usar/Revogar atômicos
- onUsarSessao em MelissaLayout: materializa virtual (preserva
determined_commitment_id da regra) → status=realizado +
billing_contract_id → create_financial_record_for_session →
sessions_used++ → (se atingiu total) contract.status=completed
- onRevogarSessao: cancela record + sessions_used-- + reativa contract
se estava completed + status=agendado. Bloqueia se record paid
(precisa estorno formal pelo Financeiro)
- Ambos aceitam payload {eventRow, contract} do dialog OU fallback
pra eventoSelecionado do popover
- Botão "Usar" verde no popover (paymentState=none) substituído por
"Revogar" vermelho (paymentState=pending). Equivalente "Usar agora"/
"Revogar uso" no info card do dialog
Fix enum status_evento_agenda
- 'realizada' não existe no enum — DB exige 'realizado' (masculino).
Corrigido em todas as ocorrências do handler
Fix campo "Título" indevido em sessão
- Sessão sem determined_commitment_id → selectedCommitment=null →
isSessionEvent=false → mostra campo Título (que é só pra não-sessão)
- Fix: materialize do Usar inclui determined_commitment_id (insert
path); update path backfilla via query da rule se NULL; Revogar
também backfilla pra consistência
Fix "Gerar fatura" não cabe em saldo
- Botão "Gerar fatura" do popover hide quando há contractInfo. Em
saldo, gerar fatura solta criaria cobrança duplicada sem incrementar
sessions_used. Fluxo correto: "Usar"
Recorrências Aplicadas — UI
- Header stats coloridos: total **azul**, realizadas **verde**,
faltaram **amber**, canceladas **cinza**, remarcadas **violeta**
- Pills com badge sólido por status (emerald-600 realizado, amber-600
faltou, stone-500 cancelado, violet-600 remarcado)
Race condition no dialog
- AgendaEventDialog mostrava botões Usar/Revogar baseado em
occFinancialRecord async; durante ~500ms de load, botão errado
podia piscar. Fix: spinner "Verificando estado…" enquanto
occFinancialLoading=true; botões só renderizam após
- Popover não fixado (race window pequena, fechar/reabrir resolve)
3 decisões UX confirmadas antes de codar
- Editar serviço pago → NÃO (cobrança fiscal imutável)
- Alternar Particular/Convênio/Gratuito em série cobrada → NÃO
- Gerar fatura individual em pacote upfront → NÃO (duplicação)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
1feb7112ff |
agenda: C7 OK + Fase 6 lock-edit ativada em Melissa + cross-week payment propagation
Cenário 7 (Pacote UPFRONT — Ana Souza Ferreira 4×R$ 200 = R$ 800)
- Testado e passou. User criou Ana, pagou os R$ 800 em dinheiro pelo
Financeiro. Borda verde + popover "Pago R$ 800" funcionando.
Fase 6 (lock-edit cobrada) ativada em Melissa
- Removido guard `if (!props.occurrenceMode) return;` em
loadOccFinancialRecord (useAgendaEventLifecycle.js:217+). Agora ele
carrega em ambos modos (Rail/Clínica E Melissa)
- loadOccFinancialRecord SINTETIZA record paid/pending pra siblings de
contrato upfront ativo — assim TODAS as ocorrências da série mostram
"Cobrança paga R$ 800 do pacote" no AgendaEventDialog
- AgendaEventDialog card Sessão/Honorários (flow Melissa) ganhou lock
template: Tag em vez de Select billingType quando occFinancialRecord
existe; Message com cadeado "Cobrança de R$ X já emitida"
- AgendaEventoFinanceiroPanel só renderiza dentro do lock quando record
é REAL (não sintetizado) — evita "Gerar cobrança" indevido em sibling
- paymentSummary do Resumo lateral unificado pra usar occFinancialRecord
(em vez do sessionPaymentRecord paralelo de antes)
Cross-week propagation de pacote upfront
- BUG: ao navegar pra semana só com virtuais (sem reais), bulk-load
caía no else `_rulePaymentMap.value = {}` — virtuais perdiam estado
paid herdado
- FIX em useMelissaAgenda._reloadRange:
* Maps (payment/amount/rule) inicializados SEMPRE no início
* Propagação roda independente de realIds.length (depende só de
ruleIdsInView.size>0, considera reais E virtuais com recurrence_id)
* Query cross-week: pra cada rule em view, busca QUALQUER evento
sibling em qualquer semana + seus records pra determinar estado do
contrato. Encontra o record do pacote mesmo em outra semana
- Saldo NÃO propaga (filter: charging_style='upfront' || NULL); cada
sessão de saldo gera cobrança individual ao realizar
- Memória durável: memory/project_cross_week_propagation.md
Visualização de virtuais cobertas
- MelissaEventoPanel.showPaymentRow: virtuais só escondem quando state
='none'. Com paid/pending herdado, exibem linha colorida
- MelissaAgenda fcEvents: isPaidSession e badge $ pendente removeram
exigência de !is_occurrence. Virtuais herdadas via propagação mostram
borda verde / badge amber
Atalho "Gerar fatura" no popover
- Pill amber pequeno ao lado de "A cobrar R$ X" quando paymentVariant
='none' && !is_occurrence. Click → gerarCobrancaManual direto, fecha
popover pra impedir double-click. Tooltip: "Gerar fatura agora"
- Wire em MelissaLayout via novo emit gerar-cobranca + handler
onGerarCobrancaQuick
Info de pacote no popover
- Header agora mostra "Sessão · Pacote · N sessões" (computed
seriesLabel lê de _raw do rule)
Botão "Excluir série inteira"
- Novo emit delete-series em MelissaEventoPanel + botão ao lado de
"Excluir sessão" quando evento tem recurrence_id
- Handler onDeleteSeries em MelissaLayout: hard delete em 3 etapas
(financial_records pendentes → agenda_eventos materializados →
recurrence_rules CASCADE leva exceptions). Bloqueia se algum record
paid (estorno via Financeiro primeiro)
cancel_session some da agenda
- useRecurrence.expandRules agora pula occurrence com exception.type=
'cancel_session' (era visível com status cancelado; doc dizia que
some). patient_missed/therapist_canceled/holiday_block permanecem
como histórico
recurrence_exceptions cancel idempotente
- MelissaLayout onDeleteEvento usa upsert com onConflict pra exception
cancel — não quebra mais com unique violation em re-cancel
billing_contract_id na 1ª materializada
- _createPackageContract agora .select() o contrato após insert e seta
billing_contract_id no insert da 1ª agenda_eventos materializada
onVerLancamentos cobre virtual de upfront
- Antes virtual sempre toast "Sem lançamentos". Agora busca records via
siblings da série pra encontrar o do pacote. Saldo/sem pacote continua
com toast
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
c23d0a574f |
agenda: C5+C6 testes OK + atalho Gerar fatura + RPC idempotência fix
DB - migration 20260519000001: create_financial_record_for_session passa a ignorar status='cancelled' na idempotência (era bug — cancelar e tentar regerar travava silencioso retornando o cancelado) Cenário 5 (convênio) — fixes pra save + visualização - Convênio: amount lia 'price' (null) → agora detecta via insurance_plan_id e usa insurance_value. payment_method forçado 'convenio' (era 'asaas') - Popover: ev.price era null em convênio → normalize expõe insurance_value e paymentLabel faz fallback. Linha mostra "A receber R$ X" corretamente - /financeiro: branch novo pra payment_method='convenio' → pill violeta com pi-id-card (antes ficava sem indicador, igual particular) Cenário 6 (recorrente sem pacote, Maria Magali) — materialização - chargeMode='none' não materializava a 1ª (todas viravam virtuais, sem badge $). Agora materializa a 1ª no fluxo de criação recorrente - Bug intermediário: usei 'paciente_id' (Portuguese) mas agendaRepository dropa esse campo. Corrigido pra 'patient_id' (English DB column) Atalho "Gerar fatura" no popover - Pill amber pequeno ao lado de "A cobrar R$ X" no popover (paymentVariant ='none' + sessão materializada) - Wire em MelissaLayout via emit gerar-cobranca + handler onGerarCobrancaQuick (chama gerarCobrancaManual, fecha popover pra impedir double-click) - Bulk-load do useMelissaAgenda e fetchRecord do AgendaEventoFinanceiroPanel agora filtram status='cancelled' (resolve badge $ residual + botão sumido) Header do popover: info de pacote/série - "Sessão · Pacote · N sessões" ou "Sessão X de Y" abaixo do tipo (computed seriesLabel lê do _raw da rule) Título do dialog "Sessão do Pacote · Sessão" - Quando commitment name é "Sessão" (default), drop pra evitar duplicação - Outros nomes (Avaliação, etc) permanecem com forma completa Excluir série inteira (popover) - Novo botão "Excluir série" no popover quando evento pertence a recorrência - Hard delete: financial_records pendentes → agenda_eventos materializados → recurrence_rules (CASCADE leva exceptions + rule_services) - Bloqueia se algum record tem status='paid' (estornar primeiro) cancel_session some da agenda - useRecurrence.expandRules agora pula occurrence com exception type= 'cancel_session' (era visível com status cancelado; doc dizia "some da agenda" mas código mantinha. Honra a promessa do diálogo) - patient_missed / therapist_canceled / holiday_block permanecem visíveis como histórico UX outros - "+ Novo convênio" toolbar em ConfiguracoesConveniosPage (botão faltava — empty state mandava clicar em botão inexistente) - InsurancePlanServiceQuickCreateDialog: cadastrar procedimento POR CIMA do AgendaEventDialog sem sair da agenda. Auto-seleciona quando nada estava selecionado antes - Hint contextual abaixo do card Sessão/Honorários: convênio = "Nº guia opcional"; gratuito = "sem cobrança". Particular sem hint - recurrence_exceptions cancel agora usa upsert com onConflict (idempotente, não quebra com unique violation em re-cancel) - goToConveniosConfig removida (dead code após quick-create inline) CSS - .aed-row-50 perdeu margin-bottom (user request) - .field-card.mb-4 ganhou margin-top: 1rem (scoped a composer wrappers) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
e95ed9b585 |
agenda: Fase 5 (status change/edit cobrada) + indicadores visuais + UX convenio
DB
- drop agenda_excecoes (substituida por financial_exceptions + lock-edit
baseado em financial_records)
- financial_records.payment_link (Asaas + link compartilhavel)
- financial_exceptions.consume_on_miss (rotular nao-show consome ou nao)
- billing_contracts.charging_style (upfront/saldo/per_session)
Payment refactor
- paymentSettlement -> paymentMethod (string) + markPaidNow (bool).
Handler aplica payment_method sempre; status='paid'+paid_at apenas
quando markPaidNow=true && method != 'link'. Asaas (link) sempre
liquida via webhook, nunca nasce paid.
- create_financial_record_for_session com pos-RPC patch pra payment_method
e (opcional) status='paid' quando user marca "ja recebi".
Indicadores visuais (3 canais distintos por estado)
- Paid: barra esquerda emerald-500 4px na agenda (MelissaAgenda),
pi-check-circle no popover/Resumo.
- Pending: badge \$ amber canto direito (mantido); linha amber no popover/
Resumo "A receber R\$ X (cobranca pendente)".
- Neutro: sem badge nem barra (compromisso pessoal, bloqueio, ou
ocorrencia virtual de pacote upfront/saldo).
- Bulk-load de paymentState em _reloadRange etapa 4 (1 query unica em
financial_records mapeada por agenda_evento_id).
- AgendaEventDialog Resumo lateral ganha linha entre pi-clock e
pi-map-marker via novo sessionPaymentRecord (sem guard de
occurrenceMode, contrario ao occFinancialRecord que continua so pra
Rail/Clinica). 5 estados: paid+paid_at, overdue+venceu, pending+vence,
sem cobranca c/ valor, sem cobranca s/ valor.
UX de convenio
- InsurancePlanServiceQuickCreateDialog novo: cadastra procedimento
POR CIMA do AgendaEventDialog sem sair da agenda. Auto-seleciona
novo procedimento so quando nada estava selecionado antes.
- Caixa cinza "Cadastrar procedimento" sempre visivel quando convenio
selecionado, com copy variavel (0 procedimentos: chamada urgente;
1+: "se quiser adicionar mais").
- "+ Novo convenio" toolbar em ConfiguracoesConveniosPage (botao
estava faltando, empty state mandava clicar em botao inexistente).
- Hint contextual abaixo do card Sessao/Honorarios: convenio = "N da
guia eh opcional", gratuito = "sem cobranca", particular = sem hint.
Label "N da Guia" tambem ganhou "(opcional)" no service-picker dialog.
Bug fixes
- pickDbFields whitelist faltando 'modalidade' (useMelissaAgenda.js:74)
— sessoes avulsas eram salvas como presencial independente da
escolha visual. Adicionado.
- goToConveniosConfig removida — fazia router.push("/therapist/
configuracoes/convenios") mas /configuracoes/* eh rota raiz, nao
filha. Substituida pelo quick-create inline (#1).
- bloqueioCobrindo + dialogBlockOverlap passados via deps em
_buildHandlers (refs do useMelissaAgenda nao sao acessiveis no
escopo de _buildHandlers).
Fase 5 (status change + edit cobrada)
- AgendaStatusChangeConfirmDialog: confirm dialog quando user muda
status pra realizada/faltou/cancelado, com opcoes de markPaid ou
gerar cobranca conforme o caso.
- useAgendaBloqueios novo composable: extrai logica de bloqueios
cinza (background events) do MelissaAgenda.
Doc viva
- src/docs/agenda-compromisso-financeiro-cenarios.html: 13 cenarios
de teste manual. C1-C4 ja validados. Cada teste validado vira parte
da doc final pra area de ajuda (pos-Fase 9).
Wiki/handoff
- agenda-compromisso-fluxo e agenda-billing-pesquisa-mercado (decisoes
arquiteturais sobre billing).
- HANDOFF.md atualizado.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
39cf0178e6 |
agenda: expandir e materializar ocorrencias de recorrencia (cross-layout)
PROBLEMA 1 — recorrencias virtuais nao apareciam em listas de sessao
============================================================
Sistema cria 1 row real em agenda_eventos por recorrencia (a primeira
ocorrencia) + 1 regra em recurrence_rules. As N-1 sessoes seguintes sao
geradas em runtime via useRecurrence.loadAndExpand. AgendaTerapeutaPage e
AgendaClinicaPage ja usavam loadAndExpand, mas composables compartilhados
("Hoje", widget, prontuario, ver todas) so liam agenda_eventos direto —
serie semanal de 4 sessoes aparecia como 1.
Fix em 3 composables cross-layout:
- usePatientSessions.load — range padrao -6mo a +12mo, filtra virtuais
por patient_id apos loadAndExpand. Cobre MelissaPaciente Tab Agenda +
PatientProntuario legacy.
- useMelissaEventos._fetchRange — merge real + virtual no range visivel.
Cobre widget "Hoje" (MelissaLayout), mini-cal, fallback standalone do
MelissaAgenda. Falha do expand cai silencioso pra so-reais.
- useMelissaTodasSessoesPaciente.fetch — mesma logica do paciente, range
-6mo a +12mo. Cobre "Ver todas as sessoes" do MelissaAgenda.
normalizeEvent agora aceita shape de virtual (paciente_nome/patient_name)
alem de joined query (patients.nome_completo). Expoe is_occurrence +
recurrence_id pra consumidores diferenciarem.
PROBLEMA 2 — UPDATE em id virtual quebra com "invalid input syntax for type uuid"
============================================================
Apos #1, ocorrencias virtuais aparecem na UI. Quando o user mudava status
(via botoes do MelissaEventoPanel, watcher do form.status no
AgendaEventDialog, ou botoes diretos no MelissaPaciente Tab Agenda), o
UPDATE caia direto no PostgreSQL com id "rec::ruleId::date" — sintaxe
invalida pra coluna UUID.
Materializacao em 4 caminhos:
- usePatientSessions.updateStatus(sessionOrId, status) — aceita row inteira
agora. Se virtual, busca row real por recurrence_id+date, ou cria nova
copiando campos da virtual (com status aplicado).
- useAgendaEventActions watcher do form.status — emit('updateSeriesEvent',
{ ..., row: form }) em vez de UPDATE direto. Parent materializa.
- MelissaLayout.updateEventoStatus — detecta virtual, delega pro
M.onUpdateSeriesEvent passando row: ev (sem isso, dialogEventRow ficaria
vazio porque user nao abriu o dialog antes — criava row orfa sem
patient_id).
- MelissaPaciente — @updateSeriesEvent do dialog local aponta pro
onSessaoDialogUpdateSeries (wrapper que delega pro composable que sabe
materializar). Antes apontava pro save normal.
useMelissaAgenda.onUpdateSeriesEvent atualizado:
- aceita row opcional do chamador (prioridade > dialogEventRow > vazio).
- guard: aborta com toast se rid (recurrence_id) for null, em vez de
criar row orfa.
- error check no .maybeSingle (antes ignorado — query falhando seguia pro
insert e duplicava sessoes).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
279b4f78e8 |
melissa/agenda: view lista 2 anos + selector SelectButton + sticky header + visual inativo
View lista: 'listWeek' -> custom 'listAll' (duration { years: 2 },
centrada em hoje via gotoDate(hoje - 1 ano) no setView). Antes a lista
mostrava so 7 dias e ocultava 3 das 4 ocorrencias semanais — agora cobre
passado + presente + futuro numa varredura. Cap MAX_RANGE_DAYS=730 do
loadAndExpand bate exato com 2 anos.
Banner showRecurrenceHint: aparece quando ha virtuais visiveis em
day/week/month (nao mostra em listAll). Texto curto + botao "Ver na
lista" que chama setView('lista').
Sticky day header (.fc-list-day): adicionado position:relative + z-index 3
+ bg opaco. Sem isso, .fc-event passava POR CIMA do header conforme
scroll (stacking context da row de evento ganhava do cushion sem z-index).
View selector: botoes manuais (.ma-cal__view-btn) -> PrimeVue SelectButton.
Visual herdado do tema, menos CSS custom.
Visual evento inativo: classNames=['ma-evt--inactive-patient'] em fcEvents
quando paciente_status === 'Arquivado'|'Inativo'. CSS aplica borda
tracejada + opacidade 0.58 (italico em list view). Mantem a cor do
commitment pra preservar contexto.
FC touch defaults: adiciona spread de FC_TOUCH_DEFAULTS (utility commitada
antes) — paridade touch <-> mouse, tap dispara select na hora em vez de
exigir long-press de 1000ms.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
6ad91e7853 |
MelissaPaciente: passa preset-commitment-id pro AgendaEventDialog (fix botao Salvar sumido)
User: "Botao pra salvar nao esta aparecendo".
CAUSA: o footer com botao Salvar tem v-if="step === 2". O lifecycle
do composer (linha 359 do useAgendaEventLifecycle) decide step inicial
assim:
if (composer.isEdit.value) step.value = 2;
else if (props.presetCommitmentId) {
composer.form.value.commitment_id = preset;
composer.step.value = 2;
} else step.value = 1;
Eu setava determined_commitment_id no eventRow (que populava
form.commitment_id via resetForm), mas NAO passava props.presetCommitmentId.
Resultado: lifecycle ia pra step=1 (escolha de tipo). E como lockType=true
escondia o conteudo do step 1 com v-if, o dialog ficava com Body vazio
+ footer step=2 nao renderizando.
FIX: passar :preset-commitment-id="sessaoDialogEventRow?.determined_commitment_id".
Como ja resolvo o id do commitment "Sessão" no goAgendar, reuso aqui
direto sem ter que duplicar o lookup.
Resultado: dialog abre direto em step=2, footer aparece, botao Salvar
visivel (com :disabled="!canSave" — ainda exige paciente_id +
items/billing valido, comportamento normal).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
cf1cd67314 |
MelissaPaciente: pre-popula eventRow com commitment_id + paciente nome/avatar/status
User detectou bug: jornada/freq/billing continuavam ocultos mesmo apos
o fix do commit anterior, e o resumo lateral nao mostrava o nome do
paciente apesar de aparecer no subtitle do header. Diagnostico correto:
form.paciente_nome estava vazio.
CAUSA: meu watch lockType (commit
|
||
|
|
30d09eb2ac |
AgendaEventDialog: props lockType + lockPatient + slot #headerLeft (additivos)
User escolheu caminho A: modificar AgendaEventDialog em vez de copiar.
Mudancas SAO ADITIVAS — comportamento atual dos 5 callsites legacy
(TherapistDashboard, PatientsListPage, MelissaAgenda,
MelissaAgendamentosRecebidos, MelissaLayout) preservado.
VALIDACAO: rodei os 7 spec files do agenda — 301 testes passaram.
Zero regressao.
ADICIONADO em src/features/agenda/components/AgendaEventDialog.vue
- Prop lockType (Boolean, default false): pula step 1 (escolha de tipo)
e vai direto pro form. Watch immediate em [lockType, modelValue]
forca step.value=2 quando lockType=true e dialog abre.
- Prop lockPatient (Boolean, default false): esconde botoes "trocar"/
"limpar" do paciente. Mostra icon de lock com tooltip "Paciente do
prontuario". Cobre o cenario "criar sessao pra paciente fixo" sem
precisar do isEdit que o patientLocked computed exige.
- Slot #headerLeft: substitui o conteudo esquerdo do header (default
era header-dot + headerTitle + previewRange). Permite callsites
customizar com icon+title+subtitle proprios sem mexer no resto do
header (X / actions).
- v-if no Step 1: "step === 1 && !lockType"
- v-if nos buttons trocar/limpar: "!patientLocked && !lockPatient"
- Lock icon: "patientLocked || lockPatient" + tooltip dinamico
MELISSAPACIENTE.VUE
- Reverte o inject-only do commit
|
||
|
|
88dff50223 |
MelissaPaciente: usa AgendaEventDialog GLOBAL via inject (em vez de dialog local)
User pediu pra trazer o AgendaEventDialog completo da Agenda pra dentro do prontuario. Estrategia: NAO duplicar o dialog (que ja vive no MelissaLayout). Em vez disso, reusar via provide/inject — pattern que ja existe (MELISSA_AGENDA_KEY). NOVO em src/layout/melissa/composables/useMelissaAgenda.js - onCreateEventoForPatient(patientId) — espelha onCreateEvento (defaults hoje proximo slot 15min, duracao default), mas injeta paciente_id no dialogEventRow. Adicionada ao return do composable. MELISSAPACIENTE.VUE - inject(MELISSA_AGENDA_KEY) pra acessar a instancia do useMelissaAgenda do MelissaLayout. - goAgendar(): chama melissaAgenda.onCreateEventoForPatient(patientId) (defensive: warn toast se nao tem inject ou funcao). - Watch em melissaAgenda.dialogOpen pra refetchar sessions+recorrencias quando o dialog fecha (true -> false), independente se foi save ou cancel. REMOVIDO (sem mais necessario — AgendaEventDialog faz tudo) - Refs novaSessaoOpen, novaSessaoForm - Catalogos FREQ_OPCOES, DIAS_SEMANA_OPCOES, QTD_SESSOES_OPCOES, SESSAO_TIPOS, SESSAO_DURACOES, SESSAO_MODALIDADES - Helpers toggleDiaSelecionado, qtdSessoesEfetiva, novaSessaoCtaLabel - Function salvarSessao (~110L de logica avulsa+recorrencia) - Import supabase (nao usado direto mais) - Import useRecurrence (era pro createRule no salvarSessao) - Import WEEKDAY_LABEL_BLOCK (era pro preview de freq) - Template <Dialog> Nova Sessao com header custom + form + freq chips + qtd sessoes + footer (~180L) Resultado: MelissaPaciente fica mais enxuto e usa exatamente o mesmo dialog completo que MelissaAgenda — todos os recursos do AgendaEventDialog (tipos de evento, paciente picker, comprometimento de servicos/billing, freq com preview de ocorrencias + conflitos, validacao por work rules, edicao de serie etc) ficam disponiveis no prontuario sem duplicacao. ESLint: 0 errors. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
b040e15c9b |
MelissaPaciente: header custom do dialog Nova Sessao (icone + titulo + nome)
Antes: header simples "Nova sessão". Agora: layout 3-col com: - Icon pi-calendar-plus em quadrado primary 40x40 - Title "Nova sessão" (1rem font-weight 700) - Subtitle: nome completo do paciente (truncate com ellipsis) CSS .mpa-dlg-head + variants. Reusavel se outros dialogs precisarem do mesmo padrao (Lancamento poderia usar tambem futuramente). ESLint: 0 errors. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
42a39ed3ea |
MelissaPaciente: dialog Nova Sessao usa "Frequencia" estilo AgendaEventDialog
User pediu pra trocar o checkbox "Repetir semanalmente" + radios pelo
mesmo widget de Frequencia que existe no AgendaEventDialog. Replicado
1:1 o pattern (chips + qtd sessoes).
REMOVIDO
- Checkbox "Repetir semanalmente"
- 3 radios de fim_tipo (open/count/data)
- Inputs inline associados (fim_count, fim_data)
ADICIONADO no form
- novaSessaoForm.freq: 'avulsa' (default) | 'semanal' | 'quinzenal' |
'diasEspecificos'
- novaSessaoForm.diasSelecionados: array<int> (so usado em
diasEspecificos)
- novaSessaoForm.qtdMode: '4' | '8' | '12' | 'personalizar'
- novaSessaoForm.qtdCustom: number (so usado em personalizar)
ADICIONADO catalogos (FREQ_OPCOES, DIAS_SEMANA_OPCOES, QTD_SESSOES_OPCOES)
e helpers (toggleDiaSelecionado, qtdSessoesEfetiva computed,
novaSessaoCtaLabel computed).
ADICIONADO no template:
- Chips horizontais "Avulsa / Semanal / Quinzenal / Dias específicos"
(estilo .mpa-freq-chip — pill arredondado, primary quando active)
- Preview com icon refresh: "Toda quarta, às 14:00" / "A cada 2 semanas,
toda quarta..."
- Grid de dias da semana (Seg Ter Qua Qui Sex Sab Dom) so quando
diasEspecificos
- Quantidade de sessoes: chips "4 sessoes / 8 / 12 / Personalizar"
+ InputNumber show-buttons quando personalizar
- Label dinamica do CTA: "Agendar sessão" (avulsa) / "Criar recorrência"
LOGICA salvarSessao mapeia freq -> recurrence_rules:
- avulsa: caminho original (createSession + INSERT agenda_eventos)
- semanal: type='weekly', interval=1, weekdays=[dow]
- quinzenal: type='biweekly', interval=2, weekdays=[dow]
- diasEspecificos: type='custom_weekdays', interval=1, weekdays=[selecionados]
Sempre com max_occurrences (qtd efetiva) — sem mais open-ended por
default. Toast detalha "{N} sessoes previstas".
Validacoes:
- diasEspecificos exige >=1 dia selecionado (toast warn)
- qtd efetiva >= 1 (cobrindo personalizar invalido)
CSS: ~120L (substitui o bloco .mpa-recur antigo). Usa accent var
--p-primary-color pra match do app theme. .mpa-freq-chip / .mpa-dia-chip
hover/active states. .mpa-freq-preview com bg color-mix do primary.
ESLint: 0 errors.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
9e76e4e6ea |
MelissaPaciente: bloco "Recorrencias do paciente" na Tab Agenda
User aprovou a ideia. Adiciona contexto "este paciente tem sessao toda segunda 14h" direto no prontuario, evitando duplicacao de regras e deixando claro o estado da serie. NOVO src/features/patients/composables/usePatientRecurrences.js (~110L) - load(patientId): SELECT recurrence_rules WHERE patient_id (DESC start_date) - cancel(ruleId) / reactivate(ruleId): UPDATE status + auto-reload - Computeds derivados: ativas, canceladas, totalAtivas, totalCanceladas - busy flag pra disable de buttons EXTENSAO src/features/patients/utils/patientFormatters.js - WEEKDAY_LABEL + WEEKDAY_LABEL_SHORT (arrays 0=Domingo..6=Sabado) - fmtRecurrenceLabel(rule): "Toda segunda às 14:00", "Quinzenal · Terça às 09:00", "Qua, Sex às 16:00" (custom_weekdays), "Mensal às 14:00", "Anual" — cobre todos os types do useRecurrence. - fmtRecurrenceFim(rule): "Sem data de fim" / "Até DD/MM/YYYY" / "N sessões no total" MELISSAPACIENTE.VUE - Composable + handlers (onCancelRecurrence, onReactivateRecurrence) com toast feedback. - recorrenciasShowCanc ref + recorrenciasVisiveis computed (toggle "ver canceladas"). - loadAll inclui recorrenciasHook.load. - salvarSessao no caminho recorrente recarrega sessions+recorrencias em Promise.all (regra recem-criada aparece na lista imediatamente). - 5o KPI na Tab Agenda: "Recorrencias" com count ativas + cap dinamica (cor #a855f7 quando > 0, cinza quando 0). - Bloco <section class="mpa-panel"> entre KPIs e filter chips listando rules ativas (default) ou todas (toggle "Ver canceladas" no header, so aparece quando ha canceladas): - Icon roxo .mpa-recur-item__icon - Top: label + Tag status (verde Ativa / amarelo Cancelada) - Meta: duracao + modalidade + fim + "desde DATE" - Obs (quando preenchido): block textual - Actions: pi-ban (cancelar) ou pi-undo (reativar) com tooltip - border-left adaptativa (#a855f7 ativo / cinza cancelado) + opacity 0.7 pros cancelados. - Mobile: stack icon+main em 2-col 2-row; actions full-width abaixo. CSS: ~120L novos. Padrao Melissa: status pills, icon roxo distintivo (diferente das sessoes que usam cinza), border-left por status. ESLint: 0 errors. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
f1d6fbad73 |
MelissaPaciente: dialog nova sessao integra useRecurrence (recorrencia semanal)
User apontou que ja existe sistema de recorrencia pronto (useRecurrence.js
+ tabela recurrence_rules + MelissaRecorrencias). Integrei no dialog de
nova sessao.
NOVO no dialog:
- Checkbox "Repetir semanalmente" + texto explicativo (cria serie no
mesmo dia da semana e horario)
- Quando ativado, mostra 3 opcoes radio:
- "Sem data de fim" (open-ended — continua ate cancelar)
- "Apos N sessoes" (max_occurrences)
- "Ate <data>" (end_date)
- Cada opcao com input inline disabled quando nao selecionada
- Label do botao salvar muda dinamicamente: "Agendar sessao" -> "Criar
recorrencia"
LOGICA salvarSessao() ramificada:
- Se repetir = false: caminho original (createSession + INSERT em
agenda_eventos)
- Se repetir = true: caminho NOVO via useRecurrence.createRule:
- type: 'weekly', interval: 1
- weekdays: [inicio.getDay()] (calculado do dia da semana selecionado)
- start_date: f.data
- end_date / max_occurrences conforme fim_tipo
- start_time: f.hora
- duration_min, modalidade, titulo_custom, observacoes, status: 'ativo'
- Insere row em recurrence_rules; ocorrencias sao geradas dinamicamente
pelo expandRules() do composable. Sessoes confirmadas/realizadas
viram rows reais sob demanda.
Validacoes adicionais:
- fim_tipo='data' exige fim_data preenchido (toast warn)
- fim_tipo='count' exige fim_count >= 1 (toast warn)
Reload das sessoes ao final pra refletir caso start_date seja hoje
(occurrence ja entra na timeline).
Toast de sucesso aponta pra "Recorrencias" como destino pra gerenciar
a serie.
ESLint: 0 errors.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
a8ab13b201 |
MelissaPaciente: dialog inline nova sessao + createSession mutation
Espelha o padrao do "Lancamento" mas pra agenda — botao "Agendar" agora
navega pra aba Agenda e abre dialog de nova sessao.
NOVO em src/features/patients/composables/usePatientSessions.js
- createSession(patientId, payload) — INSERT agenda_eventos com
status='agendado', resolve owner_id (auth.getUser) e tenant_id (lazy
import tenantStore). Auto-reload via _lastPatientId.
Validacao: inicio_em + fim_em obrigatorios.
Retorna {ok, data?, error?}.
NOVO em MelissaPaciente.vue
- Refs novaSessaoOpen + novaSessaoForm (tipo/data/hora/duracao_min/
modalidade/titulo_custom/observacoes)
- 3 catalogos:
- SESSAO_TIPOS: Sessao/Primeira/Retorno/Avaliacao/Devolutiva
- SESSAO_DURACOES: 30/40/45/50/55/60/90/120 min
- SESSAO_MODALIDADES: Presencial/Online
- goAgendar() agora alem de navegar pra aba Agenda, tambem inicializa
o form (default amanha 09:00, sessao 50min presencial) e abre o dialog.
- salvarSessao() handler com validacao (data + hora) e construcao de
inicio_em/fim_em a partir de data + hora + duracao_min. Local time
-> ISO via Date constructor.
- <Dialog> 460px com form: Tipo + grid 2-col (data + hora) + grid 2-col
(duracao + modalidade) + titulo opcional + observacoes Textarea.
- CSS .mpa-novo-lanc__opt pra "(opcional)" em cinza.
Validacoes:
- Data e hora obrigatorios (warn toast)
- Date constructor invalido -> warn toast
Pra criar sessoes mais complexas (recorrencia, multi-paciente, conflitos
de agenda), o user vai pra MelissaAgenda direto que tem o
AgendaEventDialog completo. Aqui no prontuario eh o caminho rapido.
ESLint: 0 errors.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
21c71f75d6 |
MelissaPaciente: addFinancial navega pra Financeiro + novo botao Agendar
- addFinancial(): antes so abria o dialog inline. Agora primeiro navega pra activeTab='financ' (da contexto visual), fecha drawer mobile e entao abre o dialog. User ve a aba Financeiro atualizar imediatamente apos salvar. - goAgendar() novo: navega pra activeTab='agenda', fecha drawer mobile. Sem dialog — a aba Agenda ja tem KPIs + lista por mes + acoes inline (realizada/falta/cancelar). Pra criar nova sessao o user usa MelissaAgenda direto (fora do prontuario). - Botao "Agendar" novo na sidebar Acoes Rapidas, abaixo de "Lancamento", com icon pi-calendar-plus verde #10b981. ESLint: 0 errors. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
64005a5b07 |
MelissaPaciente: fix openWhatsapp + dialog inline novo lancamento financeiro
DOIS BUGS DE COMPORTAMENTO:
1. openWhatsapp nao abria o drawer
conversationDrawerStore.openForPatient(patientId) espera STRING id,
nao objeto. Eu passava { id, name, phone, avatar_url } — store
ignorava e drawer nunca abria.
FIX: passar String(props.patientId) (mesmo pattern que MelissaPacientes).
BONUS: a store seta this.error sem dar throw quando paciente nao tem
telefone cadastrado. Detectamos com `if (err && !isOpen)` e mostramos
toast warn com a mensagem da store ("Paciente sem telefone cadastrado").
Funcao virou async pra aguardar o openForPatient.
2. addFinancial era placeholder "Em breve"
User correto: o sistema ja tem suporte (composables/useFinancialRecords
tem createManualRecord). Implementado dialog inline simples no
prontuario.
NOVO em src/features/patients/composables/usePatientFinancial.js
- createRecord(patientId, payload) — INSERT financial_records com
type='receita', resolve owner_id (auth.getUser) e tenant_id (lazy
import tenantStore pra evitar circular). Auto-reload via _lastPatientId.
Retorna {ok, data?, error?}.
NOVO em MelissaPaciente.vue
- Refs novoLancOpen + novoLancForm (description/amount/due_date/payment_method)
- PAYMENT_METHODS array (Pix/Cartao/Dinheiro/Transferencia/Boleto/Convenio)
- addFinancial() agora abre o dialog (era toast "em breve")
- salvarLancamento() handler com validacao (valor > 0, due_date obrigatorio)
- <Dialog> v-model:visible 420px com form: descricao + grid 2-col
(valor InputNumber BRL + vencimento date input) + select forma
- CSS .mpa-novo-lanc + responsive (1-col em <540px)
ESLint: 0 errors.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
301a7124a7 |
MelissaPaciente: editPatient abre PatientCadastroDialog INLINE (sem sair)
Bug reportado: ao clicar "Editar dados" no prontuario, o user era redirecionado pra /melissa/pacientes?edit=X (que entao abria o cadastro em MelissaPacientes). Isso saia da tela do prontuario — comportamento incorreto. FIX: importar PatientCadastroDialog no MelissaPaciente e abrir por cima da pagina (z-index PrimeVue ~1100 > .mpa-page z-index 40). Ao salvar, recarrega os dados do paciente in-place via detail.load(). ADICIONADO - Import PatientCadastroDialog - Refs locais cadastroOpen + cadastroPatientId - editPatient() agora seta refs e abre dialog (era router.push) - onPatientSaved() handler que fecha o dialog e refetcha o detail - <PatientCadastroDialog v-model="cadastroOpen" ...> renderizado depois da .mpa-page no template O watch route.query.edit em MelissaPacientes (Fase 8) continua valido pra deep-links externos, mas o fluxo MelissaPaciente -> editar nao usa mais essa rota. ESLint: 0 errors. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
5d2c389486 |
MelissaPaciente: fix sidebar cards encolhendo + gap das abas main
DOIS BUGS DE LAYOUT corrigidos via CSS (post-Fase 8 polish):
1. CARDS DA SIDEBAR sendo encolhidos
.mpa-side__scroll eh display:flex flex-direction:column. Os cards
.mpa-w filhos NAO tinham flex-shrink:0, entao quando havia muitos
cards stacked (Acoes + Nav 7 tabs + Sub-nav Perfil 6 + Vinculos),
o flex shrink default (1) reduzia cada card proporcionalmente.
Combinado com .mpa-w { overflow:hidden } (necessario pro radius),
itens internos das listas eram cortados/escondidos.
FIX: .mpa-side__scroll > .mpa-w { flex-shrink: 0; height: auto; }
Agora cada card cresce ate o tamanho real do conteudo, e o scroll
vertical do .mpa-side__scroll lida com overflow.
2. ABAS DO MAIN sem gap entre elementos
<div class="mpa-tab"> nao tinha CSS definido. Os filhos (KPIs grid,
panels, cards) ficavam colados. .mpa-main eh flex-col com gap, mas
como cada aba envolve seus elementos num <div .mpa-tab>, esse div
precisa replicar o spacing.
FIX: .mpa-tab { display: flex; flex-direction: column; gap: 12px; }
Visivel em todas as 7 abas. Fase 1 ja deveria ter incluido — passou
despercebido.
ESLint: 0 errors.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
159b80db6c |
MelissaPaciente: full-width + sidebar "Voltar pra Pacientes" no lugar de Configuracoes
Feedback do user pos-Fase 8: 1. Janela full-width (prontuario tem KPIs + tabelas + timeline — precisa de espaco). Removido o `right: max(6px, min(50%, calc(100% - 1006px)))` da .mpa-page no @media >=1024px. Mantém apenas inset 6px nos 4 lados. 2. Botao "Configuracoes" da sidebar removido (prontuario pertence a Pacientes, nao a Configuracoes — nao faz sentido o atalho global de cfg-* aqui). No mesmo lugar visual entra o botao "Voltar para Pacientes" com mesma classe .mpa-cfg-btn (reaproveita estilo) + modifier .mpa-cfg-btn--back pra hover sutilmente diferente. REMOVIDO - Import MelissaConfigList (nao usado mais) - Refs cfgOpen + funcoes toggleCfg/fecharCfg - Template do dual-mode (cfgOpen ? MelissaConfigList : cards) - CSS .mpa-cfg-btn.is-open + .mpa-cfg-btn__chev + .mpa-side__scroll--cfg ADICIONADO - close() agora faz history.back se houver historia, fallback pra /melissa/pacientes (cobre deep-link direto). Antes ia sempre pra /melissa/pacientes — agora respeita de onde o user veio (Agenda OU Pacientes). - goToPacientes() handler novo pro botao "Voltar pra Pacientes". - .mpa-cfg-btn--back hover style. Tooltip do X mudou de "Voltar (Esc)" pra "Fechar (Esc)" — semantica mais clara (o X fecha; o botao da sidebar voltar EXPLICITO). ESLint: 0 errors da minha mudanca. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
71ee51d38f |
MelissaPaciente Fase 8: wire-up final (Dialog -> route /melissa/paciente?id=X)
PLANO DE 8 FASES COMPLETO. Os 2 callsites Melissa do PatientProntuario.vue
legacy (3593L Dialog) trocam por navegacao pra MelissaPaciente nativo via
router.push. PatientProntuario continua intocado pros 2 callsites legacy
fora do Melissa (TherapistDashboard, PatientsListPage).
MELISSAPACIENTE.VUE — wire-up interno
- Imports: useRouter + useConversationDrawerStore
- close(): emit + router.push('/melissa/pacientes')
- editPatient(): emit + router.push('/melissa/pacientes', query: {edit: id})
pra MelissaPacientes auto-abrir o cadastroFullDialog
- openWhatsapp(): emit + conversationDrawerStore.openForPatient({id, name,
phone, avatar_url}) — drawer global desce sobre Melissa
- addFinancial(): emit + toast "Em breve" (Fase 9 — dialog inline)
MELISSAPACIENTES.VUE
- Removeu import PatientProntuario + refs prontuarioOpen/prontuarioPatient
- Removeu <PatientProntuario> template (substituido por comentario)
- abrirProntuario(p): router.push('/melissa/paciente', query: {id})
- onMounted detecta route.query.edit -> abre cadastroFullDialog +
router.replace pra limpar query (handshake com MelissaPaciente)
- Comentario header atualizado
MELISSAAGENDA.VUE
- Removeu import PatientProntuario + refs prontuarioOpen/prontuarioPatient
- Removeu <PatientProntuario> template
- abrirProntuarioPorId(id): router.push pra rota Melissa nativa
- abrirProntuarioPaciente / openProntuario / kebab "Prontuario" delegam
pra abrirProntuarioPorId
MELISSALAYOUT.VUE
- Render <MelissaPaciente> simplificado: so @close="fecharSecao".
Acoes edit/open-whatsapp/add-financial ficam internas.
ESLint: 0 errors da minha mudanca (9 pre-existentes nos arquivos tocados
sao baseline; confirmados via git stash — mesmos errors em ambos lados).
PLANO COMPLETO. Total de 8 commits no branch (Fases 1-8). MelissaPaciente.vue
~2400L + 5 composables (~407L) + utils ~280L. PatientProntuario.vue
intocado pra fallback legacy (TherapistDashboard, PatientsListPage).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
167e864b8a |
MelissaPaciente Fase 7: Tabs Documentos + Conversas (KPIs + embed componentes existentes)
Duas tabs entregues numa sessao — sao mais leves porque reusam
DocumentsListPage e PatientConversationsTab existentes (testados em
producao no PatientProntuario legacy) com KPIs Melissa por cima.
EXTENSAO src/features/patients/utils/patientFormatters.js
- fmtSize(bytes): legivel B/KB/MB/GB
- DOC_TYPE_LABEL map: atestado/receita/laudo/encaminhamento/termo/etc
- chConvLabel(c): whatsapp -> WhatsApp / sms -> SMS / email -> E-mail
EXTENSAO src/features/patients/composables/usePatientDocuments.js
- topType computed: { tipo, count, label } do tipo mais comum
- pendentes computed: count status_revisao === 'pendente'
- sizeTotalFormatted computed: fmtSize(totalBytes)
EXTENSAO src/features/patients/composables/usePatientMessages.js
- primeiraMensagem computed (mais antiga)
- canais computed: Set de m.channel unicos
MELISSAPACIENTE.VUE — Tab Documentos
- 4 KPIs adaptativos (so renderizam com dados):
Total + sizeTotalFormatted / Mais comum / Ultimo / Revisao pendente
- DocumentsListPage embedded no card Melissa (mpa-embed wrapper).
Reusa upload/preview/listagem testados.
MELISSAPACIENTE.VUE — Tab Conversas
- 4 KPIs: Mensagens com canais / Recebidas % / Enviadas % / Ultima
- CTA "Abrir conversa no drawer" estilo WhatsApp pill verde #25d366
que emite open-whatsapp pro parent (Fase 8 integra com
conversationDrawerStore.openForPatient)
- PatientConversationsTab embedded — thread completa com filter/media
CSS: ~50L novos (mpa-conv-cta + mpa-embed wrapper).
Removido kpiDocumentos nao usado (substituido por documentsHook.total
direto).
ESLint: 0 errors da minha mudanca.
PROXIMA: Fase 8 wire-up final (Dialog -> router.push em MelissaPacientes/
MelissaAgenda; decisao sobre TherapistDashboard + PatientsListPage).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|