Carpincho/src/gui_common/js/configuracion.js
Exequiel Aramburu 99264aba3d Se adecuó el UI
2026-09-27 23:05:15 -03:00

102 lines
5.3 KiB
JavaScript

/* 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};
})();