let links = []; let groups = []; let editingId = null; let studentsLoaded = false; let linkSettings = null; let linkPage = 1; const LINK_PAGE_SIZE = 20; async function loadLinkSettings() { if (!linkSettings) { const res = await fetch(`${API}/api/settings`, { headers: hdr() }); linkSettings = await res.json().catch(() => ({})); } return linkSettings; } function linkUrl(token) { return `${location.origin}/s/${token}`; } async function loadLinks() { await loadLinkSettings(); const p = new URLSearchParams(); p.set('limit', LINK_PAGE_SIZE); p.set('offset', (linkPage - 1) * LINK_PAGE_SIZE); const res = await fetch(`${API}/api/links?${p}`, { headers: hdr() }); const data = await res.json(); links = data.items; const total = data.total; const el = document.getElementById('linksList'); if (!links.length) { el.innerHTML = `
${linkPage > 1 ? 'На этой странице ссылок нет.' : 'Ссылок пока нет. Создайте её в Журнале кнопкой «Ссылка».'}
`; renderPager(document.getElementById('pager'), 1, 1, goLinkPage); return; } const now = Date.now(); el.innerHTML = links.map(l => { const namesHidden = l.show_student_names != null ? !l.show_student_names : linkSettings.share_show_student_names === 'false'; const expired = l.expires_at && new Date(l.expires_at).getTime() < now; const daysLeft = l.expires_at ? Math.ceil((new Date(l.expires_at).getTime() - now) / 86400000) : null; const soon = !expired && daysLeft !== null && daysLeft <= 1; return `
${esc(l.name)}${expired ? ' истекла' : soon ? ' истекает' : ''}
${l.group_name ? `Группа: ${esc(l.group_name)}` : ''} ${l.student_name ? `${l.group_name ? ' · ' : ''}Ученик: ${esc(l.student_name)}` : ''} ${l.date_from || l.date_to ? ` · период: ${l.date_from ? l.date_from.split('-').reverse().join('.') : '…'} — ${l.date_to ? l.date_to.split('-').reverse().join('.') : '…'}` : ' · весь период'} ${expired ? ` · срок действия истёк ${new Date(l.expires_at).toLocaleDateString('ru')}` : l.expires_at ? ` · истекает ${new Date(l.expires_at).toLocaleDateString('ru')}` : ''} ${namesHidden ? ` · имена скрыты` : ''} ${l.access_password_hash ? ` · 🔒 пароль` : ''} · создана ${new Date(l.created_at).toLocaleString('ru')}
Открыть Отчёт
`;}).join(''); renderPager(document.getElementById('pager'), linkPage, Math.max(1, Math.ceil(total / LINK_PAGE_SIZE)), goLinkPage); } function goLinkPage(p) { linkPage = p; loadLinks(); } function fmtDate(d) { const [y, m, day] = (d || '').split('-'); return d ? `${day}.${m}.${y}` : ''; } async function openLinkModal() { editingId = null; document.getElementById('linkModalTitle').textContent = 'Новая ссылка'; document.getElementById('linkName').value = ''; document.getElementById('linkDateFrom').value = ''; document.getElementById('linkDateTo').value = ''; document.getElementById('linkExpiresDays').value = '7'; document.getElementById('linkShowNames').checked = false; document.getElementById('linkPassword').value = ''; document.getElementById('linkMessage').value = ''; document.getElementById('linkUrl').value = ''; const s = await loadLinkSettings(); document.getElementById('linkShowNames').checked = s.share_show_student_names !== 'false'; document.getElementById('linkShowMessage').checked = s.share_show_student_message === 'true'; document.getElementById('linkShowDate').checked = s.share_show_entry_date === 'true'; document.getElementById('linkShowPhotos').checked = s.share_show_group_photos !== 'false'; if (!groups.length) { const res = await fetch(`${API}/api/groups`, { headers: hdr() }); groups = await res.json(); document.getElementById('linkGroup').innerHTML = '' + groups.map(g => ``).join(''); } if (!studentsLoaded) { const res = await fetch(`${API}/api/students/names`, { headers: hdr() }); const names = await res.json(); const dl = document.getElementById('linkStudentList'); dl.innerHTML = names.map(n => `' + groups.map(g => ``).join(''); } document.getElementById('linkGroup').value = l.group_id || ''; if (!studentsLoaded) { const res = await fetch(`${API}/api/students/names`, { headers: hdr() }); const names = await res.json(); const dl = document.getElementById('linkStudentList'); dl.innerHTML = names.map(n => `