user administration
This commit is contained in:
@@ -102,6 +102,114 @@
|
||||
}
|
||||
.btn-logout:hover { border-color: #f85149; color: #f85149; }
|
||||
|
||||
/* ── AVATAR DROPDOWN ── */
|
||||
.avatar-wrap { position: relative; }
|
||||
.topbar-avatar { cursor: pointer; transition: box-shadow 0.15s; }
|
||||
.topbar-avatar:hover { box-shadow: 0 0 0 2px #388bfd; }
|
||||
.avatar-menu {
|
||||
display: none; position: absolute; top: calc(100% + 8px); right: 0;
|
||||
background: #1c1f2e; border: 1px solid #30363d; border-radius: 9px;
|
||||
min-width: 180px; z-index: 500;
|
||||
box-shadow: 0 8px 24px rgba(0,0,0,0.55);
|
||||
overflow: hidden;
|
||||
}
|
||||
.avatar-menu.open { display: block; }
|
||||
.avatar-menu-item {
|
||||
display: flex; align-items: center; gap: 0.6rem;
|
||||
padding: 0.62rem 1rem; font-size: 0.85rem; color: #c9d1d9;
|
||||
cursor: pointer; transition: background 0.12s; border: none;
|
||||
background: none; width: 100%; text-align: left;
|
||||
}
|
||||
.avatar-menu-item:hover { background: #30363d; }
|
||||
.avatar-menu-item .mi-icon { font-size: 1rem; }
|
||||
.avatar-menu-sep { border: none; border-top: 1px solid #30363d; margin: 3px 0; }
|
||||
|
||||
/* ── ADMIN PANEL ── */
|
||||
.admin-overlay {
|
||||
display: none; position: fixed; inset: 0;
|
||||
background: rgba(0,0,0,0.7); z-index: 600;
|
||||
align-items: flex-start; justify-content: center;
|
||||
overflow-y: auto; padding: 2rem 1rem;
|
||||
}
|
||||
.admin-overlay.open { display: flex; }
|
||||
.admin-panel {
|
||||
background: #161b22; border: 1px solid #30363d; border-radius: 14px;
|
||||
width: 100%; max-width: 820px;
|
||||
box-shadow: 0 12px 48px rgba(0,0,0,0.7);
|
||||
display: flex; flex-direction: column;
|
||||
min-height: 0;
|
||||
}
|
||||
.admin-head {
|
||||
display: flex; align-items: center; gap: 1rem;
|
||||
padding: 1rem 1.5rem; border-bottom: 1px solid #30363d; flex-shrink: 0;
|
||||
}
|
||||
.admin-head h2 { font-size: 1rem; color: #c9d1d9; margin: 0; flex: 1; }
|
||||
.btn-admin-close {
|
||||
background: none; border: none; color: #6e7681; cursor: pointer;
|
||||
font-size: 1.2rem; line-height: 1; padding: 2px;
|
||||
transition: color 0.12s;
|
||||
}
|
||||
.btn-admin-close:hover { color: #f85149; }
|
||||
.admin-tabs {
|
||||
display: flex; gap: 0; border-bottom: 1px solid #30363d; flex-shrink: 0;
|
||||
}
|
||||
.admin-tab {
|
||||
padding: 0.62rem 1.25rem; font-size: 0.85rem; color: #8b949e;
|
||||
cursor: pointer; background: none; border: none;
|
||||
border-bottom: 2px solid transparent; transition: all 0.12s;
|
||||
font-weight: 500;
|
||||
}
|
||||
.admin-tab:hover { color: #c9d1d9; }
|
||||
.admin-tab.active { color: #58a6ff; border-bottom-color: #58a6ff; }
|
||||
.admin-body { padding: 1.25rem 1.5rem; flex: 1; }
|
||||
.admin-section { display: none; }
|
||||
.admin-section.active { display: block; }
|
||||
.admin-toolbar {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.admin-toolbar h3 { font-size: 0.9rem; color: #8b949e; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px; margin: 0; }
|
||||
.btn-admin-add {
|
||||
padding: 0.38rem 0.9rem; background: #238636; border: none; border-radius: 6px;
|
||||
color: #fff; font-size: 0.82rem; font-weight: 600; cursor: pointer; transition: background 0.15s;
|
||||
}
|
||||
.btn-admin-add:hover { background: #2ea043; }
|
||||
.admin-table { width: 100%; border-collapse: collapse; font-size: 0.85rem; }
|
||||
.admin-table th {
|
||||
text-align: left; color: #6e7681; font-size: 0.74rem; text-transform: uppercase;
|
||||
letter-spacing: 0.4px; padding: 0.4rem 0.75rem; border-bottom: 1px solid #21262d;
|
||||
}
|
||||
.admin-table td { padding: 0.55rem 0.75rem; border-bottom: 1px solid #21262d; color: #c9d1d9; vertical-align: middle; }
|
||||
.admin-table tr:last-child td { border-bottom: none; }
|
||||
.admin-table tr:hover td { background: #1c1f2e; }
|
||||
.td-actions { display: flex; gap: 0.4rem; }
|
||||
.btn-tbl { padding: 0.22rem 0.6rem; border-radius: 5px; font-size: 0.76rem; cursor: pointer; border: 1px solid; transition: all 0.12s; }
|
||||
.btn-tbl-edit { border-color: #1f6feb55; color: #58a6ff; background: #1f6feb10; }
|
||||
.btn-tbl-edit:hover { background: #1f6feb33; }
|
||||
.btn-tbl-del { border-color: #f8514955; color: #f85149; background: #f8514910; }
|
||||
.btn-tbl-del:hover { background: #f8514930; }
|
||||
.badge-admin { padding: 1px 7px; border-radius: 8px; font-size: 0.72rem; font-weight: 700; background: #1f6feb22; color: #388bfd; }
|
||||
.badge-mustchange { padding: 1px 7px; border-radius: 8px; font-size: 0.72rem; font-weight: 700; background: #e3b34122; color: #e3b341; }
|
||||
|
||||
/* Mini form in admin */
|
||||
.admin-form { background: #0d1117; border: 1px solid #30363d; border-radius: 9px; padding: 1rem; margin-bottom: 1.25rem; }
|
||||
.admin-form h4 { font-size: 0.8rem; color: #8b949e; text-transform: uppercase; letter-spacing: 0.4px; margin-bottom: 0.75rem; }
|
||||
.admin-form .row { display: flex; gap: 0.6rem; flex-wrap: wrap; }
|
||||
.admin-form .row > * { flex: 1 1 140px; }
|
||||
.admin-form input, .admin-form select {
|
||||
width: 100%; padding: 0.48rem 0.65rem;
|
||||
background: #161b22; border: 1px solid #30363d; border-radius: 6px;
|
||||
color: #e1e4e8; font-size: 0.85rem; outline: none; font-family: inherit;
|
||||
transition: border-color 0.15s;
|
||||
}
|
||||
.admin-form input:focus, .admin-form select:focus { border-color: #58a6ff; }
|
||||
.admin-form select option { background: #161b22; }
|
||||
.admin-form label { display: block; font-size: 0.75rem; color: #8b949e; margin-bottom: 0.28rem; text-transform: uppercase; letter-spacing: 0.3px; }
|
||||
.admin-form-actions { display: flex; gap: 0.6rem; margin-top: 0.75rem; justify-content: flex-end; }
|
||||
.btn-admin-save { padding: 0.42rem 1rem; background: #1f6feb; border: none; border-radius: 6px; color: #fff; cursor: pointer; font-weight: 600; font-size: 0.84rem; }
|
||||
.btn-admin-cancel { padding: 0.42rem 0.9rem; background: transparent; border: 1px solid #30363d; border-radius: 6px; color: #8b949e; cursor: pointer; font-size: 0.84rem; }
|
||||
.admin-err { color: #f85149; font-size: 0.8rem; min-height: 1.1rem; margin-top: 0.35rem; }
|
||||
|
||||
/* ── APP BODY ── */
|
||||
.app-body {
|
||||
flex: 1;
|
||||
@@ -404,9 +512,17 @@
|
||||
</div>
|
||||
<div class="topbar-spacer"></div>
|
||||
<div class="topbar-user">
|
||||
<div class="topbar-avatar" id="hdr-avatar">?</div>
|
||||
<span class="topbar-username" id="hdr-user"></span>
|
||||
<button class="btn-logout" onclick="doLogout()">Odhlásiť sa</button>
|
||||
<div class="avatar-wrap" id="avatar-wrap">
|
||||
<div class="topbar-avatar" id="hdr-avatar" onclick="toggleAvatarMenu()">?</div>
|
||||
<div class="avatar-menu" id="avatar-menu">
|
||||
<button class="avatar-menu-item" id="mi-users" onclick="openAdmin('users')" style="display:none"><span class="mi-icon">👥</span> Správca užívateľov</button>
|
||||
<button class="avatar-menu-item" id="mi-families" onclick="openAdmin('families')" style="display:none"><span class="mi-icon">🏠</span> Správca rodín</button>
|
||||
<button class="avatar-menu-item" id="mi-settings" onclick="openAdmin('settings')" style="display:none"><span class="mi-icon">⚙️</span> Nastavenia</button>
|
||||
<hr class="avatar-menu-sep" id="mi-sep" style="display:none" />
|
||||
<button class="avatar-menu-item" onclick="doLogout()"><span class="mi-icon">🚪</span> Odhlásiť sa</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -502,6 +618,103 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ══════════ ADMIN PANEL ══════════ -->
|
||||
<div class="admin-overlay" id="admin-overlay" onclick="onAdminOverlayClick(event)">
|
||||
<div class="admin-panel">
|
||||
<div class="admin-head">
|
||||
<h2>⚙️ Administrácia</h2>
|
||||
<button class="btn-admin-close" onclick="closeAdmin()">✕</button>
|
||||
</div>
|
||||
<div class="admin-tabs">
|
||||
<button class="admin-tab" data-tab="users" onclick="switchAdminTab('users')">👥 Správca užívateľov</button>
|
||||
<button class="admin-tab" data-tab="families" onclick="switchAdminTab('families')">🏠 Správca rodín</button>
|
||||
<button class="admin-tab" data-tab="settings" onclick="switchAdminTab('settings')">⚙️ Nastavenia</button>
|
||||
</div>
|
||||
<div class="admin-body">
|
||||
|
||||
<!-- USERS TAB -->
|
||||
<div class="admin-section" id="tab-users">
|
||||
<div id="user-form-wrap" style="display:none">
|
||||
<div class="admin-form">
|
||||
<h4 id="user-form-title">Nový používateľ</h4>
|
||||
<input type="hidden" id="uf-id" value="" />
|
||||
<div class="row">
|
||||
<div><label>Meno</label><input id="uf-username" placeholder="johndoe" /></div>
|
||||
<div><label>Email</label><input id="uf-email" type="email" placeholder="john@example.com" /></div>
|
||||
<div><label>Rodina</label>
|
||||
<select id="uf-family"><option value="">— bez rodiny —</option></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="admin-err" id="uf-err"></div>
|
||||
<div class="admin-form-actions">
|
||||
<button class="btn-admin-cancel" onclick="hideUserForm()">Zrušiť</button>
|
||||
<button class="btn-admin-save" onclick="saveUser()">Uložiť</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="admin-toolbar">
|
||||
<h3>Používatelia</h3>
|
||||
<button class="btn-admin-add" onclick="showUserForm()">+ Pridať</button>
|
||||
</div>
|
||||
<table class="admin-table">
|
||||
<thead><tr><th>Meno</th><th>Email</th><th>Rodina</th><th>Roly</th><th></th></tr></thead>
|
||||
<tbody id="admin-users-tbody"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- FAMILIES TAB -->
|
||||
<div class="admin-section" id="tab-families">
|
||||
<div id="family-form-wrap" style="display:none">
|
||||
<div class="admin-form">
|
||||
<h4 id="fam-form-title">Nová rodina</h4>
|
||||
<input type="hidden" id="ff-id" value="" />
|
||||
<div class="row" style="max-width:340px">
|
||||
<div><label>Názov rodiny</label><input id="ff-name" placeholder="Moja rodina" /></div>
|
||||
</div>
|
||||
<div class="admin-err" id="ff-err"></div>
|
||||
<div class="admin-form-actions">
|
||||
<button class="btn-admin-cancel" onclick="hideFamilyForm()">Zrušiť</button>
|
||||
<button class="btn-admin-save" onclick="saveFamily()">Uložiť</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="admin-toolbar">
|
||||
<h3>Rodiny</h3>
|
||||
<button class="btn-admin-add" onclick="showFamilyForm()">+ Pridať</button>
|
||||
</div>
|
||||
<table class="admin-table">
|
||||
<thead><tr><th>Názov</th><th>Počet členov</th><th></th></tr></thead>
|
||||
<tbody id="admin-families-tbody"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- SETTINGS TAB -->
|
||||
<div class="admin-section" id="tab-settings">
|
||||
<p style="color:#8b949e;font-size:0.88rem;margin-top:0.5rem">Nastavenia budú dostupné čoskoro.</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- MUST-CHANGE-PASSWORD MODAL -->
|
||||
<div class="modal-overlay" id="mcp-modal">
|
||||
<div class="modal" style="max-width:380px">
|
||||
<div class="modal-head">
|
||||
<h2>🔐 Zmeň si heslo</h2>
|
||||
</div>
|
||||
<p style="font-size:0.84rem;color:#8b949e;margin-bottom:1rem">
|
||||
Tvoj účet bol vytvorený administrátorom.<br>Prosím zmeň si heslo pred pokračovaním.
|
||||
</p>
|
||||
<div class="fg"><label>Nové heslo</label><input type="password" id="mcp-pass" placeholder="min. 6 znakov" /></div>
|
||||
<div class="fg"><label>Potvrď heslo</label><input type="password" id="mcp-pass2" placeholder="zopakuj heslo" onkeydown="if(event.key==='Enter')doMcpChange()" /></div>
|
||||
<div class="err" id="mcp-err"></div>
|
||||
<div class="modal-actions">
|
||||
<button class="btn-save" onclick="doMcpChange()">Zmeniť heslo</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const API = '/api';
|
||||
const v = id => document.getElementById(id).value;
|
||||
@@ -621,10 +834,13 @@ function saveAuth(d) {
|
||||
token = d.token;
|
||||
}
|
||||
|
||||
let isAdmin = false;
|
||||
|
||||
function doLogout() {
|
||||
localStorage.clear(); token = null; myId = 0;
|
||||
localStorage.clear(); token = null; myId = 0; isAdmin = false;
|
||||
document.getElementById('auth-screen').style.display = 'flex';
|
||||
document.getElementById('app-screen').style.display = 'none';
|
||||
document.getElementById('avatar-menu').classList.remove('open');
|
||||
}
|
||||
|
||||
function showApp() {
|
||||
@@ -632,8 +848,23 @@ function showApp() {
|
||||
document.getElementById('app-screen').style.display = 'flex';
|
||||
const u = JSON.parse(localStorage.getItem('user') || '{}');
|
||||
const name = u.username || '';
|
||||
isAdmin = !!u.isAdmin;
|
||||
document.getElementById('hdr-user').textContent = name;
|
||||
document.getElementById('hdr-avatar').textContent = name ? name[0].toUpperCase() : '?';
|
||||
|
||||
// Admin menu items
|
||||
['mi-users','mi-families','mi-settings','mi-sep'].forEach(id => {
|
||||
const el = document.getElementById(id);
|
||||
if (el) el.style.display = isAdmin ? '' : 'none';
|
||||
});
|
||||
|
||||
// MustChangePassword check
|
||||
if (u.mustChangePassword) {
|
||||
setTimeout(() => {
|
||||
document.getElementById('mcp-modal').classList.add('open');
|
||||
}, 400);
|
||||
}
|
||||
|
||||
loadReminders();
|
||||
}
|
||||
|
||||
@@ -1006,12 +1237,213 @@ function removeUser(id) { selectedUsers = selectedUsers.filter(u=>u.id!==id); re
|
||||
document.addEventListener('click', e => {
|
||||
if (!document.getElementById('user-picker').contains(e.target))
|
||||
document.getElementById('picker-dropdown').classList.remove('open');
|
||||
if (!document.getElementById('avatar-wrap').contains(e.target))
|
||||
document.getElementById('avatar-menu').classList.remove('open');
|
||||
});
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
requestAnimationFrame(() => trimCalendarCells());
|
||||
});
|
||||
|
||||
// ═══ AVATAR MENU ═══
|
||||
function toggleAvatarMenu() {
|
||||
document.getElementById('avatar-menu').classList.toggle('open');
|
||||
}
|
||||
|
||||
// ═══ MUST-CHANGE-PASSWORD ═══
|
||||
async function doMcpChange() {
|
||||
const err = document.getElementById('mcp-err');
|
||||
err.textContent = '';
|
||||
const pass = document.getElementById('mcp-pass').value;
|
||||
const pass2 = document.getElementById('mcp-pass2').value;
|
||||
const u = JSON.parse(localStorage.getItem('user') || '{}');
|
||||
if (pass.length < 6) { err.textContent = 'Heslo musí mať aspoň 6 znakov.'; return; }
|
||||
if (pass !== pass2) { err.textContent = 'Heslá sa nezhodujú.'; return; }
|
||||
const res = await fetch(`${API}/auth/reset-password`, {
|
||||
method:'POST', headers:{'Content-Type':'application/json'},
|
||||
body: JSON.stringify({ email: u.email, newPassword: pass, code: null })
|
||||
});
|
||||
if (res.ok) {
|
||||
// Update stored user to clear mustChangePassword flag
|
||||
u.mustChangePassword = false;
|
||||
localStorage.setItem('user', JSON.stringify(u));
|
||||
document.getElementById('mcp-modal').classList.remove('open');
|
||||
} else {
|
||||
err.textContent = await res.text();
|
||||
}
|
||||
}
|
||||
|
||||
// ═══ ADMIN PANEL ═══
|
||||
let adminFamilies = [];
|
||||
|
||||
function openAdmin(tab) {
|
||||
document.getElementById('avatar-menu').classList.remove('open');
|
||||
document.getElementById('admin-overlay').classList.add('open');
|
||||
switchAdminTab(tab || 'users');
|
||||
}
|
||||
function closeAdmin() {
|
||||
document.getElementById('admin-overlay').classList.remove('open');
|
||||
}
|
||||
function onAdminOverlayClick(e) {
|
||||
if (e.target === document.getElementById('admin-overlay')) closeAdmin();
|
||||
}
|
||||
function switchAdminTab(tab) {
|
||||
document.querySelectorAll('.admin-tab').forEach(t => t.classList.toggle('active', t.dataset.tab === tab));
|
||||
document.querySelectorAll('.admin-section').forEach(s => s.classList.toggle('active', s.id === `tab-${tab}`));
|
||||
if (tab === 'users') loadAdminData();
|
||||
if (tab === 'families') loadAdminFamilies();
|
||||
}
|
||||
|
||||
async function loadAdminData() {
|
||||
const [uRes, fRes] = await Promise.all([
|
||||
fetch(`${API}/admin/users`, { headers: auth() }),
|
||||
fetch(`${API}/admin/families`, { headers: auth() })
|
||||
]);
|
||||
const users = uRes.ok ? await uRes.json() : [];
|
||||
adminFamilies = fRes.ok ? await fRes.json() : [];
|
||||
renderAdminUsers(users);
|
||||
populateFamilySelect('uf-family', null);
|
||||
}
|
||||
|
||||
async function loadAdminFamilies() {
|
||||
const res = await fetch(`${API}/admin/families`, { headers: auth() });
|
||||
adminFamilies = res.ok ? await res.json() : [];
|
||||
renderAdminFamilies(adminFamilies);
|
||||
}
|
||||
|
||||
function renderAdminUsers(users) {
|
||||
const tbody = document.getElementById('admin-users-tbody');
|
||||
if (!users.length) { tbody.innerHTML = '<tr><td colspan="5" style="color:#484f58;text-align:center;padding:1.5rem">Žiadni používatelia</td></tr>'; return; }
|
||||
tbody.innerHTML = users.map(u => `
|
||||
<tr>
|
||||
<td>${u.username}</td>
|
||||
<td style="color:#8b949e">${u.email}</td>
|
||||
<td>${u.familyName || '<span style="color:#484f58">—</span>'}</td>
|
||||
<td>
|
||||
${u.isAdmin ? '<span class="badge-admin">Admin</span>' : ''}
|
||||
${u.mustChangePassword ? '<span class="badge-mustchange">Zmeniť heslo</span>' : ''}
|
||||
</td>
|
||||
<td><div class="td-actions">
|
||||
<button class="btn-tbl btn-tbl-edit" onclick="editUser(${u.id})">✏️ Upraviť</button>
|
||||
${u.isAdmin ? '' : `<button class="btn-tbl btn-tbl-del" onclick="deleteUser(${u.id},'${u.username}')">🗑 Zmazať</button>`}
|
||||
</div></td>
|
||||
</tr>`).join('');
|
||||
}
|
||||
|
||||
function renderAdminFamilies(families) {
|
||||
const tbody = document.getElementById('admin-families-tbody');
|
||||
if (!families.length) { tbody.innerHTML = '<tr><td colspan="3" style="color:#484f58;text-align:center;padding:1.5rem">Žiadne rodiny</td></tr>'; return; }
|
||||
tbody.innerHTML = families.map(f => `
|
||||
<tr>
|
||||
<td>${f.name}</td>
|
||||
<td style="color:#8b949e">${f.memberCount}</td>
|
||||
<td><div class="td-actions">
|
||||
<button class="btn-tbl btn-tbl-edit" onclick="editFamily(${f.id},'${f.name.replace(/'/g,"\\'")}')">✏️ Upraviť</button>
|
||||
<button class="btn-tbl btn-tbl-del" onclick="deleteFamily(${f.id},'${f.name.replace(/'/g,"\\'")}')">🗑 Zmazať</button>
|
||||
</div></td>
|
||||
</tr>`).join('');
|
||||
}
|
||||
|
||||
function populateFamilySelect(selectId, selectedId) {
|
||||
const sel = document.getElementById(selectId);
|
||||
sel.innerHTML = '<option value="">— bez rodiny —</option>' +
|
||||
adminFamilies.map(f => `<option value="${f.id}" ${f.id == selectedId ? 'selected' : ''}>${f.name}</option>`).join('');
|
||||
}
|
||||
|
||||
// USER FORM
|
||||
function showUserForm(clearId) {
|
||||
document.getElementById('uf-id').value = '';
|
||||
document.getElementById('uf-username').value = '';
|
||||
document.getElementById('uf-email').value = '';
|
||||
document.getElementById('uf-err').textContent = '';
|
||||
document.getElementById('user-form-title').textContent = 'Nový používateľ';
|
||||
populateFamilySelect('uf-family', null);
|
||||
document.getElementById('user-form-wrap').style.display = '';
|
||||
document.getElementById('uf-username').focus();
|
||||
}
|
||||
function hideUserForm() { document.getElementById('user-form-wrap').style.display = 'none'; }
|
||||
function editUser(id) {
|
||||
// Find from table — reload users first
|
||||
fetch(`${API}/admin/users`, { headers: auth() }).then(r => r.json()).then(users => {
|
||||
const u = users.find(x => x.id === id); if (!u) return;
|
||||
document.getElementById('uf-id').value = u.id;
|
||||
document.getElementById('uf-username').value = u.username;
|
||||
document.getElementById('uf-email').value = u.email;
|
||||
document.getElementById('uf-err').textContent = '';
|
||||
document.getElementById('user-form-title').textContent = 'Upraviť používateľa';
|
||||
populateFamilySelect('uf-family', u.familyId);
|
||||
document.getElementById('user-form-wrap').style.display = '';
|
||||
document.getElementById('uf-username').focus();
|
||||
});
|
||||
}
|
||||
async function saveUser() {
|
||||
const id = document.getElementById('uf-id').value;
|
||||
const body = {
|
||||
username: document.getElementById('uf-username').value.trim(),
|
||||
email: document.getElementById('uf-email').value.trim(),
|
||||
familyId: document.getElementById('uf-family').value ? parseInt(document.getElementById('uf-family').value) : null
|
||||
};
|
||||
const err = document.getElementById('uf-err');
|
||||
err.textContent = '';
|
||||
if (!body.username || !body.email) { err.textContent = 'Meno a email sú povinné.'; return; }
|
||||
const url = id ? `${API}/admin/users/${id}` : `${API}/admin/users`;
|
||||
const method = id ? 'PUT' : 'POST';
|
||||
const res = await fetch(url, { method, headers: { ...auth(), 'Content-Type': 'application/json' }, body: JSON.stringify(body) });
|
||||
if (res.ok) {
|
||||
hideUserForm();
|
||||
loadAdminData();
|
||||
} else {
|
||||
err.textContent = await res.text();
|
||||
}
|
||||
}
|
||||
async function deleteUser(id, name) {
|
||||
if (!confirm(`Zmazať používateľa „${name}"?`)) return;
|
||||
const res = await fetch(`${API}/admin/users/${id}`, { method: 'DELETE', headers: auth() });
|
||||
if (res.ok) loadAdminData();
|
||||
else alert(await res.text());
|
||||
}
|
||||
|
||||
// FAMILY FORM
|
||||
function showFamilyForm() {
|
||||
document.getElementById('ff-id').value = '';
|
||||
document.getElementById('ff-name').value = '';
|
||||
document.getElementById('ff-err').textContent = '';
|
||||
document.getElementById('fam-form-title').textContent = 'Nová rodina';
|
||||
document.getElementById('family-form-wrap').style.display = '';
|
||||
document.getElementById('ff-name').focus();
|
||||
}
|
||||
function hideFamilyForm() { document.getElementById('family-form-wrap').style.display = 'none'; }
|
||||
function editFamily(id, name) {
|
||||
document.getElementById('ff-id').value = id;
|
||||
document.getElementById('ff-name').value = name;
|
||||
document.getElementById('ff-err').textContent = '';
|
||||
document.getElementById('fam-form-title').textContent = 'Upraviť rodinu';
|
||||
document.getElementById('family-form-wrap').style.display = '';
|
||||
document.getElementById('ff-name').focus();
|
||||
}
|
||||
async function saveFamily() {
|
||||
const id = document.getElementById('ff-id').value;
|
||||
const name = document.getElementById('ff-name').value.trim();
|
||||
const err = document.getElementById('ff-err');
|
||||
err.textContent = '';
|
||||
if (!name) { err.textContent = 'Názov je povinný.'; return; }
|
||||
const url = id ? `${API}/admin/families/${id}` : `${API}/admin/families`;
|
||||
const method = id ? 'PUT' : 'POST';
|
||||
const res = await fetch(url, { method, headers: { ...auth(), 'Content-Type': 'application/json' }, body: JSON.stringify({ name }) });
|
||||
if (res.ok) {
|
||||
hideFamilyForm();
|
||||
loadAdminFamilies();
|
||||
} else {
|
||||
err.textContent = await res.text();
|
||||
}
|
||||
}
|
||||
async function deleteFamily(id, name) {
|
||||
if (!confirm(`Zmazať rodinu „${name}"?`)) return;
|
||||
const res = await fetch(`${API}/admin/families/${id}`, { method: 'DELETE', headers: auth() });
|
||||
if (res.ok) loadAdminFamilies();
|
||||
else alert(await res.text());
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user