import { api } from "../lib/api.js";
import { modal } from "../lib/modal.js";
class AliasesCrud extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open" });
this.items = [];
this.products = [];
this.selected = null;
this.loading = false;
this.searchQuery = "";
this.editMode = null; // 'create' | 'edit' | null
// Productos mapeados con scores
this.productMappings = [];
this.shadowRoot.innerHTML = `
`;
}
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 load() {
this.loading = true;
this.renderList();
try {
const data = await api.aliases({ q: this.searchQuery, limit: 500 });
this.items = data.items || [];
this.loading = false;
this.renderList();
} catch (e) {
console.error("Error loading aliases:", 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}`;
}
renderList() {
const list = this.shadowRoot.getElementById("list");
if (this.loading) {
list.innerHTML = `Cargando...
`;
return;
}
if (!this.items.length) {
list.innerHTML = `No se encontraron aliases
`;
return;
}
list.innerHTML = "";
for (const item of this.items) {
const el = document.createElement("div");
el.className = "item" + (this.selected?.alias === item.alias ? " active" : "");
// Mostrar productos mapeados
const mappings = item.product_mappings || [];
let productsHtml = "";
if (mappings.length > 0) {
const productNames = mappings.slice(0, 3).map(m => {
const name = this.getProductName(m.woo_product_id);
return `${name} (${m.score})`;
}).join(", ");
const more = mappings.length > 3 ? ` +${mappings.length - 3} más` : "";
productsHtml = `→ ${productNames}${more}
`;
} else if (item.woo_product_id) {
// Fallback al producto único legacy
const productName = this.getProductName(item.woo_product_id);
const boost = item.boost ? ` (boost: +${item.boost})` : "";
productsHtml = `→ ${productName}${boost}
`;
} else {
productsHtml = `→ Sin productos
`;
}
el.innerHTML = `
"${item.alias}" ${mappings.length || (item.woo_product_id ? 1 : 0)} productos
${productsHtml}
`;
el.onclick = () => {
this.selected = item;
this.editMode = "edit";
// Cargar mappings
this.productMappings = [...(item.product_mappings || [])];
// Si no hay mappings pero hay woo_product_id, crear uno por defecto
if (this.productMappings.length === 0 && item.woo_product_id) {
this.productMappings = [{ woo_product_id: item.woo_product_id, score: item.boost || 1.0 }];
}
this.renderList();
this.renderForm();
};
list.appendChild(el);
}
}
showCreateForm() {
this.selected = null;
this.editMode = "create";
this.productMappings = [];
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 un alias o crea uno nuevo
`;
return;
}
const isCreate = this.editMode === "create";
title.textContent = isCreate ? "Nuevo Alias" : "Editar Alias";
const alias = this.selected?.alias || "";
const categoryHint = this.selected?.category_hint || "";
form.innerHTML = `
| Producto |
Score (0-1) |
|
${this.renderMappingsRows()}
Mayor score = mayor prioridad. Si hay ambiguedad, se pregunta al usuario.
${!isCreate ? `` : ""}
`;
this.shadowRoot.getElementById("saveBtn").onclick = () => this.save();
this.shadowRoot.getElementById("cancelBtn").onclick = () => this.cancel();
if (!isCreate) {
this.shadowRoot.getElementById("deleteBtn").onclick = () => this.delete();
}
this.setupMappingsTable();
this.setupAddMappingSelector();
}
renderMappingsRows() {
if (!this.productMappings.length) {
return `| No hay productos mapeados |
`;
}
return this.productMappings.map((mapping, idx) => {
const name = this.getProductName(mapping.woo_product_id);
return `
| ${name} |
|
|
`;
}).join("");
}
setupMappingsTable() {
const tbody = this.shadowRoot.getElementById("mappingsTableBody");
if (!tbody) return;
// Handle score changes
tbody.querySelectorAll(".mapping-score").forEach((input, idx) => {
input.onchange = () => {
this.productMappings[idx].score = parseFloat(input.value) || 1.0;
};
});
// Handle remove buttons
tbody.querySelectorAll(".btn-remove").forEach(btn => {
btn.onclick = () => {
const idx = parseInt(btn.dataset.idx, 10);
this.productMappings.splice(idx, 1);
this.renderForm();
};
});
}
setupAddMappingSelector() {
const searchInput = this.shadowRoot.getElementById("mappingSearch");
const dropdown = this.shadowRoot.getElementById("mappingDropdown");
const addBtn = this.shadowRoot.getElementById("addMappingBtn");
if (!searchInput || !dropdown) return;
let selectedProductId = null;
const renderDropdown = (query) => {
const q = (query || "").toLowerCase().trim();
const existingIds = new Set(this.productMappings.map(m => m.woo_product_id));
let filtered = this.products.filter(p => !existingIds.has(p.woo_product_id));
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 = () => {
selectedProductId = parseInt(opt.dataset.id, 10);
searchInput.value = this.getProductName(selectedProductId);
dropdown.classList.remove("open");
};
});
dropdown.classList.add("open");
};
searchInput.oninput = () => {
selectedProductId = null;
clearTimeout(this._mappingTimer);
this._mappingTimer = setTimeout(() => renderDropdown(searchInput.value), 150);
};
searchInput.onfocus = () => renderDropdown(searchInput.value);
addBtn.onclick = () => {
if (!selectedProductId) {
modal.warn("Selecciona un producto primero");
return;
}
this.productMappings.push({
woo_product_id: selectedProductId,
score: 1.0,
});
searchInput.value = "";
selectedProductId = null;
this.renderForm();
};
// Close on outside click
document.addEventListener("click", (e) => {
if (!this.shadowRoot.getElementById("mappingSelector")?.contains(e.target)) {
dropdown.classList.remove("open");
}
});
}
async save() {
const aliasInput = this.shadowRoot.getElementById("aliasInput").value.trim().toLowerCase();
const categoryInput = this.shadowRoot.getElementById("categoryInput").value.trim();
if (!aliasInput) {
modal.warn("El alias es requerido");
return;
}
if (!this.productMappings.length) {
modal.warn("Agrega al menos un producto");
return;
}
// Usar el primer producto con mayor score como woo_product_id principal (legacy)
const sortedMappings = [...this.productMappings].sort((a, b) => (b.score || 0) - (a.score || 0));
const primaryProduct = sortedMappings[0];
const data = {
alias: aliasInput,
woo_product_id: primaryProduct.woo_product_id,
boost: primaryProduct.score || 1.0,
category_hint: categoryInput || null,
product_mappings: this.productMappings,
};
try {
if (this.editMode === "create") {
await api.createAlias(data);
} else {
await api.updateAlias(this.selected.alias, data);
}
this.editMode = null;
this.selected = null;
this.productMappings = [];
await this.load();
this.renderForm();
} catch (e) {
console.error("Error saving alias:", e);
modal.error("Error guardando: " + (e.message || e));
}
}
async delete() {
if (!this.selected?.alias) return;
const confirmed = await modal.confirm(`¿Eliminar el alias "${this.selected.alias}"?`);
if (!confirmed) return;
try {
await api.deleteAlias(this.selected.alias);
this.editMode = null;
this.selected = null;
this.productMappings = [];
await this.load();
this.renderForm();
} catch (e) {
console.error("Error deleting alias:", e);
modal.error("Error eliminando: " + (e.message || e));
}
}
cancel() {
this.editMode = null;
this.selected = null;
this.productMappings = [];
this.renderList();
this.renderForm();
}
}
customElements.define("aliases-crud", AliasesCrud);