batch add students: add optional group selection in batch modal
This commit is contained in:
+118
@@ -0,0 +1,118 @@
|
||||
const API = location.origin;
|
||||
let token = sessionStorage.getItem('adminToken');
|
||||
function hdr() { return { 'X-Admin-Token': token }; }
|
||||
function hdrJson() { return { 'Content-Type': 'application/json', 'X-Admin-Token': token }; }
|
||||
function esc(s) { const d = document.createElement('div'); d.textContent = s; return d.innerHTML; }
|
||||
|
||||
// --- Auth check ---
|
||||
async function checkAuth() {
|
||||
if (!token) { location.href = 'login.html'; return false; }
|
||||
try {
|
||||
const res = await fetch(`${API}/api/stats`, { headers: hdr() });
|
||||
if (!res.ok) { sessionStorage.removeItem('adminToken'); location.href = 'login.html'; return false; }
|
||||
return true;
|
||||
} catch { sessionStorage.removeItem('adminToken'); location.href = 'login.html'; return false; }
|
||||
}
|
||||
|
||||
function doLogout() {
|
||||
sessionStorage.removeItem('adminToken');
|
||||
location.href = 'login.html';
|
||||
}
|
||||
|
||||
// --- Sidebar ---
|
||||
const NAV = [
|
||||
{ page: 'dashboard', label: 'Дашборд' },
|
||||
{ page: 'journal', label: 'Журнал' },
|
||||
{ page: 'links', label: 'Ссылки' },
|
||||
{ page: 'students', label: 'Ученики' },
|
||||
{ page: 'groups', label: 'Группы' },
|
||||
{ page: 'settings', label: 'Настройки' }
|
||||
];
|
||||
function buildSidebar(active) {
|
||||
const el = document.getElementById('sidebar');
|
||||
if (!el) return;
|
||||
el.innerHTML = `
|
||||
<div class="logo">Админ-панель</div>
|
||||
<nav>
|
||||
${NAV.map(n => `<a href="${n.page}.html" class="${n.page === active ? 'active' : ''}">${n.label}</a>`).join('')}
|
||||
</nav>
|
||||
<div class="bottom">
|
||||
<a href="/">На главную</a>
|
||||
<a class="logout" onclick="doLogout()">Выйти</a>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
// --- Common UI ---
|
||||
function showToast(msg) {
|
||||
const t = document.getElementById('toast');
|
||||
t.textContent = msg;
|
||||
t.classList.add('show');
|
||||
setTimeout(() => t.classList.remove('show'), 3000);
|
||||
}
|
||||
|
||||
function showImg(src) {
|
||||
const img = document.getElementById('imgModalSrc');
|
||||
const m = document.getElementById('imgModal');
|
||||
if (img && m) { img.src = src; m.classList.add('open'); }
|
||||
}
|
||||
|
||||
async function fillAutocomplete() {
|
||||
const res = await fetch(`${API}/api/students`);
|
||||
const students = await res.json();
|
||||
const names = students.map(s => s.name);
|
||||
const dlEdit = document.getElementById('studentEditList');
|
||||
if (dlEdit) {
|
||||
dlEdit.innerHTML = '';
|
||||
names.forEach(n => { const o = document.createElement('option'); o.value = n; dlEdit.appendChild(o); });
|
||||
}
|
||||
const dlAdd = document.getElementById('studentAddList');
|
||||
if (dlAdd) {
|
||||
dlAdd.innerHTML = '';
|
||||
names.forEach(n => { const o = document.createElement('option'); o.value = n; dlAdd.appendChild(o); });
|
||||
}
|
||||
}
|
||||
|
||||
function copyStudentLink(name) {
|
||||
const url = `${location.origin}/?nameInput=${encodeURIComponent(name)}`;
|
||||
const done = () => showToast('Ссылка скопирована: ' + url);
|
||||
if (navigator.clipboard && window.isSecureContext) {
|
||||
navigator.clipboard.writeText(url).then(done).catch(() => fallbackCopy(url, done));
|
||||
} else {
|
||||
fallbackCopy(url, done);
|
||||
}
|
||||
}
|
||||
|
||||
function fallbackCopy(text, done) {
|
||||
const ta = document.createElement('textarea');
|
||||
ta.value = text;
|
||||
ta.style.position = 'fixed';
|
||||
ta.style.opacity = '0';
|
||||
document.body.appendChild(ta);
|
||||
ta.select();
|
||||
try { document.execCommand('copy'); done(); } catch { prompt('Скопируйте вручную:', text); }
|
||||
document.body.removeChild(ta);
|
||||
}
|
||||
|
||||
function copyText(text) {
|
||||
const done = () => showToast('Скопировано: ' + text);
|
||||
if (navigator.clipboard && window.isSecureContext) {
|
||||
navigator.clipboard.writeText(text).then(done).catch(() => fallbackCopy(text, done));
|
||||
} else {
|
||||
fallbackCopy(text, done);
|
||||
}
|
||||
}
|
||||
|
||||
function renderPager(el, page, totalPages, onPage) {
|
||||
if (!el) return;
|
||||
if (totalPages <= 1) { el.innerHTML = ''; return; }
|
||||
let html = `<button ${page === 1 ? 'disabled' : ''} onclick="(${onPage})(${page - 1})">‹</button>`;
|
||||
for (let i = 1; i <= totalPages; i++) {
|
||||
if (totalPages > 9 && i > 2 && i < totalPages - 1 && Math.abs(i - page) > 2) {
|
||||
if (i === 3) html += `<button disabled>…</button>`;
|
||||
continue;
|
||||
}
|
||||
html += `<button class="${i === page ? 'active' : ''}" onclick="(${onPage})(${i})">${i}</button>`;
|
||||
}
|
||||
html += `<button ${page === totalPages ? 'disabled' : ''} onclick="(${onPage})(${page + 1})">›</button>`;
|
||||
el.innerHTML = html;
|
||||
}
|
||||
Reference in New Issue
Block a user