modificando el patron del sistema, orientado mas al usuario

This commit is contained in:
Lucas Tettamanti
2026-01-25 22:32:58 -03:00
parent 93e331535f
commit bd63d92c50
15 changed files with 707 additions and 83 deletions

View File

@@ -1,4 +1,5 @@
import { api } from "../lib/api.js";
import { modal } from "../lib/modal.js";
class AliasesCrud extends HTMLElement {
constructor() {
@@ -380,7 +381,7 @@ class AliasesCrud extends HTMLElement {
addBtn.onclick = () => {
if (!selectedProductId) {
alert("Selecciona un producto primero");
modal.warn("Selecciona un producto primero");
return;
}
@@ -407,12 +408,12 @@ class AliasesCrud extends HTMLElement {
const categoryInput = this.shadowRoot.getElementById("categoryInput").value.trim();
if (!aliasInput) {
alert("El alias es requerido");
modal.warn("El alias es requerido");
return;
}
if (!this.productMappings.length) {
alert("Agrega al menos un producto");
modal.warn("Agrega al menos un producto");
return;
}
@@ -441,13 +442,14 @@ class AliasesCrud extends HTMLElement {
this.renderForm();
} catch (e) {
console.error("Error saving alias:", e);
alert("Error guardando: " + (e.message || e));
modal.error("Error guardando: " + (e.message || e));
}
}
async delete() {
if (!this.selected?.alias) return;
if (!confirm(`¿Eliminar el alias "${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);
@@ -458,7 +460,7 @@ class AliasesCrud extends HTMLElement {
this.renderForm();
} catch (e) {
console.error("Error deleting alias:", e);
alert("Error eliminando: " + (e.message || e));
modal.error("Error eliminando: " + (e.message || e));
}
}

View File

@@ -1,5 +1,6 @@
import { api } from "../lib/api.js";
import { emit, on } from "../lib/bus.js";
import { modal } from "../lib/modal.js";
class ChatSimulator extends HTMLElement {
constructor() {
@@ -129,7 +130,7 @@ class ChatSimulator extends HTMLElement {
const pushName = evoPushEl.value.trim();
if (!from || !text) {
alert("Falta from o text");
modal.warn("Falta from o text");
return;
}

View File

@@ -1,5 +1,6 @@
import { api } from "../lib/api.js";
import { emit, on } from "../lib/bus.js";
import { modal } from "../lib/modal.js";
class ConversationList extends HTMLElement {
constructor() {
@@ -90,7 +91,8 @@ class ConversationList extends HTMLElement {
this.shadowRoot.getElementById("uaDeleteConv").onclick = async () => {
if (!this.selectedUser) return;
const chat_id = this.selectedUser.chat_id;
if (!confirm(`¿Borrar conversación completa de ${chat_id}?`)) return;
const confirmed = await modal.confirm(`¿Borrar conversación completa de ${chat_id}?`);
if (!confirmed) return;
await api.deleteConversation(chat_id);
this.selectedUser = null;
await this.refreshUsers();
@@ -98,7 +100,8 @@ class ConversationList extends HTMLElement {
this.shadowRoot.getElementById("uaDeleteUser").onclick = async () => {
if (!this.selectedUser) return;
const chat_id = this.selectedUser.chat_id;
if (!confirm(`¿Borrar usuario ${chat_id}, sus conversaciones y el customer en Woo?`)) return;
const confirmed = await modal.confirm(`¿Borrar usuario ${chat_id}, sus conversaciones y el customer en Woo?`);
if (!confirmed) return;
await api.deleteUser(chat_id, { deleteWoo: true });
this.selectedUser = null;
await this.refreshUsers();
@@ -229,7 +232,8 @@ class ConversationList extends HTMLElement {
el.onclick = async (e) => {
if (e?.target?.dataset?.del) {
e.stopPropagation();
if (!confirm(`¿Borrar conversación completa de ${c.chat_id}?`)) return;
const confirmed = await modal.confirm(`¿Borrar conversación completa de ${c.chat_id}?`);
if (!confirmed) return;
await api.deleteConversation(c.chat_id);
if (this.selected === c.chat_id) this.selected = null;
await this.refresh();

View File

@@ -1,5 +1,6 @@
import { api } from "../lib/api.js";
import { emit, on } from "../lib/bus.js";
import { modal } from "../lib/modal.js";
class ConversationsCrud extends HTMLElement {
constructor() {
@@ -244,21 +245,22 @@ class ConversationsCrud extends HTMLElement {
this.shadowRoot.getElementById("retryLast").onclick = async () => {
try {
await api.retryLast(c.chat_id);
alert("Retry ejecutado");
modal.success("Retry ejecutado");
} catch (e) {
alert("Error: " + (e.message || e));
modal.error("Error: " + (e.message || e));
}
};
this.shadowRoot.getElementById("deleteConv").onclick = async () => {
if (!confirm(`¿Eliminar la conversacion de "${c.chat_id}"?`)) return;
const confirmed = await modal.confirm(`¿Eliminar la conversación de "${c.chat_id}"?`);
if (!confirmed) return;
try {
await api.deleteConversation(c.chat_id);
this.selected = null;
await this.load();
this.renderDetail();
} catch (e) {
alert("Error: " + (e.message || e));
modal.error("Error: " + (e.message || e));
}
};
}

View File

@@ -1,6 +1,7 @@
import { api } from "../lib/api.js";
import { on } from "../lib/bus.js";
import { navigateToItem } from "../lib/router.js";
import { modal } from "../lib/modal.js";
class ProductsCrud extends HTMLElement {
constructor() {
@@ -166,13 +167,10 @@ class ProductsCrud extends HTMLElement {
async syncFromWoo() {
// Mostrar confirmación antes de sincronizar
const confirmed = confirm(
const confirmed = await modal.confirm(
"⚠️ Resincronización de emergencia\n\n" +
"Esto reimportará TODOS los productos desde WooCommerce y sobrescribirá los datos locales.\n\n" +
"Usar solo si:\n" +
"• La plataforma estuvo caída mientras se hacían cambios en Woo\n" +
"• Los webhooks no funcionaron correctamente\n" +
"• Necesitás una sincronización completa\n\n" +
"Usar solo si la plataforma estuvo caída, los webhooks no funcionaron, o necesitás una sincronización completa.\n\n" +
"¿Continuar?"
);
@@ -185,14 +183,14 @@ class ProductsCrud extends HTMLElement {
try {
const result = await api.syncFromWoo();
if (result.ok) {
alert(`Sincronización completada: ${result.synced} productos importados`);
modal.success(`Sincronización completada: ${result.synced} productos importados`);
} else {
alert("Error: " + (result.error || "Error desconocido"));
modal.error("Error: " + (result.error || "Error desconocido"));
}
await this.load();
} catch (e) {
console.error("Error syncing products:", e);
alert("Error sincronizando: " + (e.message || e));
modal.error("Error sincronizando: " + (e.message || e));
} finally {
btn.disabled = false;
btn.textContent = "Resincronizar";
@@ -636,7 +634,7 @@ class ProductsCrud extends HTMLElement {
setTimeout(() => { btn.textContent = "Guardar cambios"; btn.disabled = false; }, 1500);
} catch (e) {
console.error("Error saving product:", e);
alert("Error guardando: " + (e.message || e));
modal.error("Error guardando: " + (e.message || e));
btn.textContent = "Guardar cambios";
btn.disabled = false;
}
@@ -710,7 +708,7 @@ class ProductsCrud extends HTMLElement {
const categoryName = select.value;
if (!categoryName) {
alert("Seleccioná una categoría");
modal.warn("Seleccioná una categoría");
return;
}
@@ -753,7 +751,7 @@ class ProductsCrud extends HTMLElement {
setTimeout(() => { btn.textContent = "Agregar"; btn.disabled = false; }, 1500);
} catch (e) {
console.error("Error adding category:", e);
alert("Error agregando categoría: " + (e.message || e));
modal.error("Error agregando categoría: " + (e.message || e));
btn.textContent = "Agregar";
btn.disabled = false;
}
@@ -804,7 +802,7 @@ class ProductsCrud extends HTMLElement {
}, 1500);
} catch (e) {
console.error("Error saving product unit:", e);
alert("Error guardando: " + (e.message || e));
modal.error("Error guardando: " + (e.message || e));
btn.textContent = count > 1 ? `Guardar para ${count}` : "Guardar";
btn.disabled = false;
}

View File

@@ -1,5 +1,6 @@
import { api } from "../lib/api.js";
import { on } from "../lib/bus.js";
import { modal } from "../lib/modal.js";
const PROMPT_LABELS = {
router: "Router (clasificador de dominio)",
@@ -376,61 +377,59 @@ class PromptsCrud extends HTMLElement {
const model = this.shadowRoot.getElementById("modelSelect").value;
if (!content.trim()) {
alert("El contenido no puede estar vacio");
modal.warn("El contenido no puede estar vacío");
return;
}
try {
await api.savePrompt(this.selected.prompt_key, { content, model });
alert("Prompt guardado correctamente");
modal.success("Prompt guardado correctamente");
await this.load();
// Re-seleccionar el prompt actual
const updated = this.items.find(i => i.prompt_key === this.selected.prompt_key);
if (updated) this.selectPrompt(updated);
} catch (e) {
console.error("Error saving prompt:", e);
alert("Error guardando: " + (e.message || e));
modal.error("Error guardando: " + (e.message || e));
}
}
async reset() {
if (!confirm("Esto desactivara todas las versiones custom y volvera al prompt por defecto. Continuar?")) {
return;
}
const confirmed = await modal.confirm("Esto desactivará todas las versiones custom y volverá al prompt por defecto. ¿Continuar?");
if (!confirmed) return;
try {
await api.resetPrompt(this.selected.prompt_key);
alert("Prompt reseteado a default");
modal.success("Prompt reseteado a default");
await this.load();
const updated = this.items.find(i => i.prompt_key === this.selected.prompt_key);
if (updated) this.selectPrompt(updated);
} catch (e) {
console.error("Error resetting prompt:", e);
alert("Error: " + (e.message || e));
modal.error("Error: " + (e.message || e));
}
}
async rollback(version) {
if (!confirm(`Restaurar version ${version}? Se creara una nueva version con ese contenido.`)) {
return;
}
const confirmed = await modal.confirm(`¿Restaurar versión ${version}? Se creará una nueva versión con ese contenido.`);
if (!confirmed) return;
try {
await api.rollbackPrompt(this.selected.prompt_key, version);
alert("Version restaurada");
modal.success("Versión restaurada");
await this.load();
const updated = this.items.find(i => i.prompt_key === this.selected.prompt_key);
if (updated) this.selectPrompt(updated);
} catch (e) {
console.error("Error rolling back:", e);
alert("Error: " + (e.message || e));
modal.error("Error: " + (e.message || e));
}
}
async runTest() {
const testMessage = this.shadowRoot.getElementById("testMessage").value;
if (!testMessage.trim()) {
alert("Ingresa un mensaje de prueba");
modal.warn("Ingresa un mensaje de prueba");
return;
}

View File

@@ -1,6 +1,7 @@
import { api } from "../lib/api.js";
import { on } from "../lib/bus.js";
import { navigateToItem } from "../lib/router.js";
import { modal } from "../lib/modal.js";
class RecommendationsCrud extends HTMLElement {
static get observedAttributes() {
@@ -661,7 +662,7 @@ class RecommendationsCrud extends HTMLElement {
addBtn.onclick = () => {
if (!selectedProductId) {
alert("Selecciona un producto primero");
modal.warn("Selecciona un producto primero");
return;
}
@@ -792,7 +793,7 @@ class RecommendationsCrud extends HTMLElement {
const active = this.shadowRoot.getElementById("activeInput").checked;
if (!ruleKey) {
alert("El nombre de la regla es requerido");
modal.warn("El nombre de la regla es requerido");
return;
}
@@ -812,11 +813,11 @@ class RecommendationsCrud extends HTMLElement {
if (this.currentRuleType === "crosssell") {
if (!this.selectedTriggerProducts.length) {
alert("Selecciona al menos un producto trigger");
modal.warn("Selecciona al menos un producto trigger");
return;
}
if (!this.selectedRecommendedProducts.length) {
alert("Selecciona al menos un producto para recomendar");
modal.warn("Selecciona al menos un producto para recomendar");
return;
}
data.trigger_product_ids = this.selectedTriggerProducts;
@@ -827,7 +828,7 @@ class RecommendationsCrud extends HTMLElement {
data.trigger_event = triggerEvent;
if (!this.ruleItems.length) {
alert("Agrega al menos un producto con cantidad");
modal.warn("Agrega al menos un producto con cantidad");
return;
}
@@ -853,13 +854,14 @@ class RecommendationsCrud extends HTMLElement {
this.renderForm();
} catch (e) {
console.error("Error saving recommendation:", e);
alert("Error guardando: " + (e.message || e));
modal.error("Error guardando: " + (e.message || e));
}
}
async delete() {
if (!this.selected?.id) return;
if (!confirm(`¿Eliminar la regla "${this.selected.rule_key}"?`)) return;
const confirmed = await modal.confirm(`¿Eliminar la regla "${this.selected.rule_key}"?`);
if (!confirmed) return;
try {
await api.deleteRecommendation(this.selected.id);
@@ -869,7 +871,7 @@ class RecommendationsCrud extends HTMLElement {
this.renderForm();
} catch (e) {
console.error("Error deleting recommendation:", e);
alert("Error eliminando: " + (e.message || e));
modal.error("Error eliminando: " + (e.message || e));
}
}

View File

@@ -1,5 +1,6 @@
import { api } from "../lib/api.js";
import { on } from "../lib/bus.js";
import { modal } from "../lib/modal.js";
class TakeoversCrud extends HTMLElement {
constructor() {
@@ -10,6 +11,7 @@ class TakeoversCrud extends HTMLElement {
this.loading = false;
this.products = [];
this.pendingCount = 0;
this.cartItemsToAdd = []; // Items que el humano quiere agregar al carrito
this.shadowRoot.innerHTML = `
<style>
@@ -79,6 +81,22 @@ class TakeoversCrud extends HTMLElement {
.product-option:hover { background:#1a2535; }
.product-option .price { font-size:11px; color:#8aa0b5; }
.cart-section { background:#0d2818; border:1px solid #2ecc71; border-radius:8px; padding:12px; margin-bottom:12px; }
.cart-section h4 { margin:0 0 12px; font-size:13px; color:#2ecc71; display:flex; align-items:center; gap:8px; }
.cart-section h4 svg { width:16px; height:16px; fill:#2ecc71; }
.cart-items-list { margin-bottom:12px; }
.cart-item-row { display:flex; align-items:center; gap:8px; padding:8px; background:#0f1520; border-radius:6px; margin-bottom:6px; }
.cart-item-row .name { flex:1; font-size:13px; color:#e7eef7; }
.cart-item-row .qty { width:60px; text-align:center; }
.cart-item-row .unit-select { width:80px; }
.cart-item-row .remove-btn { background:#e74c3c; color:#fff; border:none; border-radius:4px; padding:4px 8px; cursor:pointer; font-size:11px; }
.cart-item-row .remove-btn:hover { background:#c0392b; }
.add-cart-row { display:flex; gap:8px; align-items:flex-end; }
.add-cart-row .product-selector { flex:1; }
.add-cart-row .qty-input { width:70px; }
.add-cart-row .unit-select { width:80px; }
.add-cart-row button { white-space:nowrap; }
.no-pending { text-align:center; padding:60px 20px; color:#2ecc71; }
.no-pending svg { width:48px; height:48px; fill:#2ecc71; margin-bottom:16px; }
</style>
@@ -259,9 +277,30 @@ class TakeoversCrud extends HTMLElement {
</div>
` : ""}
<div class="cart-section">
<h4>
<svg viewBox="0 0 24 24"><path d="M7 18c-1.1 0-1.99.9-1.99 2S5.9 22 7 22s2-.9 2-2-.9-2-2-2zM1 2v2h2l3.6 7.59-1.35 2.45c-.16.28-.25.61-.25.96 0 1.1.9 2 2 2h12v-2H7.42c-.14 0-.25-.11-.25-.25l.03-.12.9-1.63h7.45c.75 0 1.41-.41 1.75-1.03l3.58-6.49c.08-.14.12-.31.12-.49 0-.55-.45-1-1-1H5.21l-.94-2H1zm16 16c-1.1 0-1.99.9-1.99 2s.89 2 1.99 2 2-.9 2-2-.9-2-2-2z"/></svg>
Agregar al carrito del cliente
</h4>
<div class="cart-items-list" id="cartItemsList"></div>
<div class="add-cart-row">
<div class="product-selector" id="cartProductSelector">
<input type="text" id="cartProductSearch" placeholder="Buscar producto..." />
<div class="product-dropdown" id="cartProductDropdown"></div>
</div>
<input type="number" id="cartQtyInput" class="qty-input" placeholder="Cant" min="0.1" step="0.1" value="1" />
<select id="cartUnitSelect" class="unit-select">
<option value="kg">kg</option>
<option value="unit">unidad</option>
<option value="g">gramos</option>
</select>
<button type="button" id="addCartItemBtn" class="secondary">+ Agregar</button>
</div>
</div>
<div class="field" style="flex:1;">
<label>Tu respuesta (se enviara como el bot)</label>
<textarea id="responseInput" placeholder="Ej: Disculpa, no tenemos ese producto. Pero tenemos..."></textarea>
<textarea id="responseInput" placeholder="Ej: Te anoto 2kg de vacío. ¿Algo más?"></textarea>
</div>
<div class="alias-section">
@@ -297,7 +336,148 @@ class TakeoversCrud extends HTMLElement {
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 = `<div style="font-size:12px;color:#8aa0b5;padding:8px;">Sin items agregados</div>`;
return;
}
container.innerHTML = this.cartItemsToAdd.map((item, idx) => `
<div class="cart-item-row">
<span class="name">${item.name}</span>
<input type="number" class="qty" value="${item.qty}" min="0.1" step="0.1" data-idx="${idx}" />
<select class="unit-select" data-idx="${idx}">
<option value="kg" ${item.unit === 'kg' ? 'selected' : ''}>kg</option>
<option value="unit" ${item.unit === 'unit' ? 'selected' : ''}>unidad</option>
<option value="g" ${item.unit === 'g' ? 'selected' : ''}>gramos</option>
</select>
<button class="remove-btn" data-idx="${idx}">✕</button>
</div>
`).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 => `
<div class="product-option" data-id="${p.woo_product_id}" data-name="${this.escapeHtml(p.name)}" data-unit="${p.sell_unit || 'kg'}">
<span>${p.name}</span>
<span class="price">$${p.price || 0}</span>
</div>
`).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) {
@@ -362,7 +542,7 @@ class TakeoversCrud extends HTMLElement {
async respond() {
const response = this.shadowRoot.getElementById("responseInput").value.trim();
if (!response) {
alert("Escribe una respuesta");
modal.warn("Escribe una respuesta");
return;
}
@@ -376,32 +556,42 @@ class TakeoversCrud extends HTMLElement {
};
}
// 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 });
alert("Respuesta enviada");
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);
alert("Error: " + (e.message || e));
modal.error("Error: " + (e.message || e));
}
}
async cancel() {
if (!confirm("Cancelar este takeover? El cliente no recibira respuesta automatica.")) {
return;
}
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);
alert("Takeover cancelado");
modal.success("Takeover cancelado");
this.selected = null;
await this.load();
this.renderForm();
} catch (e) {
console.error("Error cancelling:", e);
alert("Error: " + (e.message || e));
modal.error("Error: " + (e.message || e));
}
}
}

View File

@@ -1,4 +1,5 @@
import { api } from "../lib/api.js";
import { modal } from "../lib/modal.js";
// Datos aleatorios para generar usuarios de prueba
const NOMBRES = ["Juan", "María", "Carlos", "Ana", "Pedro", "Laura", "Diego", "Sofía"];
@@ -300,7 +301,7 @@ class TestPanel extends HTMLElement {
}
if (this.products.length === 0) {
alert("No hay productos con stock disponible");
modal.warn("No hay productos con stock disponible");
return;
}
@@ -420,11 +421,11 @@ class TestPanel extends HTMLElement {
this.shadowRoot.getElementById("orderResult").style.display = "block";
this.shadowRoot.getElementById("inputAmount").value = result.total || "";
} else {
alert("Error: " + (result.error || "Error desconocido"));
modal.error("Error: " + (result.error || "Error desconocido"));
}
} catch (e) {
console.error("[test-panel] createOrder error:", e);
alert("Error creando orden: " + e.message);
modal.error("Error creando orden: " + e.message);
} finally {
this.loading = false;
btn.textContent = "Crear Orden en WooCommerce";
@@ -435,13 +436,13 @@ class TestPanel extends HTMLElement {
async createPaymentLink() {
if (this.loading) return;
if (!this.lastOrder?.woo_order_id) {
alert("Primero creá una orden");
modal.warn("Primero creá una orden");
return;
}
const amount = parseFloat(this.shadowRoot.getElementById("inputAmount").value);
if (!amount || amount <= 0) {
alert("Ingresá un monto válido");
modal.warn("Ingresá un monto válido");
return;
}
@@ -464,11 +465,11 @@ class TestPanel extends HTMLElement {
this.shadowRoot.getElementById("preferenceIdValue").textContent = result.preference_id || "—";
this.shadowRoot.getElementById("paymentResult").style.display = "block";
} else {
alert("Error: " + (result.error || "Error desconocido"));
modal.error("Error: " + (result.error || "Error desconocido"));
}
} catch (e) {
console.error("[test-panel] createPaymentLink error:", e);
alert("Error generando link: " + e.message);
modal.error("Error generando link: " + e.message);
} finally {
this.loading = false;
btn.textContent = "Generar Link de Pago";
@@ -479,7 +480,7 @@ class TestPanel extends HTMLElement {
async simulateWebhook() {
if (this.loading) return;
if (!this.lastOrder?.woo_order_id) {
alert("Primero creá una orden");
modal.warn("Primero creá una orden");
return;
}
@@ -501,11 +502,11 @@ class TestPanel extends HTMLElement {
this.shadowRoot.getElementById("webhookOrderStatusValue").textContent = result.order_status || "processing";
this.shadowRoot.getElementById("webhookResult").style.display = "block";
} else {
alert("Error: " + (result.error || "Error desconocido"));
modal.error("Error: " + (result.error || "Error desconocido"));
}
} catch (e) {
console.error("[test-panel] simulateWebhook error:", e);
alert("Error simulando webhook: " + e.message);
modal.error("Error simulando webhook: " + e.message);
} finally {
this.loading = false;
btn.textContent = "Simular Pago Exitoso";

View File

@@ -1,6 +1,7 @@
import { api } from "../lib/api.js";
import { emit, on } from "../lib/bus.js";
import { navigateToItem } from "../lib/router.js";
import { modal } from "../lib/modal.js";
class UsersCrud extends HTMLElement {
constructor() {
@@ -291,24 +292,26 @@ class UsersCrud extends HTMLElement {
};
this.shadowRoot.getElementById("deleteConv").onclick = async () => {
if (!confirm(`¿Eliminar la conversacion de "${u.chat_id}"?`)) return;
const confirmed = await modal.confirm(`¿Eliminar la conversación de "${u.chat_id}"?`);
if (!confirmed) return;
try {
await api.deleteConversation(u.chat_id);
alert("Conversacion eliminada");
modal.success("Conversación eliminada");
} catch (e) {
alert("Error: " + (e.message || e));
modal.error("Error: " + (e.message || e));
}
};
this.shadowRoot.getElementById("deleteUser").onclick = async () => {
if (!confirm(`¿Eliminar usuario "${u.chat_id}", su conversacion y el customer en Woo?`)) return;
const confirmed = await modal.confirm(`¿Eliminar usuario "${u.chat_id}", su conversación y el customer en Woo?`);
if (!confirmed) return;
try {
await api.deleteUser(u.chat_id, { deleteWoo: true });
this.selected = null;
await this.load();
this.renderDetail();
} catch (e) {
alert("Error: " + (e.message || e));
modal.error("Error: " + (e.message || e));
}
};
}