674 lines
26 KiB
JavaScript
674 lines
26 KiB
JavaScript
import { api } from "../lib/api.js";
|
|
|
|
const DAYS = [
|
|
{ id: "lun", label: "Lunes", short: "L" },
|
|
{ id: "mar", label: "Martes", short: "M" },
|
|
{ id: "mie", label: "Miércoles", short: "X" },
|
|
{ id: "jue", label: "Jueves", short: "J" },
|
|
{ id: "vie", label: "Viernes", short: "V" },
|
|
{ id: "sab", label: "Sábado", short: "S" },
|
|
{ id: "dom", label: "Domingo", short: "D" },
|
|
];
|
|
|
|
// Lista oficial de 48 barrios de CABA
|
|
const CABA_BARRIOS = [
|
|
"Agronomía", "Almagro", "Balvanera", "Barracas", "Belgrano", "Boedo",
|
|
"Caballito", "Chacarita", "Coghlan", "Colegiales", "Constitución",
|
|
"Flores", "Floresta", "La Boca", "La Paternal", "Liniers",
|
|
"Mataderos", "Monte Castro", "Montserrat", "Nueva Pompeya", "Núñez",
|
|
"Palermo", "Parque Avellaneda", "Parque Chacabuco", "Parque Chas",
|
|
"Parque Patricios", "Puerto Madero", "Recoleta", "Retiro", "Saavedra",
|
|
"San Cristóbal", "San Nicolás", "San Telmo", "Vélez Sársfield", "Versalles",
|
|
"Villa Crespo", "Villa del Parque", "Villa Devoto", "Villa General Mitre",
|
|
"Villa Lugano", "Villa Luro", "Villa Ortúzar", "Villa Pueyrredón",
|
|
"Villa Real", "Villa Riachuelo", "Villa Santa Rita", "Villa Soldati", "Villa Urquiza"
|
|
];
|
|
|
|
class SettingsCrud extends HTMLElement {
|
|
constructor() {
|
|
super();
|
|
this.attachShadow({ mode: "open" });
|
|
this.settings = null;
|
|
this.loading = false;
|
|
this.saving = false;
|
|
|
|
this.shadowRoot.innerHTML = `
|
|
<style>
|
|
:host { display:block; height:100%; padding:16px; overflow:auto; }
|
|
* { box-sizing:border-box; font-family:system-ui,Segoe UI,Roboto,Arial; }
|
|
.container { max-width:800px; margin:0 auto; }
|
|
.panel { background:#121823; border:1px solid #1e2a3a; border-radius:10px; padding:20px; margin-bottom:16px; }
|
|
.panel-title { font-size:16px; font-weight:700; color:#e7eef7; margin-bottom:16px; display:flex; align-items:center; gap:8px; }
|
|
.panel-title svg { width:20px; height:20px; fill:#1f6feb; }
|
|
|
|
.form-row { display:grid; grid-template-columns:1fr 1fr; gap:16px; margin-bottom:16px; }
|
|
.form-row.full { grid-template-columns:1fr; }
|
|
|
|
.field { }
|
|
.field label { display:block; font-size:12px; color:#8aa0b5; margin-bottom:6px; text-transform:uppercase; letter-spacing:.4px; }
|
|
.field-hint { font-size:11px; color:#6c7a89; margin-top:4px; }
|
|
|
|
input, select, textarea {
|
|
background:#0f1520; color:#e7eef7; border:1px solid #253245;
|
|
border-radius:8px; padding:10px 14px; font-size:14px; width:100%;
|
|
}
|
|
input:focus, select:focus, textarea:focus { outline:none; border-color:#1f6feb; }
|
|
input:disabled { opacity:.6; cursor:not-allowed; }
|
|
|
|
button {
|
|
cursor:pointer; background:#1f6feb; color:#fff; border:none;
|
|
border-radius:8px; padding:10px 20px; font-size:14px; font-weight:600;
|
|
}
|
|
button:hover { background:#1a5fd0; }
|
|
button:disabled { opacity:.5; cursor:not-allowed; }
|
|
button.secondary { background:#253245; }
|
|
button.secondary:hover { background:#2d3e52; }
|
|
|
|
.toggle-row { display:flex; align-items:center; gap:12px; margin-bottom:16px; }
|
|
.toggle {
|
|
position:relative; width:48px; height:26px;
|
|
background:#253245; border-radius:13px; cursor:pointer;
|
|
transition:background .2s; flex-shrink:0;
|
|
}
|
|
.toggle.active { background:#1f6feb; }
|
|
.toggle::after {
|
|
content:''; position:absolute; top:3px; left:3px;
|
|
width:20px; height:20px; background:#fff; border-radius:50%;
|
|
transition:transform .2s;
|
|
}
|
|
.toggle.active::after { transform:translateX(22px); }
|
|
.toggle-label { font-size:14px; color:#e7eef7; }
|
|
|
|
/* Schedule grid */
|
|
.schedule-grid { display:flex; flex-direction:column; gap:8px; }
|
|
.schedule-row {
|
|
display:grid;
|
|
grid-template-columns:90px 32px 1fr;
|
|
gap:12px;
|
|
align-items:center;
|
|
padding:8px 12px;
|
|
background:#0f1520;
|
|
border-radius:8px;
|
|
border:1px solid #1e2a3a;
|
|
}
|
|
.schedule-row.disabled { opacity:0.4; }
|
|
.day-label { font-size:13px; color:#e7eef7; font-weight:500; }
|
|
.day-toggle {
|
|
width:32px; height:18px; background:#253245; border-radius:9px;
|
|
cursor:pointer; position:relative; transition:background .2s;
|
|
}
|
|
.day-toggle.active { background:#2ecc71; }
|
|
.day-toggle::after {
|
|
content:''; position:absolute; top:2px; left:2px;
|
|
width:14px; height:14px; background:#fff; border-radius:50%;
|
|
transition:transform .2s;
|
|
}
|
|
.day-toggle.active::after { transform:translateX(14px); }
|
|
.hours-inputs { display:flex; align-items:center; gap:8px; }
|
|
.hours-inputs input {
|
|
width:70px; text-align:center; font-family:monospace;
|
|
font-size:13px; padding:6px 8px; letter-spacing:1px;
|
|
}
|
|
.hours-inputs span { color:#6c7a89; font-size:12px; }
|
|
.hours-inputs.disabled input { opacity:0.4; pointer-events:none; }
|
|
|
|
.actions { display:flex; gap:12px; margin-top:24px; }
|
|
.loading { text-align:center; padding:60px; color:#8aa0b5; }
|
|
|
|
.success-msg {
|
|
background:#2ecc7130; border:1px solid #2ecc71;
|
|
color:#2ecc71; padding:12px 16px; border-radius:8px;
|
|
margin-bottom:16px; font-size:14px;
|
|
}
|
|
.error-msg {
|
|
background:#e74c3c30; border:1px solid #e74c3c;
|
|
color:#e74c3c; padding:12px 16px; border-radius:8px;
|
|
margin-bottom:16px; font-size:14px;
|
|
}
|
|
|
|
.min-order-field { margin-top:16px; padding-top:16px; border-top:1px solid #1e2a3a; }
|
|
|
|
/* Zonas de entrega */
|
|
.zones-search { margin-bottom:12px; }
|
|
.zones-search input {
|
|
width:100%; padding:10px 14px;
|
|
background:#0f1520 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%236c7a89'%3E%3Cpath d='M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z'/%3E%3C/svg%3E") no-repeat 12px center;
|
|
background-size:18px; padding-left:38px;
|
|
}
|
|
.zones-list { max-height:400px; overflow-y:auto; display:flex; flex-direction:column; gap:4px; }
|
|
.zone-row {
|
|
display:grid;
|
|
grid-template-columns:32px 1fr;
|
|
gap:12px;
|
|
align-items:start;
|
|
padding:10px 12px;
|
|
background:#0f1520;
|
|
border-radius:8px;
|
|
border:1px solid #1e2a3a;
|
|
transition:border-color .2s;
|
|
}
|
|
.zone-row.active { border-color:#1f6feb; background:#0f1825; }
|
|
.zone-row.hidden { display:none; }
|
|
.zone-toggle {
|
|
width:32px; height:18px; background:#253245; border-radius:9px;
|
|
cursor:pointer; position:relative; transition:background .2s; margin-top:2px;
|
|
}
|
|
.zone-toggle.active { background:#2ecc71; }
|
|
.zone-toggle::after {
|
|
content:''; position:absolute; top:2px; left:2px;
|
|
width:14px; height:14px; background:#fff; border-radius:50%;
|
|
transition:transform .2s;
|
|
}
|
|
.zone-toggle.active::after { transform:translateX(14px); }
|
|
.zone-content { display:flex; flex-direction:column; gap:8px; }
|
|
.zone-name { font-size:14px; color:#e7eef7; font-weight:500; }
|
|
.zone-config { display:none; gap:16px; flex-wrap:wrap; align-items:center; }
|
|
.zone-row.active .zone-config { display:flex; }
|
|
.zone-days { display:flex; gap:4px; }
|
|
.zone-day {
|
|
width:28px; height:28px; border-radius:6px;
|
|
background:#253245; color:#8aa0b5;
|
|
display:flex; align-items:center; justify-content:center;
|
|
font-size:11px; font-weight:600; cursor:pointer;
|
|
transition:all .15s;
|
|
}
|
|
.zone-day.active { background:#1f6feb; color:#fff; }
|
|
.zone-day:hover { background:#2d3e52; }
|
|
.zone-day.active:hover { background:#1a5fd0; }
|
|
.zone-cost { display:flex; align-items:center; gap:6px; }
|
|
.zone-cost label { font-size:12px; color:#8aa0b5; }
|
|
.zone-cost input { width:90px; padding:6px 10px; font-size:13px; text-align:right; }
|
|
.zones-summary {
|
|
margin-top:12px; padding:12px; background:#0f1520;
|
|
border-radius:8px; font-size:13px; color:#8aa0b5;
|
|
}
|
|
.zones-summary strong { color:#e7eef7; }
|
|
</style>
|
|
|
|
<div class="container">
|
|
<div id="messages"></div>
|
|
<div id="content">
|
|
<div class="loading">Cargando configuración...</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
connectedCallback() {
|
|
this.load();
|
|
}
|
|
|
|
async load() {
|
|
this.loading = true;
|
|
this.render();
|
|
|
|
try {
|
|
this.settings = await api.getSettings();
|
|
// Asegurar que schedule existe
|
|
if (!this.settings.schedule) {
|
|
this.settings.schedule = { delivery: {}, pickup: {} };
|
|
}
|
|
// Asegurar que delivery_zones existe
|
|
if (!this.settings.delivery_zones) {
|
|
this.settings.delivery_zones = {};
|
|
}
|
|
this.loading = false;
|
|
this.render();
|
|
} catch (e) {
|
|
console.error("Error loading settings:", e);
|
|
this.loading = false;
|
|
this.showError("Error cargando configuración: " + e.message);
|
|
}
|
|
}
|
|
|
|
getScheduleSlot(type, dayId) {
|
|
return this.settings?.schedule?.[type]?.[dayId] || null;
|
|
}
|
|
|
|
setScheduleSlot(type, dayId, slot) {
|
|
if (!this.settings.schedule) {
|
|
this.settings.schedule = { delivery: {}, pickup: {} };
|
|
}
|
|
if (!this.settings.schedule[type]) {
|
|
this.settings.schedule[type] = {};
|
|
}
|
|
this.settings.schedule[type][dayId] = slot;
|
|
}
|
|
|
|
renderScheduleGrid(type, enabled) {
|
|
const defaultStart = type === "delivery" ? "09:00" : "08:00";
|
|
const defaultEnd = type === "delivery" ? "18:00" : "20:00";
|
|
|
|
return DAYS.map(day => {
|
|
const slot = this.getScheduleSlot(type, day.id);
|
|
const isActive = slot !== null && slot !== undefined;
|
|
const start = slot?.start || defaultStart;
|
|
const end = slot?.end || defaultEnd;
|
|
|
|
return `
|
|
<div class="schedule-row ${!enabled ? 'disabled' : ''}">
|
|
<span class="day-label">${day.label}</span>
|
|
<div class="day-toggle ${isActive ? 'active' : ''}"
|
|
data-type="${type}" data-day="${day.id}"
|
|
${!enabled ? 'style="pointer-events:none"' : ''}></div>
|
|
<div class="hours-inputs ${!isActive ? 'disabled' : ''}">
|
|
<input type="text"
|
|
class="hour-start"
|
|
data-type="${type}"
|
|
data-day="${day.id}"
|
|
value="${start}"
|
|
placeholder="09:00"
|
|
maxlength="5"
|
|
${!enabled || !isActive ? 'disabled' : ''} />
|
|
<span>a</span>
|
|
<input type="text"
|
|
class="hour-end"
|
|
data-type="${type}"
|
|
data-day="${day.id}"
|
|
value="${end}"
|
|
placeholder="18:00"
|
|
maxlength="5"
|
|
${!enabled || !isActive ? 'disabled' : ''} />
|
|
</div>
|
|
</div>
|
|
`;
|
|
}).join("");
|
|
}
|
|
|
|
// Convierte nombre de barrio a key (ej: "Villa Crespo" -> "villa_crespo")
|
|
barrioToKey(name) {
|
|
return name.toLowerCase()
|
|
.normalize("NFD").replace(/[\u0300-\u036f]/g, "") // quitar acentos
|
|
.replace(/\s+/g, "_");
|
|
}
|
|
|
|
getZoneConfig(barrioKey) {
|
|
return this.settings?.delivery_zones?.[barrioKey] || null;
|
|
}
|
|
|
|
setZoneConfig(barrioKey, config) {
|
|
if (!this.settings.delivery_zones) {
|
|
this.settings.delivery_zones = {};
|
|
}
|
|
if (config === null) {
|
|
delete this.settings.delivery_zones[barrioKey];
|
|
} else {
|
|
this.settings.delivery_zones[barrioKey] = config;
|
|
}
|
|
}
|
|
|
|
renderZonesList() {
|
|
return CABA_BARRIOS.map(barrio => {
|
|
const key = this.barrioToKey(barrio);
|
|
const config = this.getZoneConfig(key);
|
|
const isActive = config?.enabled === true;
|
|
const days = config?.days || [];
|
|
const cost = config?.delivery_cost || 0;
|
|
|
|
return `
|
|
<div class="zone-row ${isActive ? 'active' : ''}" data-barrio="${key}">
|
|
<div class="zone-toggle ${isActive ? 'active' : ''}" data-barrio="${key}"></div>
|
|
<div class="zone-content">
|
|
<span class="zone-name">${barrio}</span>
|
|
<div class="zone-config">
|
|
<div class="zone-days">
|
|
${DAYS.map(d => `
|
|
<div class="zone-day ${days.includes(d.id) ? 'active' : ''}"
|
|
data-barrio="${key}" data-day="${d.id}"
|
|
title="${d.label}">${d.short}</div>
|
|
`).join("")}
|
|
</div>
|
|
<div class="zone-cost">
|
|
<label>Costo:</label>
|
|
<input type="number" class="zone-cost-input" data-barrio="${key}"
|
|
value="${cost}" min="0" step="100" placeholder="0" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}).join("");
|
|
}
|
|
|
|
renderZonesSummary() {
|
|
const zones = this.settings?.delivery_zones || {};
|
|
const activeZones = Object.entries(zones).filter(([k, v]) => v?.enabled);
|
|
|
|
if (activeZones.length === 0) {
|
|
return `<div class="zones-summary">No hay zonas de entrega configuradas. Activá los barrios donde hacés delivery.</div>`;
|
|
}
|
|
|
|
return `<div class="zones-summary"><strong>${activeZones.length}</strong> zona${activeZones.length > 1 ? 's' : ''} de entrega activa${activeZones.length > 1 ? 's' : ''}</div>`;
|
|
}
|
|
|
|
render() {
|
|
const content = this.shadowRoot.getElementById("content");
|
|
|
|
if (this.loading) {
|
|
content.innerHTML = `<div class="loading">Cargando configuración...</div>`;
|
|
return;
|
|
}
|
|
|
|
if (!this.settings) {
|
|
content.innerHTML = `<div class="loading">No se pudo cargar la configuración</div>`;
|
|
return;
|
|
}
|
|
|
|
const s = this.settings;
|
|
|
|
content.innerHTML = `
|
|
<!-- Info del Negocio -->
|
|
<div class="panel">
|
|
<div class="panel-title">
|
|
<svg viewBox="0 0 24 24"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/></svg>
|
|
Información del Negocio
|
|
</div>
|
|
|
|
<div class="form-row">
|
|
<div class="field">
|
|
<label>Nombre del negocio</label>
|
|
<input type="text" id="storeName" value="${this.escapeHtml(s.store_name || "")}" placeholder="Ej: Carnicería Don Pedro" />
|
|
<div class="field-hint">Se usa en los mensajes del bot</div>
|
|
</div>
|
|
<div class="field">
|
|
<label>Nombre del bot</label>
|
|
<input type="text" id="botName" value="${this.escapeHtml(s.bot_name || "")}" placeholder="Ej: Piaf" />
|
|
<div class="field-hint">El asistente virtual</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-row">
|
|
<div class="field">
|
|
<label>Dirección</label>
|
|
<input type="text" id="storeAddress" value="${this.escapeHtml(s.store_address || "")}" placeholder="Ej: Av. Corrientes 1234, CABA" />
|
|
</div>
|
|
<div class="field">
|
|
<label>Teléfono</label>
|
|
<input type="text" id="storePhone" value="${this.escapeHtml(s.store_phone || "")}" placeholder="Ej: +54 11 1234-5678" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Delivery -->
|
|
<div class="panel">
|
|
<div class="panel-title">
|
|
<svg viewBox="0 0 24 24"><path d="M18.92 6.01C18.72 5.42 18.16 5 17.5 5h-11c-.66 0-1.21.42-1.42 1.01L3 12v8c0 .55.45 1 1 1h1c.55 0 1-.45 1-1v-1h12v1c0 .55.45 1 1 1h1c.55 0 1-.45 1-1v-8l-2.08-5.99zM6.5 16c-.83 0-1.5-.67-1.5-1.5S5.67 13 6.5 13s1.5.67 1.5 1.5S7.33 16 6.5 16zm11 0c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zM5 11l1.5-4.5h11L19 11H5z"/></svg>
|
|
Delivery (Envío a domicilio)
|
|
</div>
|
|
|
|
<div class="toggle-row">
|
|
<div class="toggle ${s.delivery_enabled ? "active" : ""}" id="deliveryToggle"></div>
|
|
<span class="toggle-label">Delivery habilitado</span>
|
|
</div>
|
|
|
|
<div class="schedule-grid" id="deliverySchedule">
|
|
${this.renderScheduleGrid("delivery", s.delivery_enabled)}
|
|
</div>
|
|
|
|
<div class="min-order-field">
|
|
<div class="field">
|
|
<label>Pedido mínimo para delivery ($)</label>
|
|
<input type="number" id="deliveryMinOrder" value="${s.delivery_min_order || 0}" min="0" step="100" style="width:150px;" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Retiro en tienda -->
|
|
<div class="panel">
|
|
<div class="panel-title">
|
|
<svg viewBox="0 0 24 24"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-7 14l-5-5 1.41-1.41L12 14.17l4.59-4.58L18 11l-6 6z"/></svg>
|
|
Retiro en Tienda
|
|
</div>
|
|
|
|
<div class="toggle-row">
|
|
<div class="toggle ${s.pickup_enabled ? "active" : ""}" id="pickupToggle"></div>
|
|
<span class="toggle-label">Retiro en tienda habilitado</span>
|
|
</div>
|
|
|
|
<div class="schedule-grid" id="pickupSchedule">
|
|
${this.renderScheduleGrid("pickup", s.pickup_enabled)}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Zonas de Entrega -->
|
|
<div class="panel">
|
|
<div class="panel-title">
|
|
<svg viewBox="0 0 24 24"><path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z"/></svg>
|
|
Zonas de Entrega (Barrios CABA)
|
|
</div>
|
|
|
|
<div class="zones-search">
|
|
<input type="text" id="zoneSearch" placeholder="Buscar barrio..." />
|
|
</div>
|
|
|
|
<div class="zones-list" id="zonesList">
|
|
${this.renderZonesList()}
|
|
</div>
|
|
|
|
${this.renderZonesSummary()}
|
|
</div>
|
|
|
|
<div class="actions">
|
|
<button id="saveBtn" ${this.saving ? "disabled" : ""}>${this.saving ? "Guardando..." : "Guardar Configuración"}</button>
|
|
<button id="resetBtn" class="secondary">Restaurar</button>
|
|
</div>
|
|
`;
|
|
|
|
this.setupEventListeners();
|
|
}
|
|
|
|
setupEventListeners() {
|
|
// Toggle delivery
|
|
const deliveryToggle = this.shadowRoot.getElementById("deliveryToggle");
|
|
deliveryToggle?.addEventListener("click", () => {
|
|
this.settings.delivery_enabled = !this.settings.delivery_enabled;
|
|
this.render();
|
|
});
|
|
|
|
// Toggle pickup
|
|
const pickupToggle = this.shadowRoot.getElementById("pickupToggle");
|
|
pickupToggle?.addEventListener("click", () => {
|
|
this.settings.pickup_enabled = !this.settings.pickup_enabled;
|
|
this.render();
|
|
});
|
|
|
|
// Day toggles
|
|
this.shadowRoot.querySelectorAll(".day-toggle").forEach(toggle => {
|
|
toggle.addEventListener("click", () => {
|
|
const type = toggle.dataset.type;
|
|
const day = toggle.dataset.day;
|
|
const currentSlot = this.getScheduleSlot(type, day);
|
|
|
|
if (currentSlot) {
|
|
// Desactivar día
|
|
this.setScheduleSlot(type, day, null);
|
|
} else {
|
|
// Activar día con horarios default
|
|
const defaultStart = type === "delivery" ? "09:00" : "08:00";
|
|
const defaultEnd = type === "delivery" ? "18:00" : "20:00";
|
|
this.setScheduleSlot(type, day, { start: defaultStart, end: defaultEnd });
|
|
}
|
|
this.render();
|
|
});
|
|
});
|
|
|
|
// Hour inputs - update on blur
|
|
this.shadowRoot.querySelectorAll(".hour-start, .hour-end").forEach(input => {
|
|
input.addEventListener("blur", () => {
|
|
const type = input.dataset.type;
|
|
const day = input.dataset.day;
|
|
const isStart = input.classList.contains("hour-start");
|
|
|
|
const slot = this.getScheduleSlot(type, day);
|
|
if (!slot) return;
|
|
|
|
const value = input.value.trim();
|
|
if (isStart) {
|
|
slot.start = value || (type === "delivery" ? "09:00" : "08:00");
|
|
} else {
|
|
slot.end = value || (type === "delivery" ? "18:00" : "20:00");
|
|
}
|
|
this.setScheduleSlot(type, day, slot);
|
|
});
|
|
});
|
|
|
|
// Save button
|
|
this.shadowRoot.getElementById("saveBtn")?.addEventListener("click", () => this.save());
|
|
|
|
// Reset button
|
|
this.shadowRoot.getElementById("resetBtn")?.addEventListener("click", () => this.load());
|
|
|
|
// Zone search
|
|
this.shadowRoot.getElementById("zoneSearch")?.addEventListener("input", (e) => {
|
|
const query = e.target.value.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
|
|
this.shadowRoot.querySelectorAll(".zone-row").forEach(row => {
|
|
const barrio = row.dataset.barrio;
|
|
const barrioName = CABA_BARRIOS.find(b => this.barrioToKey(b) === barrio) || "";
|
|
const normalized = barrioName.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
|
|
row.classList.toggle("hidden", query && !normalized.includes(query));
|
|
});
|
|
});
|
|
|
|
// Zone toggles
|
|
this.shadowRoot.querySelectorAll(".zone-toggle").forEach(toggle => {
|
|
toggle.addEventListener("click", () => {
|
|
const barrio = toggle.dataset.barrio;
|
|
const config = this.getZoneConfig(barrio);
|
|
|
|
if (config?.enabled) {
|
|
// Desactivar zona
|
|
this.setZoneConfig(barrio, null);
|
|
} else {
|
|
// Activar zona con días default (lun-sab)
|
|
this.setZoneConfig(barrio, {
|
|
enabled: true,
|
|
days: ["lun", "mar", "mie", "jue", "vie", "sab"],
|
|
delivery_cost: 0
|
|
});
|
|
}
|
|
this.render();
|
|
});
|
|
});
|
|
|
|
// Zone day toggles
|
|
this.shadowRoot.querySelectorAll(".zone-day").forEach(dayBtn => {
|
|
dayBtn.addEventListener("click", () => {
|
|
const barrio = dayBtn.dataset.barrio;
|
|
const day = dayBtn.dataset.day;
|
|
const config = this.getZoneConfig(barrio);
|
|
if (!config) return;
|
|
|
|
const days = config.days || [];
|
|
const idx = days.indexOf(day);
|
|
if (idx >= 0) {
|
|
days.splice(idx, 1);
|
|
} else {
|
|
days.push(day);
|
|
}
|
|
config.days = days;
|
|
this.setZoneConfig(barrio, config);
|
|
|
|
// Update UI without full re-render
|
|
dayBtn.classList.toggle("active", days.includes(day));
|
|
});
|
|
});
|
|
|
|
// Zone cost inputs
|
|
this.shadowRoot.querySelectorAll(".zone-cost-input").forEach(input => {
|
|
input.addEventListener("change", () => {
|
|
const barrio = input.dataset.barrio;
|
|
const config = this.getZoneConfig(barrio);
|
|
if (!config) return;
|
|
|
|
config.delivery_cost = parseFloat(input.value) || 0;
|
|
this.setZoneConfig(barrio, config);
|
|
});
|
|
});
|
|
}
|
|
|
|
collectScheduleFromInputs() {
|
|
const schedule = { delivery: {}, pickup: {} };
|
|
|
|
for (const type of ["delivery", "pickup"]) {
|
|
this.shadowRoot.querySelectorAll(`.hour-start[data-type="${type}"]`).forEach(input => {
|
|
const day = input.dataset.day;
|
|
const endInput = this.shadowRoot.querySelector(`.hour-end[data-type="${type}"][data-day="${day}"]`);
|
|
const toggle = this.shadowRoot.querySelector(`.day-toggle[data-type="${type}"][data-day="${day}"]`);
|
|
|
|
if (toggle?.classList.contains("active")) {
|
|
schedule[type][day] = {
|
|
start: input.value.trim() || (type === "delivery" ? "09:00" : "08:00"),
|
|
end: endInput?.value.trim() || (type === "delivery" ? "18:00" : "20:00"),
|
|
};
|
|
}
|
|
});
|
|
}
|
|
|
|
return schedule;
|
|
}
|
|
|
|
async save() {
|
|
// Collect schedule from inputs
|
|
const schedule = this.collectScheduleFromInputs();
|
|
|
|
// Collect delivery zones (already in settings from event handlers)
|
|
const delivery_zones = this.settings.delivery_zones || {};
|
|
|
|
const data = {
|
|
store_name: this.shadowRoot.getElementById("storeName")?.value || "",
|
|
bot_name: this.shadowRoot.getElementById("botName")?.value || "",
|
|
store_address: this.shadowRoot.getElementById("storeAddress")?.value || "",
|
|
store_phone: this.shadowRoot.getElementById("storePhone")?.value || "",
|
|
delivery_enabled: this.settings.delivery_enabled,
|
|
pickup_enabled: this.settings.pickup_enabled,
|
|
delivery_min_order: parseFloat(this.shadowRoot.getElementById("deliveryMinOrder")?.value) || 0,
|
|
schedule,
|
|
delivery_zones,
|
|
};
|
|
|
|
// Update settings with form values
|
|
this.settings = { ...this.settings, ...data };
|
|
|
|
this.saving = true;
|
|
this.render();
|
|
|
|
try {
|
|
console.log("[settings-crud] Saving:", data);
|
|
const result = await api.saveSettings(data);
|
|
console.log("[settings-crud] Save result:", result);
|
|
|
|
if (result.ok === false) {
|
|
throw new Error(result.message || result.error || "Error desconocido");
|
|
}
|
|
|
|
this.settings = result.settings || data;
|
|
this.saving = false;
|
|
this.showSuccess(result.message || "Configuración guardada correctamente");
|
|
this.render();
|
|
} catch (e) {
|
|
console.error("[settings-crud] Error saving settings:", e);
|
|
this.saving = false;
|
|
this.showError("Error guardando: " + (e.message || e));
|
|
this.render();
|
|
}
|
|
}
|
|
|
|
escapeHtml(str) {
|
|
return (str || "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
}
|
|
|
|
showSuccess(msg) {
|
|
const messages = this.shadowRoot.getElementById("messages");
|
|
messages.innerHTML = `<div class="success-msg">${msg}</div>`;
|
|
setTimeout(() => { messages.innerHTML = ""; }, 4000);
|
|
}
|
|
|
|
showError(msg) {
|
|
const messages = this.shadowRoot.getElementById("messages");
|
|
messages.innerHTML = `<div class="error-msg">${msg}</div>`;
|
|
setTimeout(() => { messages.innerHTML = ""; }, 5000);
|
|
}
|
|
}
|
|
|
|
customElements.define("settings-crud", SettingsCrud);
|