let links = [];
let groups = [];
let editingId = null;
let studentsLoaded = false;
function linkUrl(token) { return `${location.origin}/s/${token}`; }
async function loadLinks() {
const res = await fetch(`${API}/api/links`, { headers: hdr() });
links = await res.json();
const el = document.getElementById('linksList');
if (!links.length) { el.innerHTML = '
Ссылок пока нет. Создайте её в Журнале кнопкой «Ссылка».
'; return; }
el.innerHTML = links.map(l => `
${esc(l.name)}
${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('.') : '…'} ` : ' · весь период'}
${l.expires_at ? ` · истекает ${new Date(l.expires_at).toLocaleDateString('ru')}` : ''}
${l.anonymize_names ? ` · анонимизировано ` : ''}
${l.access_password_hash ? ` · 🔒 пароль ` : ''}
· создана ${new Date(l.created_at).toLocaleString('ru')}
`).join('');
}
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('linkAnonymizeNames').checked = false;
document.getElementById('linkPassword').value = '';
if (!groups.length) {
const res = await fetch(`${API}/api/groups`, { headers: hdr() });
groups = await res.json();
document.getElementById('linkGroup').innerHTML = '— без группы — ' +
groups.map(g => `${esc(g.name)} `).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 => ``).join('');
studentsLoaded = true;
}
document.getElementById('linkModal').classList.add('open');
}
async function openLinkEdit(id) {
const l = links.find(x => x.id === id);
if (!l) return;
editingId = id;
document.getElementById('linkModalTitle').textContent = 'Редактирование ссылки';
document.getElementById('linkName').value = l.name;
document.getElementById('linkDateFrom').value = l.date_from || '';
document.getElementById('linkDateTo').value = l.date_to || '';
document.getElementById('linkExpiresDays').value = l.expires_at ? Math.ceil((new Date(l.expires_at) - new Date()) / (1000 * 60 * 60 * 24)) : '7';
document.getElementById('linkAnonymizeNames').checked = l.anonymize_names || false;
document.getElementById('linkPassword').value = '';
if (!groups.length) {
const res = await fetch(`${API}/api/groups`, { headers: hdr() });
groups = await res.json();
document.getElementById('linkGroup').innerHTML = ' — без группы — ' +
groups.map(g => `${esc(g.name)} `).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 => ``).join('');
studentsLoaded = true;
}
document.getElementById('linkStudent').value = l.student_name || '';
document.getElementById('linkModal').classList.add('open');
}
function closeLinkModal() {
document.getElementById('linkModal').classList.remove('open');
editingId = null;
}
async function saveLink() {
const name = document.getElementById('linkName').value.trim();
const group_id = document.getElementById('linkGroup').value;
const student_name = document.getElementById('linkStudent').value;
const date_from = document.getElementById('linkDateFrom').value;
const date_to = document.getElementById('linkDateTo').value;
const expires_days = parseInt(document.getElementById('linkExpiresDays').value, 10);
const anonymize_names = document.getElementById('linkAnonymizeNames').checked;
const access_password = document.getElementById('linkPassword').value;
if (!name) { alert('Введите название'); return; }
if (expires_days < 1 || expires_days > 365) { alert('Дней истечения: 1-365'); return; }
const expires_at = new Date(Date.now() + expires_days * 24 * 60 * 60 * 1000).toISOString();
const body = JSON.stringify({ name, group_id: group_id || null, student_name: student_name || null, date_from: date_from || null, date_to: date_to || null, anonymize_names, expires_at, access_password: access_password || null });
const res = await fetch(editingId ? `${API}/api/links/${editingId}` : `${API}/api/links`, {
method: editingId ? 'PUT' : 'POST',
headers: hdrJson(),
body
});
if (res.ok) {
closeLinkModal();
loadLinks();
showToast(editingId ? 'Ссылка обновлена' : 'Ссылка создана');
} else {
const err = await res.json();
alert(err.error || 'Ошибка');
}
}
async function delLink(id) {
if (!confirm('Удалить ссылку?')) return;
await fetch(`${API}/api/links/${id}`, { method: 'DELETE', headers: hdr() });
loadLinks();
showToast('Ссылка удалена');
}
document.getElementById('linkModal').addEventListener('click', e => { if (e.target === e.currentTarget) closeLinkModal(); });
document.getElementById('linkCancelBtn').addEventListener('click', closeLinkModal);
document.getElementById('linkSaveBtn').addEventListener('click', saveLink);
document.getElementById('linksList').addEventListener('click', e => {
const cp = e.target.closest('[data-copy]');
if (cp) { copyText(cp.dataset.copy); return; }
const ed = e.target.closest('[data-edit-link]');
if (ed) { openLinkEdit(parseInt(ed.dataset.editLink, 10)); return; }
const dl = e.target.closest('[data-del-link]');
if (dl) { delLink(parseInt(dl.dataset.delLink, 10)); }
});
(async () => {
if (await checkAuth()) {
buildSidebar(document.body.dataset.page);
loadLinks();
}
})();