/** * hamlib_ui.js — VIRTUAL RADIO panel (Hamlib NET rigctl emulation, SETTINGS) * * Visible to everyone (so the user knows which ports to connect their * program to), but enabling/disabling TCP ports and saving the config — * ADMIN ONLY. Toggling these servers affects all users at once (shared * port), so not everyone with access to the panel can do it — for other * roles it's a plain status view. */ (function () { 'use strict'; async function load() { const box = document.getElementById('hamlib-slots'); if (!box) return; box.innerHTML = `
${I18n.t('settings_loading')}
`; try { const r = await fetch('/api/hamlib/status'); const d = await r.json(); _render(d.servers || []); } catch (e) { box.innerHTML = `
${I18n.t('hamlib_load_error')}
`; } } function _render(servers) { const box = document.getElementById('hamlib-slots'); if (!box) return; const isAdmin = window.AppState?.role === 'admin'; const saveBtn = document.getElementById('hamlib-save-btn'); if (saveBtn) saveBtn.style.display = isAdmin ? '' : 'none'; if (!servers.length) { box.innerHTML = `
${I18n.t('hamlib_no_ports')}
`; return; } box.innerHTML = servers.map((s, i) => { const clientWord = s.clients === 1 ? I18n.t('hamlib_client_singular') : I18n.t('hamlib_client_plural'); const running = s.running ? `● ${I18n.t('hamlib_active')} · ${s.clients || 0} ${clientWord}` : `${I18n.t('hamlib_disabled')}`; if (isAdmin) { return `
${running}
`; } return `
${s.port} — ${_esc(s.label || '')} ${running}
`; }).join(''); } function _esc(s) { return String(s).replace(/[&<>"']/g, c => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c])); } async function save() { // The panel hides this button for non-admins, but the backend enforces // it anyway (role != admin -> 403) — this is just a local guard against // an accidental call before the DOM has re-rendered. if (window.AppState?.role !== 'admin') return; const msg = document.getElementById('hamlib-msg'); const rows = document.querySelectorAll('#hamlib-slots [data-slot]'); const bySlot = {}; rows.forEach(el => { const i = el.dataset.slot; bySlot[i] = bySlot[i] || {}; if (el.classList.contains('hamlib-enabled')) bySlot[i].enabled = el.checked; if (el.classList.contains('hamlib-port')) bySlot[i].port = parseInt(el.value) || 0; if (el.classList.contains('hamlib-label')) bySlot[i].label = el.value; }); const servers = Object.keys(bySlot).sort((a, b) => a - b).map(k => bySlot[k]); if (msg) msg.textContent = I18n.t('hamlib_saving'); try { const r = await fetch('/api/hamlib/config', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ servers }), }); const d = await r.json(); if (msg) msg.textContent = d.message || (d.ok ? I18n.t('settings_saved_short') : (d.error || I18n.t('status_error_generic'))); await load(); } catch (e) { if (msg) msg.textContent = I18n.t('settings_save_error_plain'); } } window.HamlibUI = { load, save }; })();