ab7526b8d7
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>
98 lines
3.3 KiB
JavaScript
98 lines
3.3 KiB
JavaScript
/*
|
|
|--------------------------------------------------------------------------
|
|
| Agência PSI
|
|
|--------------------------------------------------------------------------
|
|
| Arquivo: src/features/patients/composables/usePatientSessions.js
|
|
|
|
|
| Carrega sessoes (agenda_eventos) do paciente. Limit 100 mais recentes
|
|
| ordenadas desc por inicio_em. Compativel com a logica original do
|
|
| PatientProntuario.vue.
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
import { ref, computed } from 'vue';
|
|
import { supabase } from '@/lib/supabase/client';
|
|
|
|
export function usePatientSessions() {
|
|
const sessions = ref([]);
|
|
const loading = ref(false);
|
|
const error = ref('');
|
|
|
|
async function load(patientId) {
|
|
if (!patientId) {
|
|
sessions.value = [];
|
|
return;
|
|
}
|
|
loading.value = true;
|
|
error.value = '';
|
|
sessions.value = [];
|
|
try {
|
|
const { data, error: err } = await supabase
|
|
.from('agenda_eventos')
|
|
.select('id, inicio_em, fim_em, status, modalidade, tipo, titulo, titulo_custom, observacoes')
|
|
.eq('patient_id', patientId)
|
|
.order('inicio_em', { ascending: false })
|
|
.limit(100);
|
|
if (err) throw err;
|
|
sessions.value = data || [];
|
|
} catch (e) {
|
|
error.value = e?.message || 'Falha ao carregar sessões.';
|
|
sessions.value = [];
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
}
|
|
|
|
// Helpers derivados — proxima sessao agendada e status corrente
|
|
const proximaSessao = computed(() => {
|
|
const now = Date.now();
|
|
return [...sessions.value]
|
|
.filter((s) => s.inicio_em && new Date(s.inicio_em).getTime() >= now)
|
|
.sort((a, b) => new Date(a.inicio_em) - new Date(b.inicio_em))[0] || null;
|
|
});
|
|
|
|
const ultimaSessao = computed(() => {
|
|
const now = Date.now();
|
|
return sessions.value
|
|
.filter((s) => s.inicio_em && new Date(s.inicio_em).getTime() < now)
|
|
.sort((a, b) => new Date(b.inicio_em) - new Date(a.inicio_em))[0] || null;
|
|
});
|
|
|
|
const totalSessoes = computed(() => sessions.value.length);
|
|
|
|
// Conta status com regex pra cobrir variantes pt-br
|
|
// (realizada/realizado/presente; falta/faltou; cancelada/cancelado/remarcada).
|
|
const totalRealizadas = computed(() =>
|
|
sessions.value.filter((s) => /realiz|present/i.test(String(s.status || ''))).length
|
|
);
|
|
const totalFaltas = computed(() =>
|
|
sessions.value.filter((s) => /falt/i.test(String(s.status || ''))).length
|
|
);
|
|
const totalCanceladas = computed(() =>
|
|
sessions.value.filter((s) => /cancel|remarca/i.test(String(s.status || ''))).length
|
|
);
|
|
|
|
/**
|
|
* Top 6 sessoes "atendidas" (qualquer status que indica encontro: realizado,
|
|
* faltou, cancelado, remarcado) — alimenta a Timeline da Visao Geral.
|
|
*/
|
|
const ultimasAtendidas = computed(() =>
|
|
sessions.value
|
|
.filter((s) => /realiz|present|falt|cancel|remarca/i.test(String(s.status || '')))
|
|
.slice(0, 6)
|
|
);
|
|
|
|
return {
|
|
sessions,
|
|
loading,
|
|
error,
|
|
load,
|
|
proximaSessao,
|
|
ultimaSessao,
|
|
totalSessoes,
|
|
totalRealizadas,
|
|
totalFaltas,
|
|
totalCanceladas,
|
|
ultimasAtendidas
|
|
};
|
|
}
|