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>
This commit is contained in:
Leonardo
2026-05-08 10:06:29 -03:00
parent e7c0f6c4f5
commit 167e864b8a
5 changed files with 364 additions and 32 deletions
@@ -153,6 +153,43 @@ export function fmtDateTimeBR(iso) {
return `${dd}/${mm}/${d.getFullYear()} ${hh}:${mi}`;
}
/**
* Bytes -> string legivel (B/KB/MB/GB).
*/
export function fmtSize(bytes) {
const b = Number(bytes) || 0;
if (b < 1024) return `${b} B`;
if (b < 1024 * 1024) return `${(b / 1024).toFixed(1)} KB`;
if (b < 1024 * 1024 * 1024) return `${(b / (1024 * 1024)).toFixed(1)} MB`;
return `${(b / (1024 * 1024 * 1024)).toFixed(1)} GB`;
}
/**
* Map de tipos de documento clinico pra label pt-br.
*/
export const DOC_TYPE_LABEL = {
atestado: 'Atestado',
receita: 'Receita',
laudo: 'Laudo',
encaminhamento: 'Encaminhamento',
termo: 'Termo',
termo_assinado: 'Termo assinado',
relatorio: 'Relatório',
declaracao: 'Declaração',
outro: 'Outro'
};
/**
* Channel label pra conversa: whatsapp -> WhatsApp, sms -> SMS, email -> E-mail.
*/
export function chConvLabel(c) {
const k = String(c || '').toLowerCase();
if (k === 'whatsapp') return 'WhatsApp';
if (k === 'sms') return 'SMS';
if (k === 'email') return 'E-mail';
return c || '';
}
export function fmtCurrency(v) {
if (v === null || v === undefined || v === '') return '—';
return `R$ ${Number(v).toLocaleString('pt-BR', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;