/* Shared configuration form. Persistence is supplied by the desktop/web adapter. */ const ConfigUI = (() => { let original, baseline = '', saving = false; const form = () => document.getElementById('config-form'); const values = () => Object.fromEntries(Object.keys(ConfiguracionModelo.fields).map(id => [id, document.getElementById(id)?.value ?? ''])); const snapshot = () => JSON.stringify(values()); const dirty = () => !!form() && baseline !== snapshot(); function message(text, error = false) { const status = document.getElementById('config-status'); if (status) { status.textContent = text; status.className = error ? 'text-danger' : 'text-white'; } } function canLeave() { if (saving) { message('Esperá a que termine el guardado.'); return false; } if (!dirty()) return true; if (!window.confirm('Tenés cambios sin guardar. ¿Querés descartarlos y salir?')) return false; baseline = snapshot(); return true; } function visibility() { const v = values(); for (const [id, visible] of Object.entries({bloque_secundario: v.Modo !== 'Recolector_USB', LlaveR: v.Modo === 'Visor', bloque_por_IP_WIFI: v.Modo === 'Recolector_IP', vocesAudioDiv: v.audio === 'Activado', Server: v.Tipo_SMTP === 'Manual', puerto: v.Tipo_SMTP === 'Manual'})) { const element = document.getElementById(id); if (element) element.hidden = !visible; } for (const id of ['Usuario_SMTP', 'Contrasenia_SMTP']) document.getElementById(id).disabled = v.Tipo_SMTP === 'Deshabilitado' || saving; for (const id of ['Server_SMTP', 'Puerto_SMTP']) document.getElementById(id).disabled = v.Tipo_SMTP !== 'Manual' || saving; } function errors(fields) { for (const id of Object.keys(ConfiguracionModelo.fields)) { const element = document.getElementById(id); if (!element) continue; element.classList.toggle('is-invalid', !!fields[id]); element.setAttribute('aria-invalid', String(!!fields[id])); document.getElementById(id + '-error').textContent = fields[id] || ''; } const first = Object.keys(fields)[0]; if (first) document.getElementById(first)?.focus(); } function mount(config, interfaces = {}, voices = []) { original = config; const f = form(); if (!f) return; const selected = ConfiguracionModelo.read(config); const ip = document.getElementById('r_ip'); ip.replaceChildren(); const addresses = new Set(Object.values(interfaces).flat().filter(i => i.family === 'IPv4' && !i.internal).map(i => i.address)); if (selected.r_ip && selected.r_ip !== 'false') addresses.add(selected.r_ip); for (const value of addresses) ip.add(new Option(value, value)); if (!ip.options.length) ip.add(new Option('Sin interfaces disponibles', '')); const voice = document.getElementById('vocesAudio'); voice.replaceChildren(); const names = new Set(voices.map(v => v.name)); if (selected.vocesAudio) names.add(selected.vocesAudio); for (const value of names) voice.add(new Option(value, value)); if (!voice.options.length) voice.add(new Option('Sin voces disponibles', '')); for (const [id, value] of Object.entries(selected)) { const element = document.getElementById(id); if (element.tagName === 'SELECT' && value && ![...element.options].some(o => o.value === value)) element.add(new Option(value, value)); element.value = value; element.setAttribute('aria-describedby', id + '-error'); if (!document.getElementById(id + '-error')) { const feedback = document.createElement('div'); feedback.id = id + '-error'; feedback.className = 'invalid-feedback'; element.after(feedback); } } visibility(); baseline = snapshot(); errors({}); message('Sin cambios pendientes.'); f.oninput = f.onchange = () => { visibility(); message(dirty() ? 'Cambios sin guardar.' : 'Sin cambios pendientes.'); }; f.onsubmit = event => { event.preventDefault(); GuardarConf('conf'); }; } async function save(persist) { if (saving || !form()) return false; const data = values(); const invalid = ConfiguracionModelo.validate(data); errors(invalid); if (Object.keys(invalid).length) { message('Revisá los campos marcados.', true); return false; } const draft = ConfiguracionModelo.build(original, data); saving = true; const controls = [...form().querySelectorAll('input, select, button')]; const disabled = controls.map(e => e.disabled); controls.forEach(e => { e.disabled = true; }); message('Guardando…'); try { await persist(draft); original = draft; baseline = snapshot(); message('Configuración guardada.'); return draft; } catch (error) { if (error.fields) errors(error.fields); message(error.message || 'No se pudo guardar. Tus cambios siguen en el formulario.', true); return false; } finally { saving = false; controls.forEach((e, i) => { e.disabled = disabled[i]; }); visibility(); } } window.addEventListener('click', event => { if (event.target.closest('[data-bs-toggle="tab"], #Wclo') && !canLeave()) { event.preventDefault(); event.stopImmediatePropagation(); } }, true); window.addEventListener('beforeunload', event => { if (dirty() || saving) { event.preventDefault(); event.returnValue = ''; } }); return {mount, save, dirty, canLeave, visibility}; })();