feat: extract templates to web/ folder, add PL-test server, auto-propagate, health scoring, admin dashboard redesign
This commit is contained in:
+114
@@ -0,0 +1,114 @@
|
||||
function openModal() { document.getElementById('userModal').classList.add('active'); }
|
||||
function closeModal() { document.getElementById('userModal').classList.remove('active'); }
|
||||
|
||||
function toast(msg, ok) {
|
||||
var t = document.createElement('div');
|
||||
t.textContent = msg;
|
||||
Object.assign(t.style, {
|
||||
position:'fixed', bottom:'24px', left:'50%', transform:'translateX(-50%)',
|
||||
padding:'12px 24px', borderRadius:'12px', fontSize:'13px', fontWeight:'600',
|
||||
background: ok ? 'rgba(16,185,129,0.15)' : 'rgba(244,63,94,0.15)',
|
||||
border: ok ? '1px solid rgba(16,185,129,0.3)' : '1px solid rgba(244,63,94,0.3)',
|
||||
color: ok ? 'var(--green)' : 'var(--rose)',
|
||||
backdropFilter:'blur(12px)', zIndex:'9999',
|
||||
animation:'fadeUp .3s ease-out both', fontFamily:'"Plus Jakarta Sans",sans-serif'
|
||||
});
|
||||
document.body.appendChild(t);
|
||||
setTimeout(function() { t.style.opacity='0'; t.style.transition='opacity .4s'; setTimeout(function(){t.remove()},400) }, 3000);
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var form = document.getElementById('userForm');
|
||||
if (form) {
|
||||
form.addEventListener('submit', async function(e) {
|
||||
e.preventDefault();
|
||||
var formData = new FormData(e.target);
|
||||
var username = formData.get('username');
|
||||
var btn = e.target.querySelector('button[type="submit"]');
|
||||
var orig = btn.textContent;
|
||||
btn.disabled = true;
|
||||
btn.textContent = '...';
|
||||
|
||||
var response = await fetch('/admin/api/users', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({
|
||||
username: username,
|
||||
traffic_limit_gb: parseInt(formData.get('traffic_limit_gb')) || 0
|
||||
})
|
||||
});
|
||||
|
||||
var result = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
var msg = 'Пользователь ' + username + ' создан!';
|
||||
toast(msg, true);
|
||||
setTimeout(function(){ location.reload() }, 1000);
|
||||
} else {
|
||||
toast('Ошибка: ' + (result.error || 'unknown'), false);
|
||||
btn.disabled = false;
|
||||
btn.textContent = orig;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function editUser(id, tier, days, traffic, active) {
|
||||
document.getElementById('editId').value = id;
|
||||
document.getElementById('et').value = tier;
|
||||
document.getElementById('ed').value = days;
|
||||
document.getElementById('etr').value = traffic;
|
||||
document.getElementById('editActive').checked = active == 1;
|
||||
document.getElementById('editModal').classList.add('active');
|
||||
}
|
||||
|
||||
function closeEditModal() { document.getElementById('editModal').classList.remove('active'); }
|
||||
|
||||
async function submitEditForm() {
|
||||
var form = document.getElementById('editForm');
|
||||
var formData = new FormData(form);
|
||||
var data = {
|
||||
id: formData.get('id'),
|
||||
tier: formData.get('tier'),
|
||||
tariff_days_remaining: parseInt(formData.get('tariff_days_remaining')) || 0,
|
||||
traffic_limit_gb: formData.get('traffic_limit_gb') === '' ? 0 : parseInt(formData.get('traffic_limit_gb')),
|
||||
is_active: formData.get('is_active') === 'on'
|
||||
};
|
||||
var btn = form.querySelector('button');
|
||||
var orig = btn.textContent;
|
||||
btn.disabled = true;
|
||||
btn.textContent = '...';
|
||||
|
||||
var response = await fetch('/admin/api/users/update', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
toast('Сохранено!', true);
|
||||
setTimeout(function(){ location.reload() }, 1000);
|
||||
} else {
|
||||
var result = await response.json();
|
||||
toast('Ошибка: ' + (result.error || 'unknown'), false);
|
||||
btn.disabled = false;
|
||||
btn.textContent = orig;
|
||||
}
|
||||
}
|
||||
|
||||
function deleteUser(id, username) {
|
||||
if (confirm('Удалить пользователя ' + username + '? Это удалит его со всех серверов!')) {
|
||||
fetch('/admin/api/users/delete', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({id: id, username: username})
|
||||
}).then(function(r) {
|
||||
if (r.ok) {
|
||||
toast('Удалён!', true);
|
||||
setTimeout(function(){ location.reload() }, 1000);
|
||||
} else {
|
||||
toast('Ошибка удаления', false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user