/* global React, ChIcon, NavIcon, CHANNELS, TICKETS, StateBadge */ const { useMemo: useMemoQ, useState: useStateQ, useRef: useRefQ, useEffect: useEffectQ } = React; const initials = (name) => name.split(/\s+/).filter(Boolean).slice(0,2).map(w => w[0]).join("").toUpperCase(); const phaseFromState = (t) => { if (t.state === "resolved") return 5; if (t.state === "auto" && t.handler === "sofia") return 4; if (t.state === "progress") return 3; if (t.state === "waiting") return 2; if (t.state === "escalated" || t.urgent) return 2; return 3; }; const stateToTone = (t) => { if (t.urgent || t.state === "escalated") return "yellow"; if (t.state === "progress") return "lavender"; if (t.state === "waiting") return "pink"; if (t.state === "auto") return "lavender"; if (t.state === "resolved") return "white"; return "white"; }; const stateChipLabel = (t) => { if (t.urgent || t.state === "escalated") return { label: "Requiere operador", cls: "urgent" }; if (t.state === "auto") return { label: "Auto · SOF.IA", cls: "" }; if (t.state === "progress") return { label: "En curso", cls: "" }; if (t.state === "waiting") return { label: "Esperando cliente", cls: "" }; if (t.state === "resolved") return { label: "Resuelto", cls: "" }; return { label: t.state, cls: "" }; }; const TicketCard = ({ t, onOpen }) => { const ph = phaseFromState(t); const tone = stateToTone(t); const chip = stateChipLabel(t); const isSofia = t.handler === "sofia" && !(t.urgent || t.state === "escalated"); return (
onOpen(t)}>

{t.issue}

#{t.id} · {t.company}
{chip.label}
{[1,2,3,4,5].map(i => )}
{isSofia ? "S" : initials(t.customer)}
{isSofia ? "SOF.IA" : t.customer.split(" ").slice(0,2).join(" ")}
{t.age}{t.time ? ` · ${t.time}` : ""}
c.id === t.channel)?.label}>
); }; const PlaceholderCard = () => (
); // ============ List view ============ const TicketRow = ({ t, onOpen }) => { const chip = stateChipLabel(t); return (
onOpen(t)}>
#{t.id}
{t.customer} {t.company}
{t.printer} SN · {t.serial}
{chip.label}
{t.handler === "sofia" ? ( <>
S
SOF.IA ) : ( <>
OP
Dispatcher )}
{t.age}
); }; const STATE_FILTERS = [ { id: "all", label: "Todos" }, { id: "urgent", label: "Urgentes" }, { id: "progress", label: "En curso" }, { id: "auto", label: "Auto · SOF.IA" }, { id: "resolved", label: "Resueltos" }, ]; const SORT_OPTIONS = [ { id: "recent", label: "Más recientes" }, { id: "urgency", label: "Urgencia" }, { id: "channel", label: "Por canal" }, ]; const QueueScreen = ({ filterChannel, setFilterChannel, openTicket, setRoute }) => { const [viewMode, setViewMode] = useStateQ("cards"); const [search, setSearch] = useStateQ(""); const [sortBy, setSortBy] = useStateQ("recent"); const [sortOpen, setSortOpen] = useStateQ(false); const [stateFilter, setStateFilter] = useStateQ("all"); const sortRef = useRefQ(null); // Close sort dropdown on outside click useEffectQ(() => { if (!sortOpen) return; const handler = (e) => { if (sortRef.current && !sortRef.current.contains(e.target)) setSortOpen(false); }; document.addEventListener("mousedown", handler); return () => document.removeEventListener("mousedown", handler); }, [sortOpen]); const filtered = useMemoQ(() => { let r = TICKETS; if (filterChannel) r = r.filter(t => t.channel === filterChannel); if (stateFilter !== "all") { if (stateFilter === "urgent") r = r.filter(t => t.urgent || t.state === "escalated"); else if (stateFilter === "progress") r = r.filter(t => t.state === "progress" || t.state === "waiting"); else if (stateFilter === "auto") r = r.filter(t => t.state === "auto" && !t.urgent); else if (stateFilter === "resolved") r = r.filter(t => t.state === "resolved"); } if (search.trim()) { const q = search.trim().toLowerCase(); r = r.filter(t => (t.customer + " " + t.company + " " + t.id + " " + t.printer + " " + t.serial + " " + t.issue).toLowerCase().includes(q) ); } if (sortBy === "urgency") { const score = (t) => (t.urgent ? 3 : 0) + (t.state === "escalated" ? 2 : 0) + (t.state === "waiting" ? 1 : 0); r = [...r].sort((a, b) => score(b) - score(a)); } else if (sortBy === "channel") { r = [...r].sort((a, b) => a.channel.localeCompare(b.channel)); } return r; }, [filterChannel, search, sortBy, stateFilter]); const counts = useMemoQ(() => { const c = {}; CHANNELS.forEach(ch => { c[ch.id] = TICKETS.filter(t => t.channel === ch.id).length; }); return c; }, []); const stateCounts = useMemoQ(() => ({ all: TICKETS.length, urgent: TICKETS.filter(t => t.urgent || t.state === "escalated").length, progress: TICKETS.filter(t => t.state === "progress" || t.state === "waiting").length, auto: TICKETS.filter(t => t.state === "auto" && !t.urgent).length, resolved: TICKETS.filter(t => t.state === "resolved").length, }), []); const totalActive = TICKETS.filter(t => t.state !== "resolved").length; const sections = useMemoQ(() => { const urgent = filtered.filter(t => t.urgent || t.state === "escalated"); const progress = filtered.filter(t => !t.urgent && t.state !== "escalated" && (t.state === "progress" || t.state === "waiting")); const auto = filtered.filter(t => !t.urgent && t.state === "auto"); const resolved = filtered.filter(t => t.state === "resolved"); return { urgent, progress, auto, resolved }; }, [filtered]); const currentSort = SORT_OPTIONS.find(o => o.id === sortBy); return (

Cola Omnicanal · {filtered.length} de {TICKETS.length} tickets

Tickets Activos

Resueltos sin humano 80%
Captura promedio 42seg
Escalados hoy 23
{/* Tabs nav only */}
Activos {totalActive} setRoute && setRoute("pending")}> Pendientes 14 setRoute && setRoute("resolved")}> Resueltos hoy 47 setRoute && setRoute("lifecycle")}> Todos los tickets {TICKETS.length + 30}
{/* === TOOLBAR === */}
setSearch(e.target.value)} placeholder="Buscar por cliente, empresa, ticket, modelo, serie…" /> {search && ( )}
setViewMode("cards")} title="Vista tablero" > setViewMode("list")} title="Vista lista" >
{sortOpen && (
{SORT_OPTIONS.map(o => ( ))}
)}
{/* === FILTER ROW === */}
Estado
{STATE_FILTERS.map(s => ( setStateFilter(s.id)} > {s.label} {stateCounts[s.id]} ))}
Canal
setFilterChannel(null)} > Todos {TICKETS.length} {CHANNELS.map(ch => ( setFilterChannel(filterChannel === ch.id ? null : ch.id)} > {ch.label} {counts[ch.id]} ))}
{/* Empty state */} {filtered.length === 0 && (
— sin tickets para los filtros activos —
)} {/* === CARDS VIEW === */} {viewMode === "cards" && filtered.length > 0 && ( <> {sections.urgent.length > 0 && (
Requieren operador {sections.urgent.length} {sections.urgent.length === 1 ? "ticket" : "tickets"} · cliente esperando
{sections.urgent.map(t => )}
)} {sections.progress.length > 0 && (
En curso {sections.progress.length} {sections.progress.length === 1 ? "ticket" : "tickets"} · indagación o espera
{sections.progress.map(t => )}
)} {sections.auto.length > 0 && (
SOF.IA · resolviendo en automático {sections.auto.length} {sections.auto.length === 1 ? "ticket" : "tickets"} · sin intervención humana
{sections.auto.map(t => )}
)} {sections.resolved.length > 0 && (
Resueltos recientemente {sections.resolved.length} {sections.resolved.length === 1 ? "ticket" : "tickets"} · cerrados en las últimas horas
{sections.resolved.map(t => )}
)} )} {/* === LIST VIEW === */} {viewMode === "list" && filtered.length > 0 && (
Ticket Cliente Equipo Estado Manejado por Edad
{filtered.map(t => )}
)}
); }; window.QueueScreen = QueueScreen;