import { api } from "../lib/api.js"; class RecommendationsCrud extends HTMLElement { constructor() { super(); this.attachShadow({ mode: "open" }); this.items = []; this.selected = null; this.loading = false; this.searchQuery = ""; this.editMode = null; // 'create' | 'edit' | null // Cache de productos para el selector this.allProducts = []; this.productsLoaded = false; // Productos seleccionados en el formulario this.selectedTriggerProducts = []; this.selectedRecommendedProducts = []; this.shadowRoot.innerHTML = `
Reglas de Recomendacion
Cargando...
Detalle
Selecciona una regla o crea una nueva
`; } connectedCallback() { this.shadowRoot.getElementById("search").oninput = (e) => { this.searchQuery = e.target.value; clearTimeout(this._searchTimer); this._searchTimer = setTimeout(() => this.load(), 300); }; this.shadowRoot.getElementById("newBtn").onclick = () => this.showCreateForm(); this.load(); this.loadProducts(); } async loadProducts() { if (this.productsLoaded) return; try { const data = await api.products({ limit: 2000 }); this.allProducts = (data.items || []).filter(p => p.stock_status === "instock"); this.productsLoaded = true; } catch (e) { console.error("Error loading products:", e); this.allProducts = []; } } async load() { this.loading = true; this.renderList(); try { const data = await api.recommendations({ q: this.searchQuery, limit: 200 }); this.items = data.items || []; this.loading = false; this.renderList(); } catch (e) { console.error("Error loading recommendations:", e); this.items = []; this.loading = false; this.renderList(); } } getProductName(id) { const p = this.allProducts.find(x => x.woo_product_id === id); return p?.name || `Producto #${id}`; } renderList() { const list = this.shadowRoot.getElementById("list"); if (this.loading) { list.innerHTML = `
Cargando...
`; return; } if (!this.items.length) { list.innerHTML = `
No se encontraron reglas
`; return; } list.innerHTML = ""; for (const item of this.items) { const el = document.createElement("div"); el.className = "item" + (this.selected?.id === item.id ? " active" : ""); // Mostrar productos trigger const triggerIds = item.trigger_product_ids || []; const triggerNames = triggerIds.slice(0, 3).map(id => this.getProductName(id)).join(", "); const triggerMore = triggerIds.length > 3 ? ` (+${triggerIds.length - 3})` : ""; // Mostrar productos recomendados const recoIds = item.recommended_product_ids || []; const recoNames = recoIds.slice(0, 3).map(id => this.getProductName(id)).join(", "); const recoMore = recoIds.length > 3 ? ` (+${recoIds.length - 3})` : ""; el.innerHTML = `
${item.rule_key} ${item.active ? "Activa" : "Inactiva"} P: ${item.priority}
Cuando piden: ${triggerNames || "—"}${triggerMore}
→ Recomendar: ${recoNames || "—"}${recoMore}
`; el.onclick = () => { this.selected = item; this.editMode = "edit"; this.selectedTriggerProducts = [...(item.trigger_product_ids || [])]; this.selectedRecommendedProducts = [...(item.recommended_product_ids || [])]; this.renderList(); this.renderForm(); }; list.appendChild(el); } } showCreateForm() { this.selected = null; this.editMode = "create"; this.selectedTriggerProducts = []; this.selectedRecommendedProducts = []; this.renderList(); this.renderForm(); } renderForm() { const form = this.shadowRoot.getElementById("form"); const title = this.shadowRoot.getElementById("formTitle"); if (!this.editMode) { title.textContent = "Detalle"; form.innerHTML = `
Selecciona una regla o crea una nueva
`; return; } const isCreate = this.editMode === "create"; title.textContent = isCreate ? "Nueva Regla" : "Editar Regla"; const rule_key = this.selected?.rule_key || ""; const active = this.selected?.active !== false; const priority = this.selected?.priority || 100; form.innerHTML = `
Identificador unico, sin espacios
Productos que activan esta recomendacion
Productos a sugerir al cliente
${!isCreate ? `` : ""}
`; // Setup event handlers this.shadowRoot.getElementById("saveBtn").onclick = () => this.save(); this.shadowRoot.getElementById("cancelBtn").onclick = () => this.cancel(); if (!isCreate) { this.shadowRoot.getElementById("deleteBtn").onclick = () => this.delete(); } // Setup product selectors this.setupProductSelector("trigger", this.selectedTriggerProducts); this.setupProductSelector("reco", this.selectedRecommendedProducts); } setupProductSelector(type, selectedIds) { const searchInput = this.shadowRoot.getElementById(`${type}Search`); const dropdown = this.shadowRoot.getElementById(`${type}Dropdown`); const selectedContainer = this.shadowRoot.getElementById(`${type}Selected`); const renderSelected = () => { const ids = type === "trigger" ? this.selectedTriggerProducts : this.selectedRecommendedProducts; if (!ids.length) { selectedContainer.innerHTML = `Ningun producto seleccionado`; return; } selectedContainer.innerHTML = ids.map(id => { const name = this.getProductName(id); return `${name}×`; }).join(""); selectedContainer.querySelectorAll(".remove").forEach(btn => { btn.onclick = (e) => { e.stopPropagation(); const id = parseInt(btn.parentElement.dataset.id, 10); if (type === "trigger") { this.selectedTriggerProducts = this.selectedTriggerProducts.filter(x => x !== id); } else { this.selectedRecommendedProducts = this.selectedRecommendedProducts.filter(x => x !== id); } renderSelected(); renderDropdown(searchInput.value); }; }); }; const renderDropdown = (query) => { const q = (query || "").toLowerCase().trim(); const selectedSet = new Set(type === "trigger" ? this.selectedTriggerProducts : this.selectedRecommendedProducts); let filtered = this.allProducts; if (q) { filtered = filtered.filter(p => p.name.toLowerCase().includes(q)); } filtered = filtered.slice(0, 50); // Limit for performance if (!q && !filtered.length) { dropdown.classList.remove("open"); return; } dropdown.innerHTML = filtered.map(p => { const isSelected = selectedSet.has(p.woo_product_id); return `
${p.name} $${p.price || 0}
`; }).join(""); dropdown.querySelectorAll(".product-option").forEach(opt => { opt.onclick = () => { const id = parseInt(opt.dataset.id, 10); if (type === "trigger") { if (!this.selectedTriggerProducts.includes(id)) { this.selectedTriggerProducts.push(id); } } else { if (!this.selectedRecommendedProducts.includes(id)) { this.selectedRecommendedProducts.push(id); } } searchInput.value = ""; dropdown.classList.remove("open"); renderSelected(); }; }); dropdown.classList.add("open"); }; searchInput.oninput = () => { clearTimeout(this[`_${type}Timer`]); this[`_${type}Timer`] = setTimeout(() => renderDropdown(searchInput.value), 150); }; searchInput.onfocus = () => { if (searchInput.value || this.allProducts.length) { renderDropdown(searchInput.value); } }; // Close dropdown on outside click document.addEventListener("click", (e) => { if (!this.shadowRoot.getElementById(`${type}Selector`)?.contains(e.target)) { dropdown.classList.remove("open"); } }); renderSelected(); } async save() { const ruleKey = this.shadowRoot.getElementById("ruleKeyInput").value.trim().toLowerCase().replace(/\s+/g, "_"); const priority = parseInt(this.shadowRoot.getElementById("priorityInput").value, 10) || 100; const active = this.shadowRoot.getElementById("activeInput").checked; if (!ruleKey) { alert("El nombre de la regla es requerido"); return; } if (!this.selectedTriggerProducts.length) { alert("Selecciona al menos un producto trigger"); return; } if (!this.selectedRecommendedProducts.length) { alert("Selecciona al menos un producto para recomendar"); return; } const data = { rule_key: ruleKey, trigger: {}, // Legacy field, keep empty queries: [], // Legacy field, keep empty ask_slots: [], active, priority, trigger_product_ids: this.selectedTriggerProducts, recommended_product_ids: this.selectedRecommendedProducts, }; try { if (this.editMode === "create") { await api.createRecommendation(data); } else { await api.updateRecommendation(this.selected.id, data); } this.editMode = null; this.selected = null; await this.load(); this.renderForm(); } catch (e) { console.error("Error saving recommendation:", e); alert("Error guardando: " + (e.message || e)); } } async delete() { if (!this.selected?.id) return; if (!confirm(`¿Eliminar la regla "${this.selected.rule_key}"?`)) return; try { await api.deleteRecommendation(this.selected.id); this.editMode = null; this.selected = null; await this.load(); this.renderForm(); } catch (e) { console.error("Error deleting recommendation:", e); alert("Error eliminando: " + (e.message || e)); } } cancel() { this.editMode = null; this.selected = null; this.selectedTriggerProducts = []; this.selectedRecommendedProducts = []; this.renderList(); this.renderForm(); } } customElements.define("recommendations-crud", RecommendationsCrud);