Consultas de clientes por todos los canales
en el sitio web del cliente."; hint.style.cssText = "font-size:13px;color:#555;margin:0 0 12px 0;"; var textarea = document.createElement("textarea"); textarea.value = code; textarea.readOnly = true; textarea.style.cssText = "flex:1;min-height:280px;font-family:monospace;font-size:12px;padding:10px;"; var btnRow = document.createElement("div"); btnRow.style.cssText = "margin-top:14px;display:flex;gap:10px;"; var copyBtn = document.createElement("button"); copyBtn.textContent = "Copiar al portapapeles"; copyBtn.style.cssText = "padding:8px 20px;"; copyBtn.onclick = function () { textarea.select(); navigator.clipboard.writeText(code).then(function () { copyBtn.textContent = "Copiado!"; setTimeout(function () { copyBtn.textContent = "Copiar al portapapeles"; }, 1500); }).catch(function () { document.execCommand("copy"); }); }; var closeBtn = document.createElement("button"); closeBtn.textContent = "Cerrar"; closeBtn.style.cssText = "padding:8px 20px;"; closeBtn.onclick = function () { document.body.removeChild(overlay); }; btnRow.appendChild(copyBtn); btnRow.appendChild(closeBtn); box.appendChild(title); box.appendChild(hint); box.appendChild(textarea); box.appendChild(btnRow); overlay.appendChild(box); overlay.onclick = function (e) { if (e.target === overlay) document.body.removeChild(overlay); }; document.body.appendChild(overlay); } function showWhatsappQr(base64, companyName) { var overlay = document.createElement("div"); overlay.style.cssText = "position:fixed;inset:0;background:rgba(0,0,0,0.6);display:flex;align-items:center;justify-content:center;z-index:1000;"; var box = document.createElement("div"); box.style.cssText = "background:#fff;border-radius:8px;padding:24px;text-align:center;max-width:360px;"; var title = document.createElement("h3"); title.textContent = "Vincular WhatsApp - " + companyName; title.style.cssText = "margin:0 0 12px 0;"; var hint = document.createElement("p"); hint.textContent = "Escanea con WhatsApp > Dispositivos vinculados > Vincular un dispositivo. Caduca en poco tiempo."; hint.style.cssText = "font-size:13px;color:#555;margin:0 0 12px 0;"; var img = document.createElement("img"); img.src = base64; img.style.cssText = "width:280px;height:280px;"; var closeBtn = document.createElement("button"); closeBtn.textContent = "Cerrar"; closeBtn.style.cssText = "margin-top:14px;padding:8px 20px;"; closeBtn.onclick = function () { document.body.removeChild(overlay); }; box.appendChild(title); box.appendChild(hint); box.appendChild(img); box.appendChild(document.createElement("br")); box.appendChild(closeBtn); overlay.appendChild(box); overlay.onclick = function (e) { if (e.target === overlay) document.body.removeChild(overlay); }; document.body.appendChild(overlay); } function editCompany(id) { var c = companiesCache.filter(function (x) { return x.id === id; })[0]; if (!c) return; document.getElementById("company-id").value = c.id; Object.keys(companyFieldMap).forEach(function (formKey) { document.getElementById("company-" + formKey).value = c[companyFieldMap[formKey]] || ""; }); document.getElementById("company-slug").disabled = true; document.getElementById("company-form-title").textContent = "Editar: " + c.name; document.getElementById("company-submit").textContent = "Guardar cambios"; document.getElementById("company-cancel").style.display = "inline-block"; document.getElementById("company-status").textContent = ""; } document.getElementById("company-form").addEventListener("submit", function (e) { e.preventDefault(); var id = document.getElementById("company-id").value; var status = document.getElementById("company-status"); var btn = document.getElementById("company-submit"); var payload = {}; Object.keys(companyFieldMap).forEach(function (formKey) { payload[companyFieldMap[formKey]] = document.getElementById("company-" + formKey).value.trim() || null; }); btn.disabled = true; status.textContent = "Guardando..."; status.className = ""; var request; if (id) { delete payload.slug; request = authFetchJson("/api/companies/" + id, "PUT", payload); } else { if (!payload.slug || !payload.name) { status.textContent = "Slug y nombre son obligatorios."; status.className = "err"; btn.disabled = false; return; } request = authFetchJson("/api/companies", "POST", payload); } request.then(function () { status.textContent = "Guardado correctamente."; status.className = "ok"; clearCompanyForm(); loadCompanies(); }).catch(function (err) { status.textContent = err.message || "Hubo un error."; status.className = "err"; }).finally(function () { btn.disabled = false; }); }); var usersCache = []; function populateUserCompanySelect() { var select = document.getElementById("user-company"); select.innerHTML = ""; companiesCache.forEach(function (c) { var opt = document.createElement("option"); opt.value = c.id; opt.textContent = c.name; select.appendChild(opt); }); } function clearUserForm() { document.getElementById("user-id").value = ""; document.getElementById("user-username").value = ""; document.getElementById("user-username").disabled = false; document.getElementById("user-password").value = ""; document.getElementById("user-password-label").textContent = "Contrasena"; document.getElementById("user-superadmin").checked = false; document.getElementById("user-form-title").textContent = "Nuevo usuario"; document.getElementById("user-submit").textContent = "Crear usuario"; document.getElementById("user-cancel").style.display = "none"; document.getElementById("user-status").textContent = ""; } document.getElementById("user-cancel").onclick = clearUserForm; function loadUsers() { var list = document.getElementById("users-list"); list.innerHTML = "Cargando..."; var companiesReq = companiesCache.length ? Promise.resolve(companiesCache) : authFetch("/api/companies"); companiesReq.then(function (companies) { companiesCache = companies; populateUserCompanySelect(); return authFetch("/api/users"); }).then(function (users) { usersCache = users; if (users.length === 0) { list.innerHTML = '
'; return; } list.innerHTML = ""; users.forEach(function (u) { var item = document.createElement("div"); item.className = "admin-item"; item.innerHTML = '
'; item.querySelector(".admin-edit-btn").onclick = function () { editUser(u.id); }; item.querySelector(".admin-resetmfa-btn").onclick = function () { if (!confirm('¿Resetear el MFA de "' + u.username + '"? Tendra que volver a inscribirlo en su proximo ingreso.')) return; authFetchJson("/api/users/" + u.id + "/reset-mfa", "POST").then(function () { alert("MFA reseteado correctamente."); }).catch(function (err) { alert(err.message || "No se pudo resetear."); }); }; item.querySelector(".admin-delete-btn").onclick = function () { if (!confirm('¿Eliminar el usuario "' + u.username + '"?')) return; authFetchJson("/api/users/" + u.id, "DELETE").then(function () { loadUsers(); }).catch(function (err) { alert(err.message || "No se pudo eliminar."); }); }; list.appendChild(item); }); }); } function editUser(id) { var u = usersCache.filter(function (x) { return x.id === id; })[0]; if (!u) return; document.getElementById("user-id").value = u.id; document.getElementById("user-username").value = u.username; document.getElementById("user-username").disabled = true; document.getElementById("user-password").value = ""; document.getElementById("user-password-label").textContent = "Nueva contrasena (dejar vacio para no cambiar)"; document.getElementById("user-company").value = u.company_id; document.getElementById("user-superadmin").checked = !!u.is_superadmin; document.getElementById("user-form-title").textContent = "Editar: " + u.username; document.getElementById("user-submit").textContent = "Guardar cambios"; document.getElementById("user-cancel").style.display = "inline-block"; document.getElementById("user-status").textContent = ""; } document.getElementById("user-form").addEventListener("submit", function (e) { e.preventDefault(); var id = document.getElementById("user-id").value; var status = document.getElementById("user-status"); var btn = document.getElementById("user-submit"); var username = document.getElementById("user-username").value.trim(); var password = document.getElementById("user-password").value; var companyId = parseInt(document.getElementById("user-company").value, 10); var superadmin = document.getElementById("user-superadmin").checked; btn.disabled = true; status.textContent = "Guardando..."; status.className = ""; var request; if (id) { var payload = { company_id: companyId, is_superadmin: superadmin }; if (password) payload.password = password; request = authFetchJson("/api/users/" + id, "PUT", payload); } else { if (!username || !password) { status.textContent = "Usuario y contrasena son obligatorios."; status.className = "err"; btn.disabled = false; return; } request = authFetchJson("/api/users", "POST", { username: username, password: password, company_id: companyId, is_superadmin: superadmin }); } request.then(function () { status.textContent = "Guardado correctamente."; status.className = "ok"; clearUserForm(); loadUsers(); }).catch(function (err) { status.textContent = err.message || "Hubo un error."; status.className = "err"; }).finally(function () { btn.disabled = false; }); }); if (token) { showApp(); } else { showLogin(); }
Subir imagenes
Sube hasta 20 imagenes; la IA analiza cada una y redacta un borrador de publicacion. Nada se publica sin tu aprobacion.
Publicaciones pendientes de revision