d7cd2541e448dbf0e13f26a6f7be23f8ee5a297e
24 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
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> |
||
|
|
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>
|
||
|
|
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>
|
||
|
|
e7c0f6c4f5 |
MelissaPaciente Fase 6: Tab Financeiro completa + mark paid (mutation que legacy nao tem)
EXTENSAO src/features/patients/utils/patientFormatters.js
- recordStatus(r): pago / vencido (paid_at NULL && due_date < hoje) / pendente
- RECORD_STATUS_LABEL map
- fmtPaymentMethod(v): PIX/Cartao/Dinheiro/Boleto/Transferencia/Convenio
cobrindo variantes pt-br + camelCase
EXTENSAO src/features/patients/composables/usePatientFinancial.js
- ref `busy` + `_lastPatientId` interno
- recordsOrdenados computed: DESC por due_date com fallback created_at
- markPaid(recordId): UPDATE financial_records SET paid_at=NOW() +
auto-reload via _lastPatientId. Retorna {ok, error?}
- markUnpaid(recordId): reverte (paid_at=NULL) + auto-reload
MELISSAPACIENTE.VUE — script
- Imports: recordStatus, RECORD_STATUS_LABEL, fmtPaymentMethod
- markRecordPaid(r): chama financialHook.markPaid + toast success/error
- revertRecordPaid(r): chama markUnpaid + toast
MELISSAPACIENTE.VUE — Tab Financeiro reescrita (substitui placeholder Fase 1)
- Loading state
- Empty state com CTA "Novo lancamento" (mpa-quick-btn--cta)
- 3 KPIs: Pago / Pendente com proxVenc / Em atraso (cor adaptativa
vermelho quando > 0, cinza quando 0)
- Header "Lancamentos" com badge count + botao "+ Novo" no canto
- Tabela 6-col responsiva:
- Vencimento (date mono + relative)
- Descricao
- Forma (PIX/Cartao/etc)
- Valor (mono right-aligned)
- Status pill colorida (verde pago / vermelho vencido / azul pendente)
- Action button (pi-check verde marca pago / pi-undo amarelo reverte)
- border-left adaptativa por status
- Mobile: tabela colapsa em cards 2-col 4-row
DIFERENCA DO LEGACY: o PatientProntuario.vue exibe a tabela mas NAO
permite marcar pago/reverter direto dela. MelissaPaciente adiciona essa
acao inline (mutation auto-reload).
CSS: ~190L novos. Padrao Melissa: status pills com color-mix, JetBrains
Mono pra valores, header cell uppercase letter-spacing.
ESLint: 0 errors da minha mudanca.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
8a8d2e05bd |
MelissaPaciente Fase 5: Tab Agenda completa (KPIs + filtros + grupos por mes + acoes)
EXTENSAO src/features/patients/utils/patientFormatters.js: +2 helpers
- fmtHourShort (HH:MM 24h pt-br) — usado na coluna data dos cards
- fmtDayShort (DOW abreviado pt-br sem ponto) — usado na coluna data
EXTENSAO src/features/patients/composables/usePatientSessions.js
- Novo ref `busy` pra disable de buttons durante mutation
- _lastPatientId guardado internamente pra auto-reload
- Nova funcao `updateStatus(sessionId, novoStatus)` que faz
supabase.from('agenda_eventos').update({status}) + auto-reload da
lista de sessoes. Retorna {ok, error?}.
MELISSAPACIENTE.VUE — script
- agendaFilter ref ('all' default) + AGENDA_FILTERS array com 6 opcoes
(Todas, Proximas, Passadas, Realizadas, Faltas, Canceladas)
- agendaSessoesFiltradas computed: filtra por future/past/status (regex)
- agendaAgrupadas computed: agrupa por "Mes de YYYY" DESC
- updateSessionStatus(ev, status, msg): chama sessionsHook.updateStatus +
toast de sucesso/erro
- Removido `void toast` (toast usado de verdade agora)
MELISSAPACIENTE.VUE — Tab Agenda reescrita (substitui placeholder Fase 1)
- 4 KPI cards no padrao Visao Geral (numerados 01-04):
Total / Realizadas (% do total) / Faltas (cor adaptativa) / Proxima
- 6 filter chips redondas (cor primary quando active)
- Empty state contextual (sem sessoes vs filtro vazio)
- Grupos por mes com header (label + badge count)
- Cards 3-col: data column (DOW + dia + hora) | main (status tag + chips
modalidade/duracao + relative + titulo + note 2-line clamp) | actions
(3 buttons: ok/warn/danger com tooltip + cor adaptativa no hover)
- Mobile: stack date+main em 2 cols; actions full-width abaixo
CSS: ~150L novos. Padrao visual Melissa: data column estilo calendario,
actions hover muda cor por intent (verde realiz / amarelo falta / vermelho
cancel), border-left por status.
ESLint: 0 errors da minha mudanca.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
1278e93b01 |
MelissaPaciente Fase 4: Tab Prontuario MVP (evolucao via session.observacoes)
O legacy PatientProntuario.vue tem a aba Prontuario como PLACEHOLDER
("Em breve" rich empty state). O MVP entregue aqui SUPERA o legacy: usa
agenda_eventos.observacoes como nota evolutiva — funcional ja hoje sem
precisar de schema novo.
ESTADO + COMPUTEDS adicionados ao MelissaPaciente.vue:
- pronFilter ref ('com-evolucao' default) + PRON_FILTERS com 5 opcoes
(Com evolucao / Todas / Realizadas / Faltas / Cancelamentos)
- pronSessions computed: filtra sessoes por status/presenca de observacoes
- sessoesComEvolucao computed: count de sessoes com observacoes nao-vazia
TEMPLATE Tab Prontuario (substitui placeholder Fase 1):
- Hint banner explicativo no topo (icon info + "Prontuario em construcao")
- 4 mini-stats em grid: com evolucao / realizadas / faltas / total
- 5 filter chips redondas — selecao default 'com-evolucao' filtra so
sessoes que tem nota
- Empty states contextuais (sem sessoes / sem evolucao / filtro vazio)
- Lista de sessoes:
- border-left colorida por status (verde/vermelho/amarelo/cinza)
- head com data + relative + chips status/modalidade/duracao
- block "Evolucao" destacado quando tem observacoes (bg medium + border
primary + label uppercase + texto pre-wrap)
- "Sem evolucao registrada" italico cinza quando nao tem
- Roadmap card (border dashed) listando 4 features futuras: anamnese
estruturada / plano terapeutico / evolucao por temas / assinatura
digital + LGPD Art. 18.
CSS: ~200L novos. Padrao Melissa (chips estilo MelissaTags, border-left
adaptativa, label uppercase nos blocks de evolucao).
ESLint: 0 errors da minha mudanca.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
4fc0e3a02b |
MelissaPaciente Fase 3: Tab Perfil completa (6 sections stacked + anchors)
EXTENSAO: src/features/patients/utils/patientFormatters.js - +5 formatters: pickField (compartilhado), onlyDigits, fmtCPF (000.000.000-00), fmtRG (passthrough), fmtPhoneMobile ((XX) 9XXXX-XXXX), fmtGender (Masculino/Feminino/Nao-binario/Outro), fmtMarital (Solteiro/Casado/ Divorciado/Viuvo/Uniao estavel). MELISSAPACIENTE.VUE — script - 30+ field computeds usando pickField (cobre snake_case + camelCase): birthValue, telefone/Alternativo, email/Alternativo, genero, estadoCivil, naturalidade, ondeNosConheceu, encaminhadoPor, observacoes, notasInternas + 8 campos de endereco + 5 dados adicionais + 4 responsavel. - groupNames/groupLabel/groupCountLabel pra bloco Origem. - scrollToProfileSection(key): liga sidebar sub-nav -> scrollIntoView do anchor #mpa-perfil-XXX. Em mobile fecha o drawer. MELISSAPACIENTE.VUE — Tab Perfil reescrita Diferente do PatientProntuario legacy que usa PrimeVue Accordion (1 painel aberto por vez), o Melissa nativo mostra os 6 cards stacked com scroll suave do sidebar sub-nav. Mais legivel em desktop, mais rapido de escanear. - 1. Informacoes Pessoais: 2-col com Dados de cadastro (nome/data nasc com idade inline/genero/estado civil/CPF/RG/naturalidade) + Contato + Origem (grupos/tags chips/onde nos conheceu/encaminhado por). tel: e mailto: links onde ha valor. Observacoes full-width quando preenchido. - 2. Endereco: grid 2-col com 8 fields. - 3. Dados Adicionais: grid 2-col com escolaridade/profissao/parente/grau/ tel parente. - 4. Responsavel: 1-col com nome/CPF/tel + observacao block textual. - 5. Anotacoes Internas: card com hint lock + textblock min-height. - 6. Sessoes: lista compacta scrollable (max-height 360px) com titulo/ data/duracao/modalidade chips + tag status. CSS: ~250L novos pros componentes (mpa-fields/field-row/field-grid-2/ field-block/sess/sess-list). Pattern visual Melissa: cards com label uppercase, separadores horizontais sutis, links primary, monospace pra CPF/RG/CEP. ESLint: 0 errors da minha mudanca. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
ab7526b8d7 |
MelissaPaciente Fase 2: Tab Visao Geral completa (4 KPIs + timeline + msgs + notas)
Reescreveu o placeholder da aba Visao Geral por uma versao 1:1 do
PatientProntuario.vue legado, com estilo Melissa nativo e dados
alimentados pelos composables criados na Fase 1.
NOVO: src/features/patients/utils/patientFormatters.js (~165L)
- Helpers compartilhaveis extraidos do PatientProntuario:
parseDateLoose, fmtDateBR, fmtDateTimeBR, fmtCurrency, fmtRelative
(pt-br: "agora"/"ha 5 min"/"em 2 dias"/"ha 3 sem"), sessionDuration,
calcAge.
- STATUS_LABEL e STATUS_SEVERITY pra mapear status de sessao (cobre
variantes: realizado/realizada, falta/faltou, cancelado/cancelada).
- tagStyle com contraste auto (luminance WCAG-ish: bg colorido +
texto preto/branco baseado em luminance < 0.45).
- Sera reutilizado pelas Fases 3-7 e na Fase 8 substitui as funcoes
duplicadas do PatientProntuario.
EXTENSAO de composables (Fase 1):
- usePatientSessions: novo computed `ultimasAtendidas` (top 6 sessoes
com status realiz/falt/cancel/remarc pra Timeline). totalRealizadas/
Faltas/Canceladas refinados pra usar regex (cobre variantes pt-br).
- usePatientFinancial: novo computed `statusFinanceiro` que retorna
{ emDia: bool, proxVenc: record, totalPendente, totalPago, vencidos }
pra alimentar KPI 02 com info detalhada de status financeiro.
MELISSAPACIENTE.VUE — Visao Geral reescrita:
- 4 KPI cards ricos (substituem os simples da Fase 1):
- 01 Sessoes: realizadas / total + faltas + canceladas
- 02 Pagamento: status (Em dia/atraso) + prox venc + cor adaptativa
(vermelho atrasado / primary ok)
- 03 Proxima sessao: relative + datetime + modalidade
- 04 Mensagens: ultima relative + direction + count
- Grid 2-col abaixo (1.4fr / 1fr em >=900px):
- Timeline coluna esquerda: dots coloridos por status, tags severity,
chips modalidade + duracao, nota observacoes inline.
- Coluna direita: Mensagens recentes (4) com border-left in/out +
meta direction/relative + body 3-line clamp; Notas e observacoes
em card papel com label uppercase e icone lock.
- Removeu kpiEmAberto/Atrasado nao usados (statusFinanceiro encapsula).
CSS: ~280L novos pros componentes (KPIs ricos, panel base, empty rich,
timeline, mensagens, notas). Mantem o pattern visual Melissa.
ESLint: 0 errors da minha mudanca.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
df61cc4d99 |
MelissaPaciente Fase 1: foundation (5 composables + skeleton 7 tabs + slug paciente)
Inicio do port do PatientProntuario.vue (3593L Dialog) pra Melissa nativo. Plano em 8 fases — esta entrega cobre apenas a Fase 1 (foundation). PatientProntuario continua intocado nos 4 callsites (TherapistDashboard, MelissaAgenda, MelissaPacientes, PatientsListPage); migration acontece nas fases 2-8. 5 COMPOSABLES NOVOS em src/features/patients/composables/ - usePatientDetail.js (108L): patients + groups + tags - usePatientSessions.js (83L): agenda_eventos + computeds proxima/ultima/totais - usePatientFinancial.js (82L): financial_records + computeds totalRecebido/Aberto/Atrasado - usePatientMessages.js (64L): conversation_messages + computeds recentes/totalIn/Out - usePatientDocuments.js (70L): documents + computeds total/Bytes/tiposCount Cada composable encapsula a query original do PatientProntuario.vue + adiciona computeds derivados. Reutilizaveis em outros lugares no futuro (dashboards, relatorios, etc). MELISSAPACIENTE.VUE NOVO (1190L) em src/layout/melissa/ - Prefixo CSS .mpa-*. Chrome glass + drawer mobile + right: max(...) >=1024px (mesmo padrao MelissaAgendador/Negocio). - Header: avatar + nome + ageLabel + pronomes + Tag status/convenio + risco-elevado pill + actions (Conversar / Editar / Close). - Subheader condicional: banner risco elevado. - Body 2-col: sidebar 320px (esquerda, drawer no mobile) + main flex 1. - Sidebar com 4 cards: Acoes Rapidas / Navegacao 7 tabs / Sub-nav Perfil / Vinculos (chips grupos+tags). - Main: 7 tabs (Visao Geral / Perfil / Prontuario / Agenda / Financeiro / Documentos / Conversas). Visao Geral ja mostra 4 KPIs reais via composables. Outras 6 abas com placeholders "Em desenvolvimento — Fase X". MELISSALAYOUT.VUE - Import MelissaPaciente. - SECOES.paciente entry novo. - 'paciente' adicionado ao MELISSA_NON_CONFIG_SLUGS. - Render condicional com :patient-id="String(route.query.id || '')" — navegacao via /melissa/paciente?id=xxx. ESLint: 0 errors da mudanca. 2 errors pre-existentes em MelissaLayout (duplicate key 'financeiro' L242, empty block L1130) — nao toquei essas linhas. PatientProntuario tem outros pre-existentes nao tocados. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |