1c2a2b6e19
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>
82 lines
2.6 KiB
JavaScript
82 lines
2.6 KiB
JavaScript
/*
|
|
|--------------------------------------------------------------------------
|
|
| Agência PSI
|
|
|--------------------------------------------------------------------------
|
|
| Arquivo: src/features/patients/composables/usePatientDocuments.js
|
|
|
|
|
| Documentos do paciente — campos pra KPIs (count, tipo, última atualização).
|
|
| O detalhe completo fica em DocumentsListPage. Filtra deletados.
|
|
|
|
|
| I/O delegada ao patientsRepository.
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
import { ref, computed } from 'vue';
|
|
import { listDocumentsByPatient } from '@/features/patients/services/patientsRepository';
|
|
import { fmtSize, DOC_TYPE_LABEL } from '@/features/patients/utils/patientFormatters';
|
|
|
|
export function usePatientDocuments() {
|
|
const documents = ref([]);
|
|
const loading = ref(false);
|
|
const error = ref('');
|
|
|
|
async function load(patientId) {
|
|
if (!patientId) {
|
|
documents.value = [];
|
|
return;
|
|
}
|
|
loading.value = true;
|
|
error.value = '';
|
|
documents.value = [];
|
|
try {
|
|
documents.value = await listDocumentsByPatient(patientId);
|
|
} catch (e) {
|
|
error.value = e?.message || 'Falha ao carregar documentos.';
|
|
documents.value = [];
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
}
|
|
|
|
const total = computed(() => documents.value.length);
|
|
const totalBytes = computed(() => documents.value.reduce((acc, d) => acc + Number(d.tamanho_bytes || 0), 0));
|
|
const tiposCount = computed(() => {
|
|
const map = new Map();
|
|
documents.value.forEach((d) => {
|
|
const k = d.tipo_documento || 'outro';
|
|
map.set(k, (map.get(k) || 0) + 1);
|
|
});
|
|
return Object.fromEntries(map);
|
|
});
|
|
const ultimo = computed(() => documents.value[0] || null);
|
|
|
|
const topType = computed(() => {
|
|
const por = {};
|
|
for (const d of documents.value) {
|
|
const t = d.tipo_documento || 'outro';
|
|
por[t] = (por[t] || 0) + 1;
|
|
}
|
|
const entries = Object.entries(por).sort((a, b) => b[1] - a[1]);
|
|
if (!entries.length) return null;
|
|
const [tipo, count] = entries[0];
|
|
return { tipo, count, label: DOC_TYPE_LABEL[tipo] || tipo };
|
|
});
|
|
|
|
const pendentes = computed(() => documents.value.filter((d) => d.status_revisao === 'pendente').length);
|
|
|
|
const sizeTotalFormatted = computed(() => fmtSize(totalBytes.value));
|
|
|
|
return {
|
|
documents,
|
|
loading,
|
|
error,
|
|
load,
|
|
total,
|
|
totalBytes,
|
|
tiposCount,
|
|
ultimo,
|
|
topType,
|
|
pendentes,
|
|
sizeTotalFormatted
|
|
};
|
|
}
|