PAGES.login = { Esconder: true, Title: "Login", onboarding: function (step) { // Multi-step onboarding flow step = step || 'config'; if (step === 'config') { // Step 1: "Configuración de datos" var field_couch = safeuuid(); var field_couch_dbname = safeuuid(); var field_couch_user = safeuuid(); var field_couch_pass = safeuuid(); var field_secret = safeuuid(); var btn_existing_server = safeuuid(); var btn_new_server = safeuuid(); var btn_skip = safeuuid(); var div_server_config = safeuuid(); container.innerHTML = `

¡Bienvenido a TeleSec! 🎉

Paso 1: Configuración de datos

Para comenzar, elige cómo quieres configurar tu base de datos:

`; document.getElementById(btn_existing_server).onclick = () => { document.getElementById(div_server_config).style.display = 'block'; }; document.getElementById(btn_new_server).onclick = () => { window.open('https://tech.eus/telesec-signup.php', '_blank'); toastr.info('Una vez creado el servidor, vuelve aquí y conéctate usando el botón "Conectar a un servidor existente"'); }; document.getElementById(btn_skip).onclick = () => { // Continue to persona creation without server config open_page('login,onboarding-persona'); setUrlHash('login,onboarding-persona'); }; document.getElementById(btn_skip + '-save').onclick = () => { var url = document.getElementById(field_couch).value.trim(); var dbname = document.getElementById(field_couch_dbname).value.trim(); var user = document.getElementById(field_couch_user).value.trim(); var pass = document.getElementById(field_couch_pass).value; var secret = document.getElementById(field_secret).value || ''; if (!url) { toastr.error('Por favor ingresa un servidor CouchDB'); return; } // Normalize URL: add https:// if no protocol specified var normalizedUrl = url; if (!/^https?:\/\//i.test(url)) { normalizedUrl = 'https://' + url; } localStorage.setItem('TELESEC_COUCH_URL', normalizedUrl); localStorage.setItem('TELESEC_COUCH_DBNAME', dbname); localStorage.setItem('TELESEC_COUCH_USER', user); localStorage.setItem('TELESEC_COUCH_PASS', pass); if (secret) { localStorage.setItem('TELESEC_SECRET', secret.toUpperCase()); SECRET = secret.toUpperCase(); } try { DB.init({ secret: SECRET, remoteServer: normalizedUrl, username: user, password: pass, dbname: dbname || undefined }); toastr.success('Servidor configurado correctamente'); // Continue to persona creation setTimeout(() => { open_page('login,onboarding-persona'); setUrlHash('login,onboarding-persona'); }, 500); } catch (e) { toastr.error('Error al configurar el servidor: ' + (e.message || e)); } }; } else if (step === 'persona') { // Step 2: "Crea una persona" var field_nombre = safeuuid(); var btn_crear = safeuuid(); container.innerHTML = `

¡Bienvenido a TeleSec! 🎉

Paso 2: Crea tu cuenta de administrador

Para continuar, necesitas crear una cuenta personal con permisos de administrador.

ℹ️ Esta cuenta tendrá todos los permisos de administrador y podrás gestionar la aplicación completamente.

`; document.getElementById(btn_crear).onclick = () => { var nombre = document.getElementById(field_nombre).value.trim(); if (!nombre) { toastr.error('Por favor ingresa tu nombre'); return; } // Create persona with all admin permissions from PERMS object var allPerms = Object.keys(PERMS).join(',') + ','; var personaId = safeuuid('admin-'); var persona = { Nombre: nombre, Roles: allPerms, Region: '', Monedero_Balance: 0, markdown: 'Cuenta de administrador creada durante el onboarding' }; DB.put('personas', personaId, persona).then(() => { toastr.success('¡Cuenta creada exitosamente! 🎉'); localStorage.setItem('TELESEC_ONBOARDING_COMPLETE', 'true'); localStorage.setItem('TELESEC_ADMIN_ID', personaId); // Auto-login SUB_LOGGED_IN_ID = personaId; SUB_LOGGED_IN_DETAILS = persona; SUB_LOGGED_IN = true; SetPages(); setTimeout(() => { open_page('index'); setUrlHash('index'); }, 500); }).catch((e) => { toastr.error('Error creando cuenta: ' + (e.message || e)); }); }; } }, edit: function (mid) { // Handle onboarding routes if (mid === 'onboarding-config') { PAGES.login.onboarding('config'); return; } if (mid === 'onboarding-persona') { PAGES.login.onboarding('persona'); return; } // Setup form to configure CouchDB remote and initial group/secret var field_couch = safeuuid(); var field_couch_dbname = safeuuid(); var field_couch_user = safeuuid(); var field_couch_pass = safeuuid(); var field_secret = safeuuid(); var btn_import_json = safeuuid(); var div_import_area = safeuuid(); var field_json = safeuuid(); var field_file = safeuuid(); var btn_parse_json = safeuuid(); var btn_start_scan = safeuuid(); var div_scan = safeuuid(); var btn_save = safeuuid(); container.innerHTML = `

Configuración del servidor CouchDB

Aviso: Después de guardar, la aplicación intentará sincronizar con el servidor CouchDB en segundo plano. Puede que falten registros hasta que se termine. Tenga paciencia.

Después de guardar, el navegador intentará sincronizar en segundo plano con el servidor.

`; // Helper: normalize and apply config object function applyConfig(cfg) { try { if (!cfg) throw new Error('JSON vacío'); var url = cfg.server || cfg.couch || cfg.url || cfg.host || cfg.hostname || cfg.server_url; var dbname = cfg.dbname || cfg.database || cfg.db || cfg.name; var user = cfg.username || cfg.user || cfg.u; var pass = cfg.password || cfg.pass || cfg.p; var secret = (cfg.secret || cfg.key || cfg.secretKey || cfg.SECRET || '').toString(); if (!url) throw new Error('Falta campo "server" en JSON'); localStorage.setItem('TELESEC_COUCH_URL', 'https://' + url.replace(/^https?:\/\//, '')); if (dbname) localStorage.setItem('TELESEC_COUCH_DBNAME', dbname); if (user) localStorage.setItem('TELESEC_COUCH_USER', user); if (pass) localStorage.setItem('TELESEC_COUCH_PASS', pass); if (secret) { localStorage.setItem('TELESEC_SECRET', secret.toUpperCase()); SECRET = secret.toUpperCase(); } DB.init({ secret: SECRET, remoteServer: 'https://' + url.replace(/^https?:\/\//, ''), username: user, password: pass, dbname: dbname || undefined }); toastr.success('Configuración aplicada e iniciando sincronización'); location.hash = '#login'; setTimeout(function(){ location.reload(); }, 400); } catch (e) { toastr.error('Error aplicando configuración: ' + (e && e.message ? e.message : e)); } } // Toggle import area document.getElementById(btn_import_json).onclick = function () { var el = document.getElementById(div_import_area); el.style.display = (el.style.display === 'none') ? 'block' : 'none'; }; // Parse textarea JSON document.getElementById(btn_parse_json).onclick = function () { var txt = document.getElementById(field_json).value.trim(); if (!txt) { toastr.error('JSON vacío'); return; } try { var obj = JSON.parse(txt); applyConfig(obj); } catch (e) { toastr.error('JSON inválido: ' + e.message); } }; // File input: read JSON file and apply document.getElementById(field_file).addEventListener('change', function (ev) { var f = ev.target.files && ev.target.files[0]; if (!f) return; var r = new FileReader(); r.onload = function (e) { try { var txt = e.target.result; document.getElementById(field_json).value = txt; var obj = JSON.parse(txt); applyConfig(obj); } catch (err) { toastr.error('Error leyendo archivo JSON: ' + (err && err.message ? err.message : err)); } }; r.readAsText(f); }); // QR scanning (if html5-qrcode available) document.getElementById(btn_start_scan).onclick = function () { var scanDiv = document.getElementById(div_scan); scanDiv.innerHTML = ''; if (window.Html5QrcodeScanner || window.Html5Qrcode) { try { var targetId = div_scan + '-cam'; scanDiv.innerHTML = '
'; var html5Qr; if (window.Html5Qrcode) { html5Qr = new Html5Qrcode(targetId); Html5Qrcode.getCameras().then(function(cameras){ var camId = (cameras && cameras[0] && cameras[0].id) ? cameras[0].id : undefined; html5Qr.start({ facingMode: 'environment' }, { fps: 10, qrbox: 250 }, function(decodedText){ try { var obj = JSON.parse(decodedText); html5Qr.stop(); applyConfig(obj); } catch (e) { toastr.error('QR no contiene JSON válido'); } }, function(err){ /* ignore scan errors */ }).catch(function(err){ toastr.error('Error iniciando cámara: ' + err); }); }).catch(function(){ // fallback: start without camera list html5Qr.start({ facingMode: 'environment' }, { fps: 10, qrbox: 250 }, function(decodedText){ try { applyConfig(JSON.parse(decodedText)); } catch(e){ toastr.error('QR no contiene JSON válido'); } }, function(){}).catch(function(err){ toastr.error('Error iniciando cámara: ' + (err && err.message ? err.message : err)); }); }); } else { // Html5QrcodeScanner fallback var scanner = new Html5QrcodeScanner(targetId, { fps: 10, qrbox: 250 }); scanner.render(function(decodedText){ try { applyConfig(JSON.parse(decodedText)); scanner.clear(); } catch(e){ toastr.error('QR no contiene JSON válido'); } }); } // stop button document.getElementById(targetId + '-stop').onclick = function () { if (html5Qr && html5Qr.getState && html5Qr.getState() === Html5Qrcode.ScanStatus.SCANNING) { html5Qr.stop().catch(function(){}); } scanDiv.innerHTML = ''; }; } catch (e) { toastr.error('Error al iniciar escáner: ' + (e && e.message ? e.message : e)); } } else { scanDiv.innerHTML = '

Escáner no disponible. Copia/pega el JSON o sube un archivo.

'; } }; document.getElementById(btn_save).onclick = () => { var url = document.getElementById(field_couch).value.trim(); var dbname = document.getElementById(field_couch_dbname).value.trim(); var user = document.getElementById(field_couch_user).value.trim(); var pass = document.getElementById(field_couch_pass).value; var secret = document.getElementById(field_secret).value || ''; localStorage.setItem('TELESEC_COUCH_URL', "https://" + url); localStorage.setItem('TELESEC_COUCH_DBNAME', dbname); localStorage.setItem('TELESEC_COUCH_USER', user); localStorage.setItem('TELESEC_COUCH_PASS', pass); localStorage.setItem('TELESEC_SECRET', secret.toUpperCase()); SECRET = secret.toUpperCase(); try { DB.init({ secret: SECRET, remoteServer: "https://" + url, username: user, password: pass, dbname: dbname || undefined }); toastr.success('Iniciando sincronización con CouchDB'); location.hash = "#login"; location.reload(); } catch (e) { toastr.error('Error al iniciar sincronización: ' + e.message); } }; }, index: function (mid) { // Check if onboarding is needed var onboardingComplete = localStorage.getItem('TELESEC_ONBOARDING_COMPLETE'); var hasPersonas = Object.keys(SC_Personas).length > 0; // If no personas exist and onboarding not complete, redirect to onboarding if (!hasPersonas && !onboardingComplete && !AC_BYPASS) { open_page('login,onboarding-config'); setUrlHash('login,onboarding-config'); return; } var field_persona = safeuuid(); var btn_guardar = safeuuid(); var btn_reload = safeuuid(); var div_actions = safeuuid(); container.innerHTML = `

Iniciar sesión

Valores
Configurar base de datos
`; var divact = document.getElementById(div_actions); addCategory_Personas( divact, SC_Personas, "", (value) => { document.getElementById(field_persona).value = value; }, "¿Quién eres?", true, "- Pulsa recargar o rellena los credenciales abajo, si quieres crear un nuevo grupo, pulsa el boton 'Desde cero' -" ); document.getElementById(btn_guardar).onclick = () => { if (document.getElementById(field_persona).value == "") { alert("Tienes que elegir tu cuenta!"); return; } SUB_LOGGED_IN_ID = document.getElementById(field_persona).value SUB_LOGGED_IN_DETAILS = SC_Personas[SUB_LOGGED_IN_ID] SUB_LOGGED_IN = true SetPages() if (location.hash.replace("#", "").startsWith("login")) { open_page("index"); setUrlHash("index") } else{ open_page(location.hash.replace("#", "")); } }; document.getElementById(btn_reload).onclick = () => { open_page("login") }; // AC_BYPASS: allow creating a local persona from the login screen if (AC_BYPASS) { var btn_bypass_create = safeuuid(); divact.innerHTML += ``; document.getElementById(btn_bypass_create).onclick = () => { var name = prompt("Nombre de la persona (ej: Admin):"); if (!name) return; var id = 'bypass-' + Date.now(); var persona = { Nombre: name, Roles: 'ADMIN,' }; DB.put('personas', id, persona).then(() => { toastr.success('Persona creada: ' + id); localStorage.setItem('TELESEC_BYPASS_ID', id); SUB_LOGGED_IN_ID = id; SUB_LOGGED_IN_DETAILS = persona; SUB_LOGGED_IN = true; SetPages(); open_page('index'); }).catch((e) => { toastr.error('Error creando persona: ' + (e && e.message ? e.message : e)); }); }; } } }