import { api } from "../lib/api.js"; import { on } from "../lib/bus.js"; const PROMPT_LABELS = { router: "Router (clasificador de dominio)", greeting: "Saludos", orders: "Pedidos", shipping: "Envio/Retiro", payment: "Pago", browse: "Consultas de catalogo", }; class PromptsCrud extends HTMLElement { constructor() { super(); this.attachShadow({ mode: "open" }); this.items = []; this.selected = null; this.loading = false; this.versions = []; this.availableVariables = []; this.availableModels = []; this.currentSettings = {}; // Valores actuales de las variables this.testResult = null; this.testLoading = false; this.shadowRoot.innerHTML = `
Prompts del Sistema
Cargando...
Editor de Prompt
Selecciona un prompt para editarlo
`; } connectedCallback() { this.load(); // Refrescar settings cuando se vuelve a esta vista (por si cambiaron en Config) this._unsubRouter = on("router:viewChanged", ({ view }) => { if (view === "prompts") { this.refreshSettings(); } }); } disconnectedCallback() { this._unsubRouter?.(); } async refreshSettings() { try { const settings = await api.getSettings(); this.currentSettings = { store_name: settings.store_name || "", store_hours: this.formatStoreHours(settings), store_address: settings.store_address || "", store_phone: settings.store_phone || "", bot_name: settings.bot_name || "", current_date: new Date().toLocaleDateString("es-AR"), customer_name: "(nombre del cliente)", state: "(estado actual)", }; // Re-renderizar el form si hay uno seleccionado if (this.selected) { this.renderForm(); } } catch (e) { console.debug("Error refreshing settings:", e); } } async load() { this.loading = true; this.renderList(); try { // Cargar prompts y settings en paralelo const [data, settings] = await Promise.all([ api.prompts(), api.getSettings().catch(() => ({})), ]); this.items = data.items || []; this.availableVariables = data.available_variables || []; this.availableModels = data.available_models || []; // Mapear settings a variables this.currentSettings = { store_name: settings.store_name || "", store_hours: this.formatStoreHours(settings), store_address: settings.store_address || "", store_phone: settings.store_phone || "", bot_name: settings.bot_name || "", current_date: new Date().toLocaleDateString("es-AR"), customer_name: "(nombre del cliente)", state: "(estado actual)", }; this.loading = false; this.renderList(); } catch (e) { console.error("Error loading prompts:", e); this.items = []; this.loading = false; this.renderList(); } } formatStoreHours(settings) { if (!settings.pickup_days) return ""; // Mapeo de días cortos a nombres legibles const dayNames = { lun: "Lun", mar: "Mar", mie: "Mié", jue: "Jue", vie: "Vie", sab: "Sáb", dom: "Dom" }; const days = settings.pickup_days.split(",").map(d => dayNames[d.trim()] || d).join(", "); const start = (settings.pickup_hours_start || "08:00").slice(0, 5); const end = (settings.pickup_hours_end || "20:00").slice(0, 5); return `${days} de ${start} a ${end}`; } renderList() { const list = this.shadowRoot.getElementById("list"); if (this.loading) { list.innerHTML = `
Cargando...
`; return; } if (!this.items.length) { list.innerHTML = `
No se encontraron prompts
`; return; } list.innerHTML = ""; for (const item of this.items) { const el = document.createElement("div"); el.className = "item" + (this.selected?.prompt_key === item.prompt_key ? " active" : ""); const label = PROMPT_LABELS[item.prompt_key] || item.prompt_key; const statusClass = item.is_default ? "default" : "custom"; const statusText = item.is_default ? "Default" : `v${item.version}`; el.innerHTML = `
${label}
${statusText} ${item.model ? ` | ${item.model}` : ""}
`; el.onclick = () => this.selectPrompt(item); list.appendChild(el); } } async selectPrompt(item) { this.selected = item; this.testResult = null; this.renderList(); // Cargar detalles con versiones try { const details = await api.getPrompt(item.prompt_key); this.selected = { ...item, ...details.current }; this.versions = details.versions || []; this.availableVariables = details.available_variables || this.availableVariables; this.availableModels = details.available_models || this.availableModels; this.renderForm(); } catch (e) { console.error("Error loading prompt details:", e); this.renderForm(); } } renderForm() { const form = this.shadowRoot.getElementById("form"); const title = this.shadowRoot.getElementById("formTitle"); if (!this.selected) { title.textContent = "Editor de Prompt"; form.innerHTML = `
Selecciona un prompt para editarlo
`; return; } const label = PROMPT_LABELS[this.selected.prompt_key] || this.selected.prompt_key; title.textContent = `Editar: ${label}`; const content = this.selected.content || ""; const model = this.selected.model || "gpt-4-turbo"; form.innerHTML = `
Variables disponibles (click para insertar):
${this.availableVariables.map(v => { const key = typeof v === 'string' ? v : v.key; const desc = typeof v === 'string' ? '' : (v.description || ''); const value = this.currentSettings[key] || ''; const displayValue = value ? `= ${value}` : '(vacío)'; return ` ${this.escapeHtml(displayValue)} `; }).join("")}
${this.versions.length > 0 ? `
${this.versions.map(v => `
v${v.version} ${v.is_active ? "(activa)" : ""} ${this.formatDate(v.created_at)} ${!v.is_active ? `` : ""}
`).join("")}
` : ""}
`; // Event listeners this.shadowRoot.getElementById("saveBtn").onclick = () => this.save(); this.shadowRoot.getElementById("resetBtn").onclick = () => this.reset(); this.shadowRoot.getElementById("testBtn").onclick = () => this.toggleTestSection(); this.shadowRoot.getElementById("runTestBtn").onclick = () => this.runTest(); // Variable buttons this.shadowRoot.querySelectorAll(".var-btn").forEach(btn => { btn.onclick = () => this.insertVariable(btn.dataset.var); }); // Version restore buttons this.shadowRoot.querySelectorAll(".versions-list button").forEach(btn => { btn.onclick = () => this.rollback(parseInt(btn.dataset.version, 10)); }); } escapeHtml(str) { return (str || "").replace(/&/g, "&").replace(//g, ">").replace(/"/g, """); } formatDate(dateStr) { if (!dateStr) return ""; const d = new Date(dateStr); return d.toLocaleDateString("es-AR", { day: "2-digit", month: "2-digit", hour: "2-digit", minute: "2-digit" }); } insertVariable(varName) { const textarea = this.shadowRoot.getElementById("contentInput"); const start = textarea.selectionStart; const end = textarea.selectionEnd; const text = textarea.value; const insertion = `{{${varName}}}`; textarea.value = text.slice(0, start) + insertion + text.slice(end); textarea.selectionStart = textarea.selectionEnd = start + insertion.length; textarea.focus(); } toggleTestSection() { const section = this.shadowRoot.getElementById("testSection"); section.style.display = section.style.display === "none" ? "block" : "none"; } async save() { const content = this.shadowRoot.getElementById("contentInput").value; const model = this.shadowRoot.getElementById("modelSelect").value; if (!content.trim()) { alert("El contenido no puede estar vacio"); return; } try { await api.savePrompt(this.selected.prompt_key, { content, model }); alert("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)); } } async reset() { if (!confirm("Esto desactivara todas las versiones custom y volvera al prompt por defecto. Continuar?")) { return; } try { await api.resetPrompt(this.selected.prompt_key); alert("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)); } } async rollback(version) { if (!confirm(`Restaurar version ${version}? Se creara una nueva version con ese contenido.`)) { return; } try { await api.rollbackPrompt(this.selected.prompt_key, version); alert("Version 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)); } } async runTest() { const testMessage = this.shadowRoot.getElementById("testMessage").value; if (!testMessage.trim()) { alert("Ingresa un mensaje de prueba"); return; } const content = this.shadowRoot.getElementById("contentInput").value; const container = this.shadowRoot.getElementById("testResultContainer"); container.innerHTML = `
Ejecutando prueba...
`; try { const result = await api.testPrompt(this.selected.prompt_key, { content, test_message: testMessage, store_config: { store_name: "Carniceria Demo", bot_name: "Piaf" }, }); if (result.ok) { let parsed = result.response; try { parsed = JSON.stringify(JSON.parse(result.response), null, 2); } catch (e) { /* no es JSON */ } container.innerHTML = `
${this.escapeHtml(parsed)}
Modelo: ${result.model} | Latencia: ${result.latency_ms}ms | Tokens: ${result.usage?.total_tokens || "?"}
`; } else { container.innerHTML = `
Error: ${result.error || "Unknown"}
`; } } catch (e) { console.error("Error testing prompt:", e); container.innerHTML = `
Error: ${e.message || e}
`; } } } customElements.define("prompts-crud", PromptsCrud);