import { api } from "../lib/api.js"; import { on } from "../lib/bus.js"; class TakeoversCrud extends HTMLElement { constructor() { super(); this.attachShadow({ mode: "open" }); this.items = []; this.selected = null; this.loading = false; this.products = []; this.pendingCount = 0; this.shadowRoot.innerHTML = `
Takeovers Pendientes
Cargando...
Responder
Selecciona un takeover para responder
`; } connectedCallback() { this.load(); this.loadProducts(); // Refresh cuando se recibe evento SSE de nuevo takeover this._unsubSse = on("sse:takeover", () => this.load()); } disconnectedCallback() { this._unsubSse?.(); } async load() { this.loading = true; this.renderList(); try { const data = await api.takeovers({ limit: 50 }); this.items = data.items || []; this.pendingCount = data.pending_count || this.items.length; this.loading = false; this.renderList(); } catch (e) { console.error("Error loading takeovers:", e); this.items = []; this.loading = false; this.renderList(); } } async loadProducts() { try { const data = await api.products({ limit: 2000 }); this.products = data.items || []; } catch (e) { console.error("Error loading products:", e); this.products = []; } } getProductName(id) { const p = this.products.find(x => x.woo_product_id === id); return p?.name || `Producto #${id}`; } formatTime(dateStr) { if (!dateStr) return ""; const d = new Date(dateStr); const now = new Date(); const diffMs = now - d; const diffMins = Math.floor(diffMs / 60000); if (diffMins < 1) return "Hace un momento"; if (diffMins < 60) return `Hace ${diffMins} min`; if (diffMins < 1440) return `Hace ${Math.floor(diffMins / 60)} hs`; return d.toLocaleDateString("es-AR", { day: "2-digit", month: "2-digit", hour: "2-digit", minute: "2-digit" }); } renderList() { const list = this.shadowRoot.getElementById("list"); const badge = this.shadowRoot.getElementById("pendingBadge"); if (this.pendingCount > 0) { badge.textContent = this.pendingCount; badge.style.display = "inline"; } else { badge.style.display = "none"; } if (this.loading) { list.innerHTML = `
Cargando...
`; return; } if (!this.items.length) { list.innerHTML = `
No hay takeovers pendientes
`; return; } list.innerHTML = ""; for (const item of this.items) { const el = document.createElement("div"); el.className = "item" + (this.selected?.id === item.id ? " active" : ""); el.innerHTML = `
"${item.pending_query}"
${this.getReasonLabel(item.reason)}
Chat: ${item.chat_id}
${this.formatTime(item.created_at)}
`; el.onclick = () => this.selectTakeover(item); list.appendChild(el); } } getReasonLabel(reason) { const labels = { product_not_found: "Producto no encontrado", low_confidence: "Baja confianza del NLU", ambiguous: "Consulta ambigua", }; return labels[reason] || reason; } async selectTakeover(item) { this.selected = item; this.renderList(); // Cargar detalles con historial try { const details = await api.getTakeover(item.id); this.selected = { ...item, ...details }; this.renderForm(); } catch (e) { console.error("Error loading takeover details:", e); this.renderForm(); } } renderForm() { const form = this.shadowRoot.getElementById("form"); if (!this.selected) { form.innerHTML = `
Selecciona un takeover para responder
`; return; } const history = this.selected.conversation_history || this.selected.recent_messages || []; form.innerHTML = `
"${this.selected.pending_query}"
${history.length > 0 ? `
${history.slice(-10).map(m => `
${m.role === "user" ? "Cliente" : "Bot"}
${this.escapeHtml((m.content || "").slice(0, 300))}
`).join("")}
` : ""}

Agregar Alias (opcional)

`; // Event listeners this.shadowRoot.getElementById("respondBtn").onclick = () => this.respond(); this.shadowRoot.getElementById("cancelBtn").onclick = () => this.cancel(); const addAliasCheck = this.shadowRoot.getElementById("addAliasCheck"); const aliasSection = this.shadowRoot.getElementById("aliasProductSection"); addAliasCheck.onchange = () => { aliasSection.style.display = addAliasCheck.checked ? "block" : "none"; }; this.setupProductSelector(); } escapeHtml(str) { return (str || "").replace(/&/g, "&").replace(//g, ">").replace(/"/g, """); } setupProductSelector() { const searchInput = this.shadowRoot.getElementById("productSearch"); const dropdown = this.shadowRoot.getElementById("productDropdown"); if (!searchInput || !dropdown) return; this._selectedProductId = null; const renderDropdown = (query) => { const q = (query || "").toLowerCase().trim(); let filtered = this.products; if (q) { filtered = filtered.filter(p => p.name.toLowerCase().includes(q)); } filtered = filtered.slice(0, 30); if (!filtered.length) { dropdown.classList.remove("open"); return; } dropdown.innerHTML = filtered.map(p => `
${p.name} $${p.price || 0}
`).join(""); dropdown.querySelectorAll(".product-option").forEach(opt => { opt.onclick = () => { this._selectedProductId = parseInt(opt.dataset.id, 10); searchInput.value = this.getProductName(this._selectedProductId); dropdown.classList.remove("open"); }; }); dropdown.classList.add("open"); }; searchInput.oninput = () => { this._selectedProductId = null; clearTimeout(this._searchTimer); this._searchTimer = setTimeout(() => renderDropdown(searchInput.value), 150); }; searchInput.onfocus = () => renderDropdown(searchInput.value); // Close on outside click document.addEventListener("click", (e) => { if (!this.shadowRoot.getElementById("productSelector")?.contains(e.target)) { dropdown.classList.remove("open"); } }); } async respond() { const response = this.shadowRoot.getElementById("responseInput").value.trim(); if (!response) { alert("Escribe una respuesta"); return; } const addAliasCheck = this.shadowRoot.getElementById("addAliasCheck"); let addAlias = null; if (addAliasCheck.checked && this._selectedProductId) { addAlias = { query: this.selected.pending_query, woo_product_id: this._selectedProductId, }; } try { await api.respondTakeover(this.selected.id, { response, add_alias: addAlias }); alert("Respuesta enviada"); this.selected = null; await this.load(); this.renderForm(); } catch (e) { console.error("Error responding:", e); alert("Error: " + (e.message || e)); } } async cancel() { if (!confirm("Cancelar este takeover? El cliente no recibira respuesta automatica.")) { return; } try { await api.cancelTakeover(this.selected.id); alert("Takeover cancelado"); this.selected = null; await this.load(); this.renderForm(); } catch (e) { console.error("Error cancelling:", e); alert("Error: " + (e.message || e)); } } } customElements.define("takeovers-crud", TakeoversCrud);