import { api } from "../lib/api.js"; import { on } from "../lib/bus.js"; import { modal } from "../lib/modal.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.cartItemsToAdd = []; // Items que el humano quiere agregar al carrito 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 al carrito del cliente

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"; }; // Limpiar items al cambiar de takeover this.cartItemsToAdd = []; this.renderCartItemsList(); this.setupProductSelector(); this.setupCartProductSelector(); } renderCartItemsList() { const container = this.shadowRoot.getElementById("cartItemsList"); if (!container) return; if (this.cartItemsToAdd.length === 0) { container.innerHTML = `
Sin items agregados
`; return; } container.innerHTML = this.cartItemsToAdd.map((item, idx) => `
${item.name}
`).join(""); // Event listeners para editar/eliminar container.querySelectorAll(".qty").forEach(input => { input.onchange = (e) => { const idx = parseInt(e.target.dataset.idx, 10); this.cartItemsToAdd[idx].qty = parseFloat(e.target.value) || 1; }; }); container.querySelectorAll(".unit-select").forEach(select => { select.onchange = (e) => { const idx = parseInt(e.target.dataset.idx, 10); this.cartItemsToAdd[idx].unit = e.target.value; }; }); container.querySelectorAll(".remove-btn").forEach(btn => { btn.onclick = (e) => { const idx = parseInt(e.target.dataset.idx, 10); this.cartItemsToAdd.splice(idx, 1); this.renderCartItemsList(); }; }); } setupCartProductSelector() { const searchInput = this.shadowRoot.getElementById("cartProductSearch"); const dropdown = this.shadowRoot.getElementById("cartProductDropdown"); const addBtn = this.shadowRoot.getElementById("addCartItemBtn"); const qtyInput = this.shadowRoot.getElementById("cartQtyInput"); const unitSelect = this.shadowRoot.getElementById("cartUnitSelect"); if (!searchInput || !dropdown || !addBtn) return; let selectedProduct = 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 = () => { selectedProduct = { woo_id: parseInt(opt.dataset.id, 10), name: opt.dataset.name, }; searchInput.value = selectedProduct.name; // Auto-seleccionar unidad según producto const productUnit = opt.dataset.unit || "kg"; if (unitSelect) { unitSelect.value = productUnit === "unit" || productUnit === "Unidad" ? "unit" : "kg"; } dropdown.classList.remove("open"); }; }); dropdown.classList.add("open"); }; searchInput.oninput = () => { selectedProduct = null; clearTimeout(this._cartSearchTimer); this._cartSearchTimer = setTimeout(() => renderDropdown(searchInput.value), 150); }; searchInput.onfocus = () => renderDropdown(searchInput.value); addBtn.onclick = () => { if (!selectedProduct) { modal.warn("Selecciona un producto primero"); return; } const qty = parseFloat(qtyInput.value) || 1; const unit = unitSelect.value || "kg"; this.cartItemsToAdd.push({ woo_id: selectedProduct.woo_id, name: selectedProduct.name, qty, unit, }); // Limpiar inputs searchInput.value = ""; qtyInput.value = "1"; selectedProduct = null; this.renderCartItemsList(); }; // Close dropdown on outside click document.addEventListener("click", (e) => { if (!this.shadowRoot.getElementById("cartProductSelector")?.contains(e.target)) { dropdown.classList.remove("open"); } }); } 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) { modal.warn("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, }; } // Items a agregar al carrito del cliente const cartItems = this.cartItemsToAdd.length > 0 ? this.cartItemsToAdd : null; try { await api.respondTakeover(this.selected.id, { response, add_alias: addAlias, cart_items: cartItems, }); const itemsMsg = cartItems ? ` (${cartItems.length} item(s) agregados al carrito)` : ""; modal.success("Respuesta enviada" + itemsMsg); this.selected = null; this.cartItemsToAdd = []; await this.load(); this.renderForm(); } catch (e) { console.error("Error responding:", e); modal.error("Error: " + (e.message || e)); } } async cancel() { const confirmed = await modal.confirm("¿Cancelar este takeover? El cliente no recibirá respuesta automática."); if (!confirmed) return; try { await api.cancelTakeover(this.selected.id); modal.success("Takeover cancelado"); this.selected = null; await this.load(); this.renderForm(); } catch (e) { console.error("Error cancelling:", e); modal.error("Error: " + (e.message || e)); } } } customElements.define("takeovers-crud", TakeoversCrud);