127 lines
5.6 KiB
JavaScript
127 lines
5.6 KiB
JavaScript
async function init() {
|
|
if (!(await checkAuth())) return;
|
|
if (!isAdmin()) { location.href = 'journal.html'; return; }
|
|
buildSidebar('users');
|
|
document.getElementById('addUserBtn').addEventListener('click', () => openModal(null));
|
|
document.getElementById('uCancel').addEventListener('click', closeModal);
|
|
document.getElementById('uSave').addEventListener('click', saveUser);
|
|
document.getElementById('uRole').addEventListener('change', () => {
|
|
document.getElementById('uBranchesField').style.display = document.getElementById('uRole').value === 'admin' ? 'none' : '';
|
|
});
|
|
await loadUsers();
|
|
}
|
|
|
|
let branches = [];
|
|
let editingId = null;
|
|
|
|
async function loadUsers() {
|
|
const [uRes, bRes] = await Promise.all([
|
|
fetch(`${API}/api/users`, { headers: hdr() }),
|
|
fetch(`${API}/api/branches`, { headers: hdr() }),
|
|
]);
|
|
if (!uRes.ok) { showToast('Ошибка загрузки пользователей'); return; }
|
|
branches = await bRes.json();
|
|
renderUsers(await uRes.json());
|
|
}
|
|
|
|
function renderUsers(users) {
|
|
const tbody = document.querySelector('#usersTable tbody');
|
|
const empty = document.getElementById('usersEmpty');
|
|
tbody.innerHTML = '';
|
|
empty.style.display = users.length ? 'none' : 'block';
|
|
const branchById = {};
|
|
branches.forEach(b => { branchById[b.id] = b.name; });
|
|
users.forEach(u => {
|
|
const tr = document.createElement('tr');
|
|
const branchNames = (u.branch_ids || []).map(id => branchById[id] || `#${id}`).join(', ') || '—';
|
|
const roleCls = u.role === 'admin' ? 'badge-admin' : 'badge-tutor';
|
|
const roleTxt = u.role === 'admin' ? 'Администратор' : 'Тутор';
|
|
const status = u.is_active ? '<span class="status-ok">Активен</span>' : '<span class="status-off">Заблокирован</span>';
|
|
const delBtn = u.id === currentUser.id
|
|
? ''
|
|
: `<button class="row-btn del" data-del="${u.id}" title="Удалить">×</button>`;
|
|
tr.innerHTML = `
|
|
<td>${u.id}</td>
|
|
<td>${esc(u.username)}</td>
|
|
<td>${esc(u.name || '')}</td>
|
|
<td><span class="${roleCls}">${roleTxt}</span></td>
|
|
<td class="user-branches">${esc(branchNames)}</td>
|
|
<td>${status}</td>
|
|
<td>
|
|
<button class="row-btn" data-edit="${u.id}" title="Редактировать">✎</button>
|
|
${delBtn}
|
|
</td>`;
|
|
tbody.appendChild(tr);
|
|
});
|
|
tbody.querySelectorAll('[data-edit]').forEach(b => {
|
|
const userId = Number(b.dataset.edit);
|
|
const user = users.find(u => u.id === userId);
|
|
b.addEventListener('click', () => openModal(user));
|
|
});
|
|
tbody.querySelectorAll('[data-del]').forEach(b => b.addEventListener('click', () => delUser(Number(b.dataset.del))));
|
|
}
|
|
|
|
function openModal(user) {
|
|
editingId = user ? user.id : null;
|
|
document.getElementById('userModalTitle').textContent = user ? `Пользователь: ${user.username}` : 'Новый пользователь';
|
|
document.getElementById('uUsername').value = user ? user.username : '';
|
|
document.getElementById('uName').value = user ? (user.name || '') : '';
|
|
document.getElementById('uRole').value = user ? user.role : 'tutor';
|
|
document.getElementById('uPassword').value = '';
|
|
document.getElementById('uActive').checked = user ? user.is_active : true;
|
|
document.getElementById('pwdHint').textContent = user ? '(оставьте пустым, чтобы не менять)' : '(мин. 6 символов)';
|
|
document.getElementById('uRole').disabled = !!user && user.id === currentUser.id;
|
|
const sel = document.getElementById('uBranches');
|
|
sel.innerHTML = '';
|
|
branches.forEach(b => {
|
|
const o = document.createElement('option');
|
|
o.value = b.id;
|
|
o.textContent = b.name;
|
|
if (user && (user.branch_ids || []).includes(b.id)) o.selected = true;
|
|
sel.appendChild(o);
|
|
});
|
|
document.getElementById('uBranchesField').style.display = document.getElementById('uRole').value === 'admin' ? 'none' : '';
|
|
document.getElementById('userModal').classList.add('open');
|
|
}
|
|
|
|
function closeModal() {
|
|
document.getElementById('userModal').classList.remove('open');
|
|
}
|
|
|
|
async function saveUser() {
|
|
const username = document.getElementById('uUsername').value.trim();
|
|
const name = document.getElementById('uName').value.trim() || null;
|
|
const role = document.getElementById('uRole').value;
|
|
const password = document.getElementById('uPassword').value;
|
|
const is_active = document.getElementById('uActive').checked;
|
|
const branchSel = document.getElementById('uBranches');
|
|
const branch_ids = role === 'admin' ? [] : [...branchSel.selectedOptions].map(o => Number(o.value));
|
|
if (!username) { showToast('Укажите логин'); return; }
|
|
if (!editingId && password.length < 6) { showToast('Пароль должен быть не короче 6 символов'); return; }
|
|
const payload = { username, name, role, is_active, branch_ids };
|
|
if (password) payload.password = password;
|
|
const res = await fetch(editingId ? `${API}/api/users/${editingId}` : `${API}/api/users`, {
|
|
method: editingId ? 'PUT' : 'POST',
|
|
headers: hdrJson(),
|
|
body: JSON.stringify(payload),
|
|
});
|
|
const data = await res.json().catch(() => ({}));
|
|
if (!res.ok) { showToast(data.error || 'Ошибка сохранения'); return; }
|
|
showToast('Сохранено');
|
|
closeModal();
|
|
loadUsers();
|
|
}
|
|
|
|
async function delUser(id) {
|
|
if (!confirm('Удалить пользователя?')) return;
|
|
const res = await fetch(`${API}/api/users/${id}`, { method: 'DELETE', headers: hdr() });
|
|
if (!res.ok) {
|
|
const data = await res.json().catch(() => ({}));
|
|
showToast(data.error || 'Ошибка удаления');
|
|
return;
|
|
}
|
|
showToast('Пользователь удалён');
|
|
loadUsers();
|
|
}
|
|
|
|
init(); |