Agenda, Agendador, Configurações
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
// src/composables/useDocsHealth.js
|
||||
// Singleton que computa métricas de saúde dos documentos.
|
||||
// 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.30 // 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,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user