diff --git a/frontend/src/pages/Asesores.jsx b/frontend/src/pages/Asesores.jsx index 5e739a3..2c2be4a 100644 --- a/frontend/src/pages/Asesores.jsx +++ b/frontend/src/pages/Asesores.jsx @@ -1,8 +1,12 @@ // src/pages/Asesores.jsx -import { useState, useEffect, useCallback } from "react"; +import { useState, useEffect, useCallback, Fragment } from "react"; import { api } from "../lib/api"; import { Loader, ErrorBox } from "../components/UI"; +const MES_LARGO = ["Enero","Febrero","Marzo","Abril","Mayo","Junio","Julio","Agosto","Septiembre","Octubre","Noviembre","Diciembre"]; +const MES_CORTO = ["Ene","Feb","Mar","Abr","May","Jun","Jul","Ago","Sep","Oct","Nov","Dic"]; +const calNavBtn = { width:26, height:26, borderRadius:7, border:"1px solid #e2e8f0", background:"#f8fafc", cursor:"pointer", fontSize:13, color:"#475569" }; + const ESTADO_INFO = { online: { txt: "En Línea", color: "#10b981", bg: "#d1fae5", fg: "#065f46" }, busy: { txt: "Ocupado", color: "#f59e0b", bg: "#fef9c3", fg: "#854d0e" }, @@ -17,11 +21,47 @@ export default function Asesores() { const [histAsesor, setHistAsesor] = useState(null); // { nombre, email } const [histFilas, setHistFilas] = useState([]); const [histLoading, setHistLoading] = useState(false); + const [semanaBase, setSemanaBase] = useState(null); // Date del lunes de la semana mostrada + const [calAbierto, setCalAbierto] = useState(false); // popover del mini-calendario + const [calVista, setCalVista] = useState("dias"); // "dias" | "meses" | "anios" + const [calAno, setCalAno] = useState(new Date().getFullYear()); + const [calMes, setCalMes] = useState(new Date().getMonth()); + const [histVista, setHistVista] = useState("calendario"); // "calendario" | "lista" + const [listaDia, setListaDia] = useState(""); // filtro día en vista lista (YYYY-MM-DD) + const [listaCalAbierto, setListaCalAbierto] = useState(false); + const [listaCalVista, setListaCalVista] = useState("dias"); + const [listaCalAno, setListaCalAno] = useState(new Date().getFullYear()); + const [listaCalMes, setListaCalMes] = useState(new Date().getMonth()); + + // Lunes de la semana que contiene la fecha dada. + const lunesDe = (d) => { + const x = new Date(d); + const dia = (x.getDay() + 6) % 7; // 0=lunes ... 6=domingo + x.setHours(0, 0, 0, 0); + x.setDate(x.getDate() - dia); + return x; + }; + + // Eventos ordenados de más antiguo a más reciente. + const histOrden = histFilas.slice() + .sort((a, b) => (a.fecha_hora || "").localeCompare(b.fecha_hora || "")); + + // Intervalos "estuvo conectado" [desde, hasta, enCurso] a partir de los ON/OFF. + const intervalosON = (() => { + const out = []; + let ini = null; + for (const h of histOrden) { + if (h.accion === "ON" && !ini) ini = new Date(h.fecha_hora); + else if (h.accion === "OFF" && ini) { out.push([ini, new Date(h.fecha_hora), false]); ini = null; } + } + if (ini) out.push([ini, new Date(), true]); // sigue conectado hasta ahora (en curso) + return out; + })(); async function verHistorial(ag) { const nombre = ag.name || ag.available_name || "—"; setHistAsesor({ nombre, email: ag.email }); - setHistFilas([]); setHistLoading(true); + setHistFilas([]); setSemanaBase(lunesDe(new Date())); setHistLoading(true); try { const r = await api.asesorHistorial(ag.email); setHistFilas(r.historial || []); @@ -117,6 +157,21 @@ export default function Asesores() {
{ag.email || "—"}
+ {/* Auto-offline (solo lectura) */} + {(() => { + const ao = ag.auto_offline === true || ag.auto_offline === "true"; + return ( + + + Auto-offline: {ao ? "ON" : "OFF"} + + ); + })()} {/* Historial */} +
+ {/* Toggle Calendario / Lista */} +
+ {[["calendario","📅 Calendario"],["lista","📋 Lista"]].map(([v,txt])=>( + + ))} +
+ +
-
+ {/* Navegación de semana (solo en vista Calendario) */} + {semanaBase && histVista==="calendario" && ( +
+ + + {(() => { + const fin = new Date(semanaBase); fin.setDate(fin.getDate()+6); + const f = (d)=>d.toLocaleDateString("es-PE",{day:"2-digit",month:"short"}); + return `${f(semanaBase)} – ${f(fin)}`; + })()} + + + {/* Calendario custom (botón) */} +
+ + {calAbierto && ( + <> +
setCalAbierto(false)} style={{ position:"fixed", inset:0, zIndex:1099 }} /> +
+ {/* Cabecera del calendario */} +
+ + {calVista==="dias" && ( +
+ + +
+ )} +
+ + {/* Vista DÍAS */} + {calVista==="dias" && (() => { + const primero = new Date(calAno, calMes, 1); + const offset = (primero.getDay()+6)%7; // lunes=0 + const diasMes = new Date(calAno, calMes+1, 0).getDate(); + const celdas = []; + for (let i=0;i +
+ {["L","M","M","J","V","S","D"].map((x,i)=>( +
{x}
+ ))} +
+
+ {celdas.map((d,i)=>{ + if(!d) return
; + const fecha = new Date(calAno, calMes, d); + const esHoy = fecha.toDateString()===hoy.toDateString(); + const enSemana = fecha>=semanaBase && fecha < (()=>{const f=new Date(semanaBase);f.setDate(f.getDate()+7);return f;})(); + return ( + + ); + })} +
+
+ ); + })()} + + {/* Vista MESES */} + {calVista==="meses" && ( +
+ {MES_CORTO.map((m,i)=>( + + ))} +
+ )} + + {/* Vista AÑOS */} + {calVista==="anios" && ( +
+ {Array.from({length:12},(_,i)=>calAno-5+i).map((y)=>( + + ))} +
+ )} +
+ + )} +
+
+ )} +
{histLoading ?
Cargando...
: - histFilas.length === 0 ?
Sin registros.
: -
- {histFilas.map((h, i) => { - const on = h.accion === "ON"; - return ( -
- - - {on ? "🟢 Se conectó" : "⚪ Se desconectó"} - - {fmtHora(h.fecha_hora)} - {h.origen} + histVista==="lista" ? (() => { + const diaLoc = (iso)=>{ if(!iso) return ""; const d=new Date(iso); const y=d.getFullYear(); const m=String(d.getMonth()+1).padStart(2,"0"); const dd=String(d.getDate()).padStart(2,"0"); return `${y}-${m}-${dd}`; }; + const filas = (listaDia ? histOrden.filter((h)=>diaLoc(h.fecha_hora)===listaDia) : histOrden).slice().reverse(); + return ( +
+ {/* Filtro por día (calendario custom) */} +
+ Filtrar por día: +
+ + {listaCalAbierto && ( + <> +
setListaCalAbierto(false)} style={{ position:"fixed", inset:0, zIndex:1099 }} /> +
+
+ + {listaCalVista==="dias" && ( +
+ + +
+ )} +
+ {listaCalVista==="dias" && (() => { + const primero = new Date(listaCalAno, listaCalMes, 1); + const offset = (primero.getDay()+6)%7; + const diasMes = new Date(listaCalAno, listaCalMes+1, 0).getDate(); + const celdas = []; for(let i=0;i +
+ {["L","M","M","J","V","S","D"].map((x,i)=>
{x}
)} +
+
+ {celdas.map((d,i)=>{ + if(!d) return
; + const fecha = new Date(listaCalAno, listaCalMes, d); + const val = `${listaCalAno}-${String(listaCalMes+1).padStart(2,"0")}-${String(d).padStart(2,"0")}`; + const esHoy = fecha.toDateString()===hoy.toDateString(); + const sel = listaDia===val; + return ( + + ); + })} +
+
+ ); + })()} + {listaCalVista==="meses" && ( +
+ {MES_CORTO.map((m,i)=>( + + ))} +
+ )} + {listaCalVista==="anios" && ( +
+ {Array.from({length:12},(_,i)=>listaCalAno-5+i).map((y)=>( + + ))} +
+ )} +
+ + )}
+ {listaDia && ( + + )} +
+ {filas.length===0 ?
Sin registros{listaDia?" para este día":""}.
: +
+ {filas.map((h,i)=>{ + const on = h.accion==="ON"; + return ( +
+ + + {on ? "🟢 Se conectó" : "⚪ Se desconectó"} + + {fmtHora(h.fecha_hora)} + {h.origen} +
+ ); + })} +
} +
+ ); + })() : + !semanaBase ? null : + (() => { + const H_INI = 7, H_FIN = 23; // franja mostrada 7am-23h + const horas = []; + for (let h=H_INI; h<=H_FIN; h++) horas.push(h); + const dias = []; + for (let i=0; i<7; i++){ const d=new Date(semanaBase); d.setDate(d.getDate()+i); dias.push(d); } + const NOM = ["Lun","Mar","Mié","Jue","Vie","Sáb","Dom"]; + const finSem = new Date(semanaBase); finSem.setDate(finSem.getDate()+7); + // ¿algún intervalo cae en esta semana? + const hayDatos = intervalosON.some(([a,b]) => b>=semanaBase && a { + const HORA_PX = 42; // alto de cada hora (px) + const totalH = (H_FIN - H_INI + 1) * HORA_PX; + const fmt = (x)=>x.toLocaleTimeString("es-PE",{hour:"2-digit",minute:"2-digit",hour12:true}); + return ( +
+ {!hayDatos && ( +
+ Sin conexiones registradas esta semana. +
+ )} + {/* Cabecera de días */} +
+
+ {dias.map((d,i)=>{ + const esHoy = d.toDateString()===new Date().toDateString(); + return ( +
+
{NOM[i]}
+
{d.getDate()}
+
+ ); + })} +
+ {/* Cuerpo: horas + columnas de días con bloques posicionados */} +
+ {/* Columna de horas */} +
+ {horas.map((h)=>( +
{h}:00
+ ))} +
+ {/* Una columna por día */} + {dias.map((d,di)=>{ + const colIni = new Date(d); colIni.setHours(H_INI,0,0,0); + // líneas de hora de fondo + return ( +
+ {horas.map((h,idx)=>( +
+ ))} + {/* Bloques ON continuos */} + {intervalosON.map(([a,b,enCurso],k)=>{ + const diaFin = new Date(d); diaFin.setHours(H_FIN+1,0,0,0); + const s = Math.max(a, colIni), e = Math.min(b, diaFin); + if (e<=s) return null; // no cae en este día/franja + const top = ((s - colIni) / (60*60*1000)) * HORA_PX; + const alto = Math.max(6, ((e - s) / (60*60*1000)) * HORA_PX); + const finTxt = enCurso ? "en curso" : fmt(b); + return ( +
+ {alto>=34 ? ( + <> + + + {fmt(a)} + + + {enCurso ? "en curso" : `hasta ${fmt(b)}`} + + + ) : alto>=18 ? ( + {fmt(a)} – {finTxt} + ) : null} +
+ ); + })} +
+ ); + })} +
+
); - })} -
} + })() + ); + })()}