diff --git a/app/Http/Controllers/PostProxyController.php b/app/Http/Controllers/PostProxyController.php
index 8a87872..e666828 100644
--- a/app/Http/Controllers/PostProxyController.php
+++ b/app/Http/Controllers/PostProxyController.php
@@ -388,9 +388,18 @@ public function operationState($area): JsonResponse
$data = $response->json();
return response()->json($data);
}
- public function operationQueueState($area): JsonResponse
+ public function operationPromState($area): JsonResponse
{
- $response = Http::get("http://10.57.251.181:3014/operacion/{$area}");
+ $response = Http::get("http://10.57.251.181:3015/tiempo-respuesta/{$area}");
+ if (!$response->successful()) {
+ return response()->json(['error' => 'No se pudo obtener los datos'], 500);
+ }
+ $data = $response->json();
+ return response()->json($data);
+ }
+ public function operationAgentSatus($area): JsonResponse
+ {
+ $response = Http::get("http://10.57.251.181:3016/operacion/{$area}");
if (!$response->successful()) {
return response()->json(['error' => 'No se pudo obtener los datos'], 500);
}
diff --git a/resources/js/components/queuesAgentsModal.jsx b/resources/js/components/queuesAgentsModal.jsx
new file mode 100644
index 0000000..12f3b88
--- /dev/null
+++ b/resources/js/components/queuesAgentsModal.jsx
@@ -0,0 +1,53 @@
+// ModalColasAgent.jsx
+import React from 'react';
+import {
+ Glasses,
+} from 'lucide-react';
+function ModalColasAgent({ titulo = "Agentes", usuarios = [], tipo }) {
+ const estadoColorMap = {
+ disponibles: 'bg-green-100 dark:bg-green-900 text-green-700 dark:text-green-300',
+ ocupados: 'bg-red-100 dark:bg-red-900 text-red-700 dark:text-red-300',
+ en_pausa: 'bg-yellow-100 dark:bg-yellow-900 text-yellow-700 dark:text-yellow-300',
+ };
+
+ const badgeClass = `text-xs font-semibold px-3 py-1 rounded-full ${estadoColorMap[tipo] ?? ''}`;
+ return (
+
+
{loading || !allLoaded ? ( // ⛳ doble condición: hasta que TODOS estén listos
) : (
@@ -72,7 +74,7 @@ export default function AgentRankingWidget() {
{agente.agente}
- Directas: {agente.directas}, Transferidas: {agente.transferidas}
+ promedio: ,
Total en llamadas:
diff --git a/resources/js/components/welcome/callPerOperationChart.jsx b/resources/js/components/welcome/callPerOperationChart.jsx
index 0837cc0..c6eb26e 100644
--- a/resources/js/components/welcome/callPerOperationChart.jsx
+++ b/resources/js/components/welcome/callPerOperationChart.jsx
@@ -13,7 +13,7 @@ import { useEffect, useState } from 'react';
import DiscordLoader from '@/components/discordloader';
import axios from "axios";
import { useLoadStatus } from "../context/loadContext";
-import { themeByProject } from "../utils/theme";
+import { themeByProject, getChartColors } from '../utils/theme';
ChartJS.register(
CategoryScale,
LinearScale,
@@ -63,6 +63,8 @@ export default function CallsPerOperationChart() {
const theme = themeByProject[proyecto];
const [loading, setLoading] = useState(true);
const { allLoaded, markLoaded } = useLoadStatus();
+ const chartColors = getChartColors(proyecto);
+
const [callData, setCallData] = useState({
Soporte: 0,
Tramites: 0,
@@ -111,8 +113,8 @@ export default function CallsPerOperationChart() {
callData.Movil,
callData.Pruebas
],
- backgroundColor: 'rgba(168, 85, 247, 0.6)',
- borderColor: 'rgba(147, 51, 234, 1)',
+ backgroundColor: chartColors.fill,
+ borderColor: chartColors.border,
borderWidth: 1,
borderRadius: 10,
},
@@ -120,7 +122,7 @@ export default function CallsPerOperationChart() {
};
return (
-
+
{!allLoaded ? (
) : (
diff --git a/resources/js/hooks/useOperationModal.jsx b/resources/js/hooks/useOperationModal.jsx
new file mode 100644
index 0000000..fcb4e48
--- /dev/null
+++ b/resources/js/hooks/useOperationModal.jsx
@@ -0,0 +1,15 @@
+import { useState } from 'react';
+
+export default function useOperationModal() {
+ const [modal, setModal] = useState({ show: false, tipo: null });
+
+ const showModal = (tipo) => {
+ setModal({ show: true, tipo });
+ };
+
+ const hideModal = () => {
+ setModal({ show: false, tipo: null });
+ };
+
+ return { modal, showModal, hideModal };
+}
diff --git a/resources/js/pages/agentState.jsx b/resources/js/pages/agentState.jsx
index 9445270..aed7779 100644
--- a/resources/js/pages/agentState.jsx
+++ b/resources/js/pages/agentState.jsx
@@ -37,31 +37,33 @@ export default function TableAgents() {
-
-
Resumen del Día
-
- -
- 124 llamadas atendidas
- Excelente desempeño general.
-
- -
- 32 llamadas en espera
- Revisar horarios de mayor demanda.
-
- -
- 18 llamadas perdidas
- Oportunidad de mejora en atención.
-
-
-
- Última actualización: hace 5 minutos.
+
+
+
Estadísticas Curiosas
+
+
+ -
+ 🏆 luisa611 fue el agente más rápido
+ Promedio de duración: 2:31
+
+ -
+ 🧱 baguirre39 trabajó sin pausas por 3h 10m
+ Resistencia comprobada 💪
+
+ -
+ 🎯 sramoss1 recibió 12 transferencias
+ ¡Más apoyo recibido!
+
+
+
+
+ Última actualización: hace 5 minutos.
+
+
diff --git a/resources/js/pages/operationState.jsx b/resources/js/pages/operationState.jsx
index b9b2d10..7a3803f 100644
--- a/resources/js/pages/operationState.jsx
+++ b/resources/js/pages/operationState.jsx
@@ -8,6 +8,10 @@ import axios from 'axios';
import { useLoadStatus } from '@/components/context/loadContext';
import { themeByProject } from '@/components/utils/theme';
import { usePage } from "@inertiajs/react";
+import TiempoFormateado from '@/components/utils/formatTime';
+import AgentModalWrapper from '@/components/agentsModalWrapper';
+import useOperationModal from '@/hooks/useOperationModal';
+import ModalColasAgent from '@/components/queuesAgentsModal';
import {
Hourglass,
@@ -28,9 +32,12 @@ export default function CallsWaitingByOperation() {
const [operation, setOperation] = useState(null);
const [pollingInterval, setPollingInterval] = useState(null);
const [stats, setStats] = useState([]);
+ const [promedio, setPromedio] = useState([]);
+ const [estadoAgentes, setEstadoAgentes] = useState([]);
const { props } = usePage();
const proyecto = props?.auth?.user?.proyecto || 'AZZU';
const theme = themeByProject[proyecto];
+ const { modal, showModal, hideModal } = useOperationModal();
@@ -46,29 +53,42 @@ export default function CallsWaitingByOperation() {
.catch(() => console.error('Error fetching stats'));
}
- const fetchStastQueueOperation = (operation) => {
+ const fetchStastPromOperation = (operation) => {
if (!operation) return;
- fetch(`/api/operationQueueState/${operation}`)
+ fetch(`/api/operationPromState/${operation}`)
.then(res => res.json())
.then(data => {
- console.log('Datos crudos de la API:', data);
- setStats(data);
+ // console.log('Datos crudos del promedio:', data);
+ setPromedio(data);
})
.catch(() => console.error('Error fetching stats'));
+ }
-
+ const fetchStatusAgentsOperation = (operation) => {
+ if (!operation) return;
+
+ fetch(`/api/operationStatusAgentOperation/${operation}`)
+ .then(res => res.json())
+ .then(data => {
+ // console.log('Datos crudos del estado del agente:', data);
+ setEstadoAgentes(data);
+ })
+ .catch(() => console.error('Error fetching stats'));
}
+
const startPolling = (operation) => {
// Limpia cualquier intervalo existente antes de iniciar uno nuevo
if (pollingInterval) {
clearInterval(pollingInterval);
}
- fetchStastOperation(operation); // Realiza la primera solicitud inmediatamente
+ fetchStastOperation(operation);
+ (operation) // Realiza la primera solicitud inmediatamente
const intervalId = setInterval(() => {
fetchStastOperation(operation);
+ fetchStatusAgentsOperation(operation);
}, 8000);
@@ -90,7 +110,7 @@ export default function CallsWaitingByOperation() {
.then(res => setUserOps(res.data.operations))
.catch(err => console.error('Error cargando operaciones', err));
}, []);
- console.log('Operaciones asignadas:',userOps);
+ // console.log('Operaciones asignadas:',userOps);
const customTheme = {
root: {
@@ -115,17 +135,17 @@ export default function CallsWaitingByOperation() {
-
+
-
+
{userOps.map((op) => (
- { startPolling(op); setOperation(op); }}>
+ { startPolling(op); setOperation(op); fetchStastPromOperation(op) }}>
{op}
))}
@@ -156,19 +176,40 @@ export default function CallsWaitingByOperation() {
- {stats.detalle_colas && stats.detalle_colas.map((cola) => (
-
-
-
Cola {cola.cola}
- {cola.llamadas} llamadas
-
-
-
Total: {cola.agentes_totales}
-
Ocupados: {cola.agentes_ocupados}
-
Disponibles: {cola.agentes_disponibles}
-
-
- ))}
+ {stats.detalle_colas &&
+ [...stats.detalle_colas]
+ .sort((a, b) => b.llamadas - a.llamadas) // ← ordena de mayor a menor
+ .map((cola) => {
+ let containerClass =
+ "p-3 rounded-lg border mb-4 transition-colors ";
+
+ if (cola.llamadas > 5) {
+ containerClass += "border-red-500 dark:border-red-600 bg-red-50 dark:bg-red-900/10 text-red-800 dark:text-red-300";
+ } else if (cola.llamadas >= 1) {
+ containerClass += "border-blue-500 dark:border-blue-600 bg-blue-50 dark:bg-blue-900/10 text-blue-800 dark:text-blue-300";
+ } else {
+ containerClass += "border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-900 text-gray-800 dark:text-white";
+ }
+
+ return (
+
+
+
+
+ Cola {cola.cola}
+
+
+ {cola.llamadas} llamadas
+
+
+
+
Total: {cola.agentes_totales}
+
Ocupados: {cola.agentes_ocupados}
+
Disponibles: {cola.agentes_disponibles}
+
+
+ );
+ })}
@@ -192,7 +233,7 @@ export default function CallsWaitingByOperation() {
{!operation ? (
-
+
Análisis detallado por operación
Aún no has seleccionado una operación.
@@ -204,13 +245,14 @@ export default function CallsWaitingByOperation() {
{/* Columna 1: Tiempo promedio de espera */}
-
01:38
+
Promedio actual en la operación {operation}
- Máximo histórico hoy: 04:15
- Objetivo ideal: ≤ 02:00
+ Máximo histórico hoy:
+ Tiempo maximo de respuesta hoy:
+ Objetivo ideal: ≤ 01:00
@@ -218,26 +260,39 @@ export default function CallsWaitingByOperation() {
{/* Columna 2: Agentes conectados */}
-
9 agentes conectados
-
- - 🟢 4 disponibles
- - 🔴 3 ocupados
- - 🟡 2 en pausa
-
-
-
Top 3 agentes activos
-
- - Karol.Ospina — 18 llamadas
- - David.Mendez — 14 llamadas
- - Andrea.Sierra — 13 llamadas
-
-
-
+
9 agentes conectados
+
+ - showModal('disponibles')}>
+ 🟢 {estadoAgentes?.total?.disponibles ?? 0} disponibles
+
+ - showModal('ocupados')}>
+ 🔴 {estadoAgentes?.total?.ocupados ?? 0} ocupados
+
+ - showModal('en_pausa')}>
+ 🟡 {estadoAgentes?.total?.en_pausa ?? 0} en pausa
+
+
+
+
Has click en el status que desees para obtener mas informacion
+
+
+
{/*
*/}
)}
+ {modal.show && (
+
+
+
+ )}
+
diff --git a/routes/api.php b/routes/api.php
index 1579b6c..590092e 100644
--- a/routes/api.php
+++ b/routes/api.php
@@ -19,6 +19,7 @@
Route::post('/transfer-call', [PostProxyController::class, 'channelTransfer']);
Route::get('/getCallsPerOperation', [PostProxyController::class, 'getCallsPerOperation']);
Route::get('/operationState/{area}', [PostProxyController::class, 'operationState']);
-Route::get('/operationQueueState/{area}', [PostProxyController::class, 'operationQueueState']);
+Route::get('/operationPromState/{area}', [PostProxyController::class, 'operationPromState']);
+Route::get('/operationStatusAgentOperation/{area}', [PostProxyController::class, 'operationAgentSatus']);
// routes/api.php
Route::post('/spy', [SpyController::class, 'start']);