104 lines
3.9 KiB
JavaScript
104 lines
3.9 KiB
JavaScript
/*
|
|
|--------------------------------------------------------------------------
|
|
| Agência PSI
|
|
|--------------------------------------------------------------------------
|
|
| Criado e desenvolvido por Leonardo Nohama
|
|
|
|
|
| Tecnologia aplicada à escuta.
|
|
| Estrutura para o cuidado.
|
|
|
|
|
| Arquivo: src/composables/Usedocshealth.js
|
|
| Data: 2026
|
|
| Local: São Carlos/SP — Brasil
|
|
|--------------------------------------------------------------------------
|
|
| © 2026 — Todos os direitos reservados
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
// Critério de "atenção": mais de 30% dos votos são negativos
|
|
// (mínimo 3 votos totais para evitar falso-positivo com 1 voto negativo).
|
|
//
|
|
// O `countAtencao` é exportado como singleton para uso no menu SaaS:
|
|
//
|
|
// import { countAtencao } from '@/composables/useDocsHealth'
|
|
// // No lugar onde saasMenu() é chamado:
|
|
// saasMenu(sessionCtx, { mismatchCount, docsAtencaoCount: countAtencao.value })
|
|
|
|
import { ref, computed } from 'vue';
|
|
|
|
const THRESHOLD_NEGATIVO = 0.3; // 30%
|
|
const MIN_VOTOS = 3; // mínimo de votos para considerar
|
|
|
|
// Estado singleton — alimentado pelo SaasDocsPage após load()
|
|
const _docs = ref([]);
|
|
|
|
// ── Classificação individual (exportada standalone p/ uso externo) ─
|
|
export function saudeDocItem(doc) {
|
|
const total = (doc.votos_util || 0) + (doc.votos_nao_util || 0);
|
|
if (total < MIN_VOTOS) return 'sem_dados';
|
|
const pctNeg = (doc.votos_nao_util || 0) / total;
|
|
return pctNeg > THRESHOLD_NEGATIVO ? 'atencao' : 'ok';
|
|
}
|
|
|
|
// ── Singleton reativo — use no menu ou em qualquer lugar ──────
|
|
export const countAtencao = computed(() => _docs.value.filter((d) => saudeDocItem(d) === 'atencao').length);
|
|
|
|
export function useDocsHealth() {
|
|
function setDocs(docs) {
|
|
_docs.value = docs;
|
|
}
|
|
|
|
function saudeDoc(doc) {
|
|
return saudeDocItem(doc);
|
|
}
|
|
|
|
function pctNegativo(doc) {
|
|
const total = (doc.votos_util || 0) + (doc.votos_nao_util || 0);
|
|
if (!total) return 0;
|
|
return Math.round(((doc.votos_nao_util || 0) / total) * 100);
|
|
}
|
|
|
|
// ── Métricas globais ───────────────────────────────────────
|
|
const totalDocs = computed(() => _docs.value.length);
|
|
const docsAtencao = computed(() => _docs.value.filter((d) => saudeDoc(d) === 'atencao'));
|
|
const docsOk = computed(() => _docs.value.filter((d) => saudeDoc(d) === 'ok'));
|
|
const docsSemDados = computed(() => _docs.value.filter((d) => saudeDoc(d) === 'sem_dados'));
|
|
|
|
// Doc mais útil (maior % positivo com mínimo de votos)
|
|
const docMaisUtil = computed(() => {
|
|
const comVotos = _docs.value.filter((d) => (d.votos_util || 0) + (d.votos_nao_util || 0) >= MIN_VOTOS);
|
|
if (!comVotos.length) return null;
|
|
return comVotos.reduce((best, d) => {
|
|
const pct = (d.votos_util || 0) / ((d.votos_util || 0) + (d.votos_nao_util || 0));
|
|
const bestPct = (best.votos_util || 0) / ((best.votos_util || 0) + (best.votos_nao_util || 0));
|
|
return pct > bestPct ? d : best;
|
|
});
|
|
});
|
|
|
|
// ── Ordenação por saúde ────────────────────────────────────
|
|
// Problemáticas primeiro → ok → sem dados
|
|
function sortBySaude(lista) {
|
|
const ordem = { atencao: 0, ok: 1, sem_dados: 2 };
|
|
return [...lista].sort((a, b) => {
|
|
const sa = saudeDoc(a);
|
|
const sb = saudeDoc(b);
|
|
if (ordem[sa] !== ordem[sb]) return ordem[sa] - ordem[sb];
|
|
return pctNegativo(b) - pctNegativo(a);
|
|
});
|
|
}
|
|
|
|
return {
|
|
setDocs,
|
|
saudeDoc,
|
|
pctNegativo,
|
|
totalDocs,
|
|
docsAtencao,
|
|
docsOk,
|
|
docsSemDados,
|
|
countAtencao,
|
|
docMaisUtil,
|
|
sortBySaude,
|
|
THRESHOLD_NEGATIVO,
|
|
MIN_VOTOS
|
|
};
|
|
}
|