144 lines
5.1 KiB
JavaScript
144 lines
5.1 KiB
JavaScript
// Reflector Dashboard — web front-end for SvxReflector
|
|
// Copyright (C) 2026 SA7AUX (Björn)
|
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify it under
|
|
// the terms of the GNU Affero General Public License as published by the Free
|
|
// Software Foundation, either version 3 of the License, or (at your option) any
|
|
// later version. See the LICENSE file for details.
|
|
|
|
'use strict';
|
|
|
|
const $ = id => document.getElementById(id);
|
|
let ME = null;
|
|
|
|
const editableFields = [
|
|
{key:'firstname', label:'First Name'},
|
|
{key:'lastname', label:'Last Name'},
|
|
{key:'shortname', label:'Short Name'},
|
|
{key:'email', label:'Email'},
|
|
{key:'city', label:'City'},
|
|
{key:'state', label:'State'},
|
|
{key:'country', label:'Country'},
|
|
];
|
|
|
|
function setMsg(el, txt, err){
|
|
if (!el) return;
|
|
if (txt === null) { el.style.display='none'; el.textContent=''; return; }
|
|
el.style.display='block';
|
|
el.textContent = txt;
|
|
el.style.color = err ? '#ff8080' : '#00ff80';
|
|
}
|
|
|
|
async function loadMe(){
|
|
try {
|
|
const r = await fetch('/auth/me.php', {credentials:'same-origin'});
|
|
const j = await r.json();
|
|
if (j && j.logged_in) ME = j;
|
|
else { window.location.href = '/'; return; }
|
|
} catch(_) { window.location.href = '/'; return; }
|
|
renderProfileEditFields();
|
|
}
|
|
|
|
function renderProfileEditFields(){
|
|
if (!ME) return;
|
|
const container = $('profileEditFields');
|
|
container.innerHTML = '';
|
|
|
|
// Header info
|
|
const info = document.createElement('div');
|
|
info.className = 'mb-3 d-flex gap-3 align-items-center flex-wrap';
|
|
const FLAG_NAMES = [[1,'unreg'],[2,'reg'],[4,'normal'],[8,'lvl1'],[16,'lvl2'],[32,'lvl3']];
|
|
const lvlText = FLAG_NAMES.filter(x => (ME.flags||0) & x[0]).map(x => x[1]).join(', ') || '-';
|
|
const lvlColor = ME.admin_level >= 2 ? '#ffc800' : 'rgba(0,255,255,.75)';
|
|
const star = ME.admin_level >= 2 ? ' \u2605' : '';
|
|
info.innerHTML =
|
|
'<span class="small text-secondary">UUID: <span class="mono">' + ME.uuid + '</span></span>'
|
|
+ '<span class="small" style="color:' + lvlColor + ';">' + lvlText + star + '</span>'
|
|
+ '<span class="small text-secondary">Callsign: <span class="mono">' + (ME.callsign||'') + '</span></span>';
|
|
container.appendChild(info);
|
|
|
|
for (const f of editableFields) {
|
|
const lbl = document.createElement('label');
|
|
lbl.className = 'form-label small text-secondary';
|
|
lbl.textContent = f.label;
|
|
lbl.setAttribute('for', 'pedit_' + f.key);
|
|
const inp = document.createElement('input');
|
|
inp.className = 'form-control form-control-sm mb-2';
|
|
inp.id = 'pedit_' + f.key;
|
|
inp.value = ME[f.key] || '';
|
|
container.appendChild(lbl);
|
|
container.appendChild(inp);
|
|
}
|
|
}
|
|
|
|
async function saveProfile(){
|
|
const body = {};
|
|
for (const f of editableFields) {
|
|
const el = $('pedit_' + f.key);
|
|
if (el) body[f.key] = el.value;
|
|
}
|
|
const r = await fetch('/auth/profile_update.php', {
|
|
method:'POST', credentials:'same-origin',
|
|
headers:{'Content-Type':'application/json'},
|
|
body: JSON.stringify(body)
|
|
});
|
|
const j = await r.json().catch(()=>null);
|
|
if (j && j.ok) {
|
|
setMsg($('profileEditMsg'), 'Saved!', false);
|
|
await loadMe();
|
|
} else {
|
|
setMsg($('profileEditMsg'), (j && j.error) || 'Failed', true);
|
|
}
|
|
}
|
|
|
|
function switchProfileTab(tab){
|
|
document.querySelectorAll('.profile-tab').forEach(b => {
|
|
const active = b.dataset.ptab === tab;
|
|
b.classList.toggle('active', active);
|
|
b.setAttribute('aria-selected', active ? 'true' : 'false');
|
|
});
|
|
document.querySelectorAll('.profile-tab-pane').forEach(p => {
|
|
p.style.display = p.id === 'ptab-' + tab ? '' : 'none';
|
|
});
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
loadMe();
|
|
|
|
document.querySelectorAll('.profile-tab[data-ptab]').forEach(btn => {
|
|
btn.addEventListener('click', () => switchProfileTab(btn.dataset.ptab));
|
|
});
|
|
|
|
$('btnSaveProfile').addEventListener('click', saveProfile);
|
|
|
|
// Reset web password modal
|
|
$('btnUserResetPwd').addEventListener('click', () => {
|
|
$('userPwdOld').value = '';
|
|
$('userPwdNew').value = '';
|
|
$('userPwdNew2').value = '';
|
|
setMsg($('userPwdMsg'), null);
|
|
new bootstrap.Modal($('userPwdModal')).show();
|
|
});
|
|
|
|
$('btnUserPwdSave').addEventListener('click', async () => {
|
|
const old_password = $('userPwdOld').value;
|
|
const new_password = $('userPwdNew').value;
|
|
const new_password2 = $('userPwdNew2').value;
|
|
if (!old_password || !new_password) { setMsg($('userPwdMsg'),'Fill all fields',true); return; }
|
|
if (new_password !== new_password2) { setMsg($('userPwdMsg'),'Passwords do not match',true); return; }
|
|
const r = await fetch('/auth/password_change.php', {
|
|
method:'POST', credentials:'same-origin',
|
|
headers:{'Content-Type':'application/json'},
|
|
body: JSON.stringify({old_password, new_password})
|
|
});
|
|
const j = await r.json().catch(()=>null);
|
|
if (j && j.ok) {
|
|
setMsg($('userPwdMsg'),'Password changed!', false);
|
|
setTimeout(() => bootstrap.Modal.getInstance($('userPwdModal'))?.hide(), 800);
|
|
} else {
|
|
setMsg($('userPwdMsg'), ((j && j.error) || 'Failed').replace(/_/g,' '), true);
|
|
}
|
|
});
|
|
});
|