|
@@ -1,5 +1,5 @@
|
|
|
<script setup>
|
|
<script setup>
|
|
|
-import { computed, onMounted, ref } from "vue";
|
|
|
|
|
|
|
+import { computed, onBeforeUnmount, onMounted, ref } from "vue";
|
|
|
import LayoutSistema from "../../layout/LayoutSistema.vue";
|
|
import LayoutSistema from "../../layout/LayoutSistema.vue";
|
|
|
import BaseBadge from "../../components/base/BaseBadge.vue";
|
|
import BaseBadge from "../../components/base/BaseBadge.vue";
|
|
|
import {
|
|
import {
|
|
@@ -25,6 +25,13 @@ const filtroSentimento = ref("");
|
|
|
|
|
|
|
|
const totalPaginas = computed(() => Math.max(1, Math.ceil(total.value / pageSize)));
|
|
const totalPaginas = computed(() => Math.max(1, Math.ceil(total.value / pageSize)));
|
|
|
|
|
|
|
|
|
|
+const progresso = computed(() => {
|
|
|
|
|
+ const totalAt = stats.value?.totalAtendimentos ?? 0;
|
|
|
|
|
+ if (!totalAt) return null;
|
|
|
|
|
+ const feitos = Math.max(0, totalAt - (stats.value?.pendentes ?? 0));
|
|
|
|
|
+ return { total: totalAt, feitos, pct: Math.round((feitos / totalAt) * 100) };
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
function scoreVariant(score) {
|
|
function scoreVariant(score) {
|
|
|
if (score === null || score === undefined) return "default";
|
|
if (score === null || score === undefined) return "default";
|
|
|
if (score >= 8) return "success";
|
|
if (score >= 8) return "success";
|
|
@@ -109,9 +116,19 @@ function alternarExpandido(id) {
|
|
|
expandido.value = expandido.value === id ? null : id;
|
|
expandido.value = expandido.value === id ? null : id;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// enquanto houver pendentes, atualiza as estatísticas sozinho para a barra andar
|
|
|
|
|
+let statsTimer = null;
|
|
|
|
|
+
|
|
|
onMounted(() => {
|
|
onMounted(() => {
|
|
|
carregarStats();
|
|
carregarStats();
|
|
|
carregarLista();
|
|
carregarLista();
|
|
|
|
|
+ statsTimer = setInterval(() => {
|
|
|
|
|
+ if (stats.value?.pendentes > 0 && !avaliandoLote.value) carregarStats();
|
|
|
|
|
+ }, 30_000);
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
|
|
+onBeforeUnmount(() => {
|
|
|
|
|
+ if (statsTimer) clearInterval(statsTimer);
|
|
|
});
|
|
});
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|
|
@@ -150,6 +167,25 @@ onMounted(() => {
|
|
|
</button>
|
|
</button>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
|
|
+ <div v-if="progresso" class="mt-5">
|
|
|
|
|
+ <div class="flex items-center justify-between text-xs text-gray-500 dark:text-gray-400">
|
|
|
|
|
+ <span>
|
|
|
|
|
+ Progresso das avaliações
|
|
|
|
|
+ <span v-if="stats.pendentes > 0" class="text-gray-400 dark:text-gray-500"> — atualiza sozinho a cada 30s</span>
|
|
|
|
|
+ </span>
|
|
|
|
|
+ <span class="font-medium text-gray-700 dark:text-gray-300">
|
|
|
|
|
+ {{ progresso.feitos }} de {{ progresso.total }} ({{ progresso.pct }}%)
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="mt-1.5 h-2 overflow-hidden rounded-full bg-gray-200 dark:bg-gray-700">
|
|
|
|
|
+ <div
|
|
|
|
|
+ class="h-full rounded-full bg-primary transition-all duration-700"
|
|
|
|
|
+ :class="{ 'animate-pulse': stats.pendentes > 0 }"
|
|
|
|
|
+ :style="{ width: `${progresso.pct}%` }"
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
<div v-if="avaliandoLote" class="mt-4 rounded-lg bg-blue-50 px-4 py-3 text-xs text-blue-800 dark:bg-blue-900/30 dark:text-blue-300">
|
|
<div v-if="avaliandoLote" class="mt-4 rounded-lg bg-blue-50 px-4 py-3 text-xs text-blue-800 dark:bg-blue-900/30 dark:text-blue-300">
|
|
|
Avaliando um lote de atendimentos com a IA — isso pode levar alguns minutos...
|
|
Avaliando um lote de atendimentos com a IA — isso pode levar alguns minutos...
|
|
|
</div>
|
|
</div>
|