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>
This commit is contained in:
Leonardo
2026-05-08 10:21:35 -03:00
parent 167e864b8a
commit 71ee51d38f
5 changed files with 142 additions and 54 deletions
+55
View File
@@ -50,6 +50,61 @@ Touched: none
## [2026-05-08 00:00] session | Melissa cfg-* nativas + temas + cronometro DB
Touched: none
## [2026-05-08 19:30] session | MelissaPaciente Fase 8 — wire-up final (Dialog -> route)
Touched: none
Detalhes: PLANO DE 8 FASES COMPLETO. Os 2 callsites Melissa do
PatientProntuario.vue legacy (3593L Dialog) trocam por router.push pra
/melissa/paciente?id=X. PatientProntuario continua intocado pros 2
callsites legacy (TherapistDashboard, PatientsListPage) quando user nao
esta no layout Melissa.
MELISSAPACIENTE.VUE — wire-up
- useRouter + useConversationDrawerStore.
- close(): emit + router.push('/melissa/pacientes') (volta pra lista).
- editPatient(): emit + router.push('/melissa/pacientes', query: {edit: id})
pra MelissaPacientes detectar e abrir o cadastroFullDialog automaticamente.
- openWhatsapp(): emit + conversationDrawerStore.openForPatient({id, name,
phone, avatar_url}) — drawer global desce sobre Melissa sem fechar.
- addFinancial(): emit + toast "Em breve" (Fase 9 — dialog inline).
MELISSAPACIENTES.VUE — wire-up
- Removeu import PatientProntuario, refs prontuarioOpen/prontuarioPatient,
template PatientProntuario.
- abrirProntuario(p): router.push('/melissa/paciente', query: {id}).
- onMounted: detecta route.query.edit -> abre cadastroFullDialog +
router.replace pra limpar a query. Permite navegacao MelissaPaciente
-> MelissaPacientes?edit=X -> auto-open do cadastro.
- Comentario header atualizado.
MELISSAAGENDA.VUE — wire-up
- Removeu import PatientProntuario, refs prontuarioOpen/prontuarioPatient,
template PatientProntuario.
- abrirProntuarioPorId(id): router.push('/melissa/paciente', query: {id}).
- abrirProntuarioPaciente() / openProntuario(patient) / item kebab
"Prontuario" todos delegam pra abrirProntuarioPorId.
MELISSALAYOUT.VUE
- Render do <MelissaPaciente> simplificado: so passa @close="fecharSecao".
Acoes edit/open-whatsapp/add-financial agora ficam internas no MelissaPaciente.
ESLint: 0 errors da minha mudanca (9 errors pre-existentes nos arquivos
tocados, mesmos de antes do diff — confirmados via git stash baseline).
PLANO COMPLETO. Status final por fase:
1. Foundation (composables + skeleton) — done (Fase 1)
2. Tab Visao Geral (KPIs ricos + timeline + msgs) — done (Fase 2)
3. Tab Perfil (6 sections stacked + anchors) — done (Fase 3)
4. Tab Prontuario MVP (evolucao via observacoes) — done (Fase 4)
5. Tab Agenda (KPIs + filtros + grupos + acoes) — done (Fase 5)
6. Tab Financeiro (KPIs + tabela + mark paid) — done (Fase 6)
7. Tabs Documentos + Conversas (KPIs + embeds) — done (Fase 7)
8. Wire-up final (Dialog -> route) — done (Fase 8)
PatientProntuario.vue (3593L) NAO foi deletado — continua usado pelo
TherapistDashboard.vue (homepage do role therapist) e PatientsListPage.vue
(rota /therapist/patients fora do Melissa). Quando user troca pra Melissa
em /account/profile, ele ve a versao nativa (MelissaPaciente).
## [2026-05-08 18:30] session | MelissaPaciente Fase 7 — Tabs Documentos + Conversas
Touched: none
Detalhes: Duas tabs entregues numa sessao (sao mais leves: KPIs + embed
+11 -28
View File
@@ -34,7 +34,6 @@ import MelissaAgendaSearchPopover from './MelissaAgendaSearchPopover.vue';
import MelissaAgendaActionsPopover from './MelissaAgendaActionsPopover.vue';
import PatientCadastroDialog from '@/components/ui/PatientCadastroDialog.vue';
import ComponentCadastroRapido from '@/components/ComponentCadastroRapido.vue';
import PatientProntuario from '@/features/patients/prontuario/PatientProntuario.vue';
import { useConversationDrawerStore } from '@/stores/conversationDrawerStore';
import { getSessionCounts } from '@/features/patients/services/patientsRepository';
// `Menu` PrimeVue: NÃO importar explicitamente — projeto usa auto-import
@@ -234,13 +233,9 @@ function pacienteRowDblclick(id) {
abrirProntuarioPorId(id);
}
function abrirProntuarioPorId(id) {
const p =
props.pacientes.find((x) => x.id === id) ||
pacientesAside.value.find((x) => x.id === id) ||
null;
if (!p) return;
prontuarioPatient.value = { ...p };
prontuarioOpen.value = true;
if (!id) return;
// Fase 8 wire-up: navega pra MelissaPaciente nativo.
router.push({ path: '/melissa/paciente', query: { id: String(id) } });
}
// ── Calendar (FullCalendar) ───────────────────────────────────
@@ -992,8 +987,6 @@ function onPatientCreated() {
// Pattern espelha PatientsListPage (goEdit/goConversation/openProntuario).
// Aparece no .melissa-dock via Teleport quando há paciente selecionado.
const conversationDrawerStore = useConversationDrawerStore();
const prontuarioOpen = ref(false);
const prontuarioPatient = ref(null);
const sessionCountsMap = ref(new Map()); // id → count (cache)
// Cache pra patients carregados sob demanda — quando o pacienteSelecionadoId
@@ -1169,9 +1162,8 @@ function abrirWhatsappPaciente() {
}
function abrirProntuarioPaciente() {
const p = pacienteSelecionado.value;
if (!p) return;
prontuarioPatient.value = { ...p };
prontuarioOpen.value = true;
if (!p?.id) return;
abrirProntuarioPorId(p.id);
}
// API pública pra MelissaLayout chamar via ref (botão "Editar paciente"
// do MelissaEventoPanel). Abre o PatientCadastroDialog já no modo edição.
@@ -1211,7 +1203,7 @@ const kebabItems = computed(() => {
return [
{ label: 'Sessões', icon: 'pi pi-history', command: () => { pacienteSelecionadoId.value = p.id; abrirSessoesPaciente(); } },
{ label: 'WhatsApp', icon: 'pi pi-whatsapp', command: () => conversationDrawerStore.openForPatient(String(p.id)) },
{ label: 'Prontuário', icon: 'pi pi-file', command: () => { prontuarioPatient.value = { ...p }; prontuarioOpen.value = true; } },
{ label: 'Prontuário', icon: 'pi pi-file', command: () => abrirProntuarioPorId(p.id) },
{ label: 'Editar', icon: 'pi pi-pencil', command: () => { editPatientId.value = String(p.id); cadastroFullDialog.value = true; } }
];
});
@@ -1220,9 +1212,8 @@ const kebabItems = computed(() => {
// MelissaEventoPanel emite ações que o parent precisa orquestrar com
// a Agenda — aqui ficam os métodos invocáveis via ref.
function openProntuario(patient) {
if (!patient) return;
prontuarioPatient.value = { ...patient };
prontuarioOpen.value = true;
if (!patient?.id) return;
abrirProntuarioPorId(patient.id);
}
defineExpose({
refetch: refetchEventosFc,
@@ -1905,17 +1896,9 @@ defineExpose({
<!-- Menu kebab (mobile) — abre via toggleKebab a partir de qualquer .ma-pat -->
<Menu ref="kebabMenu" :model="kebabItems" popup append-to="body" />
<!-- Dialog Prontuário — reaproveita componente do PatientsListPage.
:key força re-mount quando troca de paciente. Optional chain
em prontuarioPatient pra blindar contra bloco-tree opt do Vue. -->
<PatientProntuario
v-if="prontuarioPatient"
:key="prontuarioPatient?.id || 'none'"
v-model="prontuarioOpen"
:patient="prontuarioPatient"
@close="prontuarioOpen = false"
@edit="(id) => { prontuarioOpen = false; editPatientId = String(id); cadastroFullDialog = true; }"
/>
<!-- Prontuario migrado pra MelissaPaciente nativo (Fase 8 wire-up).
abrirProntuarioPorId(id) navega pra /melissa/paciente?id=X. -->
</section>
</template>
+4 -5
View File
@@ -1865,15 +1865,14 @@ function onKeydown(e) {
@close="fecharSecao"
/>
<!-- Pagina nativa do prontuario do paciente (Fase 1 foundation).
ID vem via route.query.id (?id=xxx). Quando ID muda, MelissaPaciente
refetcha tudo via composables (usePatientDetail/Sessions/etc). -->
<!-- Pagina nativa do prontuario do paciente (Fase 8 wire-up).
ID vem via route.query.id (?id=xxx). MelissaPaciente cuida
internamente das acoes (close -> /melissa/pacientes; edit ->
/melissa/pacientes?edit=<id>; open-whatsapp -> conversationDrawerStore). -->
<MelissaPaciente
v-if="layoutReady && secaoAberta === 'paciente'"
:patient-id="String(route.query.id || '')"
@close="fecharSecao"
@edit="(id) => fecharSecao()"
@open-whatsapp="(id) => fecharSecao()"
/>
<MelissaPlano
+53 -4
View File
@@ -19,7 +19,9 @@
* Prefixo CSS: .mpa-* (Melissa PAciente).
*/
import { ref, computed, watch, nextTick, onMounted, onBeforeUnmount } from 'vue';
import { useRouter } from 'vue-router';
import { useToast } from 'primevue/usetoast';
import { useConversationDrawerStore } from '@/stores/conversationDrawerStore';
import MelissaConfigList from './MelissaConfigList.vue';
import DocumentsListPage from '@/features/documents/DocumentsListPage.vue';
import PatientConversationsTab from '@/features/patients/prontuario/PatientConversationsTab.vue';
@@ -58,7 +60,9 @@ const props = defineProps({
});
const emit = defineEmits(['close', 'edit', 'add-financial', 'open-whatsapp']);
const router = useRouter();
const toast = useToast();
const conversationDrawerStore = useConversationDrawerStore();
// ── Composables de dados ───────────────────────────────────
const detail = usePatientDetail();
@@ -323,10 +327,55 @@ const kpiRealizadas = computed(() => sessionsHook.totalRealizadas.value);
const kpiMensagens = computed(() => messagesHook.messages.value.length);
// ── Acoes ──────────────────────────────────────────────────
function close() { emit('close'); }
function editPatient() { emit('edit', props.patientId); }
function addFinancial() { emit('add-financial', props.patientId); }
function openWhatsapp() { emit('open-whatsapp', props.patientId); }
// Volta pra lista de pacientes (preserva o estado de Melissa).
function close() {
emit('close');
router.push('/melissa/pacientes');
}
// Edit: navega pra /melissa/pacientes?edit=<id> e a propria
// MelissaPacientes detecta esse query param e abre o cadastroFullDialog.
function editPatient() {
emit('edit', props.patientId);
router.push({ path: '/melissa/pacientes', query: { edit: props.patientId } });
}
// Open WhatsApp: usa o conversationDrawerStore global (mesmo padrao
// que MelissaPacientes usa). O drawer desce sobre o Melissa sem fechar.
function openWhatsapp() {
emit('open-whatsapp', props.patientId);
if (!props.patientId) return;
const data = patientData.value || {};
try {
if (typeof conversationDrawerStore.openForPatient === 'function') {
conversationDrawerStore.openForPatient({
id: props.patientId,
name: data.nome_completo || data.nome || '',
phone: data.telefone || data.phone || '',
avatar_url: data.avatar_url || data.avatar || ''
});
}
} catch (e) {
toast.add({
severity: 'error',
summary: 'Falha ao abrir conversa',
detail: e?.message || 'Erro ao abrir o drawer.',
life: 3500
});
}
}
// Add financial: por enquanto so emite. Futuro: dialog inline de
// novo lancamento (Fase 9).
function addFinancial() {
emit('add-financial', props.patientId);
toast.add({
severity: 'info',
summary: 'Em breve',
detail: 'Dialog de novo lançamento será adicionado numa próxima sessão.',
life: 3000
});
}
// ── Load data quando patientId muda ────────────────────────
async function loadAll(id) {
+19 -17
View File
@@ -18,13 +18,14 @@
* e patient_patient_tag (via patientsRepository.listGroupsByPatient/Tags...)
*
* Integrações:
* - PatientProntuario (overlay dialog) — abre via duplo-click no card ou
* - MelissaPaciente (rota /melissa/paciente?id=X) — abre via duplo-click ou
* botão "Abrir prontuário" da COL 3
* - PatientCadastroDialog — cadastro completo / edição
* - PatientCreatePopover + ComponentCadastroRapido — fluxo de novo paciente
* - conversationDrawerStore — botão WhatsApp da COL 3
*/
import { ref, computed, watch, onMounted, onBeforeUnmount } from 'vue';
import { useRouter, useRoute } from 'vue-router';
import { useToast } from 'primevue/usetoast';
import { useConfirm } from 'primevue/useconfirm';
import { useMelissaPacientes } from './composables/useMelissaPacientes';
@@ -40,7 +41,6 @@ import {
softDeletePatient
} from '@/features/patients/services/patientsRepository';
import { usePatientLifecycle } from '@/composables/usePatientLifecycle';
import PatientProntuario from '@/features/patients/prontuario/PatientProntuario.vue';
import PatientCreatePopover from '@/components/ui/PatientCreatePopover.vue';
import PatientCadastroDialog from '@/components/ui/PatientCadastroDialog.vue';
import ComponentCadastroRapido from '@/components/ComponentCadastroRapido.vue';
@@ -52,6 +52,8 @@ const emit = defineEmits(['close', 'patient-created', 'goto-agenda', 'goto-grupo
const toast = useToast();
const confirm = useConfirm();
const tenantStore = useTenantStore();
const router = useRouter();
const route = useRoute();
const { reactivatePatient } = usePatientLifecycle();
const conversationDrawerStore = useConversationDrawerStore();
@@ -284,6 +286,13 @@ onMounted(() => {
isCompact.value = _mqCompact.matches;
_mqCompact.addEventListener('change', _onMqCompactChange);
}
// Fase 8 wire-up: se navegou pra ca com ?edit=<id> (vindo do
// MelissaPaciente), abre o cadastro full direto.
if (route.query.edit) {
editPatientId.value = String(route.query.edit);
cadastroFullDialog.value = true;
router.replace({ query: { ...route.query, edit: undefined } });
}
});
onBeforeUnmount(() => {
if (_mqMobile) _mqMobile.removeEventListener('change', _onMqMobileChange);
@@ -534,9 +543,6 @@ const cadastroFullDialog = ref(false);
const quickDialog = ref(false);
const editPatientId = ref(null);
const prontuarioOpen = ref(false);
const prontuarioPatient = ref(null);
function openCreatePopover(e) {
createPopoverRef.value?.toggle(e);
}
@@ -551,10 +557,12 @@ function onPatientCreated() {
refetchTudo();
}
// Abrir prontuario agora navega pra MelissaPaciente nativo (Fase 8 wire-up).
// O Dialog PatientProntuario.vue legacy continua existindo pros 2 callsites
// fora do Melissa (TherapistDashboard e PatientsListPage).
function abrirProntuario(p) {
if (!p) return;
prontuarioPatient.value = { ...p };
prontuarioOpen.value = true;
if (!p?.id) return;
router.push({ path: '/melissa/paciente', query: { id: String(p.id) } });
}
function editarPaciente(p) {
@@ -1349,15 +1357,9 @@ function sessaoStatusColor(s) {
@created="onPatientCreated"
/>
<!-- Dialog Prontuário :key força re-mount quando troca de paciente -->
<PatientProntuario
v-if="prontuarioPatient"
:key="prontuarioPatient?.id || 'none'"
v-model="prontuarioOpen"
:patient="prontuarioPatient"
@close="prontuarioOpen = false"
@edit="(id) => { prontuarioOpen = false; editPatientId = String(id); cadastroFullDialog = true; }"
/>
<!-- Prontuario migrado pra MelissaPaciente nativo (Fase 8 wire-up).
abrirProntuario(p) navega pra /melissa/paciente?id=X via router. -->
<!-- Dialog: Novo grupo (nome + cor) -->
<Dialog