// public/info.js – страница просмотра записей let currentUser = null; let currentRegistrations = []; document.addEventListener('DOMContentLoaded', async () => { await checkAuth(); await loadFilterOptions(); loadRegistrations(); setupEventListeners(); }); async function checkAuth() { try { const res = await fetch('/api/me'); const data = await res.json(); if (!data.authenticated || (data.user.role !== 'admin' && data.user.role !== 'user')) { window.location.href = '/login.html'; return; } currentUser = data.user; document.getElementById('userInfo').innerHTML = `👋 ${currentUser.full_name} (${currentUser.role})`; } catch (err) { window.location.href = '/login.html'; } } async function loadFilterOptions() { try { const [classes, teachers, subjects] = await Promise.all([ fetch('/api/filter-options/class-names').then(r => r.json()), fetch('/api/filter-options/teachers').then(r => r.json()), fetch('/api/filter-options/subjects').then(r => r.json()) ]); populateSelect('filterClass', classes, 'Все классы'); populateSelect('filterTeacher', teachers, 'Все учителя'); populateSelect('filterSubject', subjects, 'Все предметы'); } catch (err) { console.error('Ошибка загрузки опций', err); } } function populateSelect(selectId, options, defaultLabel) { const select = document.getElementById(selectId); if (!select) return; select.innerHTML = ``; options.forEach(opt => { const option = document.createElement('option'); option.value = opt; option.textContent = opt; select.appendChild(option); }); } async function loadRegistrations() { const params = new URLSearchParams({ parent_name: document.getElementById('filterParentName').value, class_name: document.getElementById('filterClass').value, subject: document.getElementById('filterSubject').value, teacher: document.getElementById('filterTeacher').value }); try { const res = await fetch(`/api/info/registrations?${params}`); currentRegistrations = await res.json(); renderTable(currentRegistrations); document.getElementById('recordsCount').innerText = `Найдено: ${currentRegistrations.length}`; } catch (err) { console.error(err); document.getElementById('tableBody').innerHTML = 'Ошибка загрузки'; } } function renderTable(registrations) { const tbody = document.getElementById('tableBody'); if (!registrations.length) { tbody.innerHTML = 'Нет записей'; return; } tbody.innerHTML = registrations.map(reg => { let dateStr = '', timeStr = ''; if (reg.topic === 'Консультация' && reg.date && reg.time) { dateStr = escapeHtml(reg.date); timeStr = escapeHtml(reg.time); } else { dateStr = 'Согласно расписанию'; timeStr = ''; } return ` ${escapeHtml(reg.parent_name)} ${escapeHtml(reg.parent_phone)} ${escapeHtml(reg.class_name)} ${escapeHtml(reg.subject)} ${escapeHtml(reg.teacher)} ${escapeHtml(reg.topic || '—')} ${dateStr} ${timeStr} ${new Date(reg.created_at).toLocaleString()} `; }).join(''); } function escapeHtml(str) { if (!str) return ''; return str.replace(/[&<>]/g, function(m) { if (m === '&') return '&'; if (m === '<') return '<'; if (m === '>') return '>'; return m; }); } function exportToCSV() { if (!currentRegistrations.length) { alert('Нет данных для экспорта'); return; } const headers = ['ФИО родителя', 'Телефон', 'Класс', 'Предмет', 'Учитель', 'Тема урока', 'Дата урока', 'Время', 'Дата регистрации']; const rows = currentRegistrations.map(reg => { let dateVal = '', timeVal = ''; if (reg.topic === 'Консультация' && reg.date && reg.time) { dateVal = reg.date; timeVal = reg.time; } else { dateVal = 'Согласно расписанию'; timeVal = ''; } return [ reg.parent_name, reg.parent_phone, reg.class_name, reg.subject, reg.teacher, reg.topic || '', dateVal, timeVal, new Date(reg.created_at).toLocaleString() ]; }); const csvContent = [headers, ...rows] .map(row => row.map(cell => `"${String(cell).replace(/"/g, '""')}"`).join(';')) .join('\n'); const blob = new Blob(['\uFEFF' + csvContent], { type: 'text/csv;charset=utf-8;' }); const link = document.createElement('a'); const url = URL.createObjectURL(blob); link.href = url; link.setAttribute('download', 'zapis_roditelei.csv'); document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(url); } // Функция выгрузки уникальных родителей для охраны с автоматической шириной столбца async function exportForGuard() { try { const res = await fetch('/api/info/registrations'); const registrations = await res.json(); if (!registrations.length) { alert('Нет данных для экспорта'); return; } const uniqueMap = new Map(); registrations.forEach(reg => { if (!uniqueMap.has(reg.parent_name)) { uniqueMap.set(reg.parent_name, { 'ФИО родителя': reg.parent_name }); } }); const uniqueArray = Array.from(uniqueMap.values()); // Вычисляем максимальную длину ФИО let maxLen = 0; uniqueArray.forEach(item => { const len = item['ФИО родителя'].length; if (len > maxLen) maxLen = len; }); // Ширина столбца в Excel (в символах) – делаем запас +2, минимум 30 const colWidth = Math.max(maxLen + 2, 50); const ws = XLSX.utils.json_to_sheet(uniqueArray); // Устанавливаем ширину для столбца A (индекс 0) ws['!cols'] = [{ wch: colWidth }]; const wb = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, ws, 'Список для охраны'); XLSX.writeFile(wb, 'spisok_dlya_ohrany.xlsx'); } catch (err) { console.error(err); alert('Ошибка при выгрузке'); } } function setupEventListeners() { document.getElementById('applyFiltersBtn')?.addEventListener('click', () => loadRegistrations()); document.getElementById('resetFiltersBtn')?.addEventListener('click', () => { document.getElementById('filterParentName').value = ''; document.getElementById('filterClass').value = ''; document.getElementById('filterSubject').value = ''; document.getElementById('filterTeacher').value = ''; loadRegistrations(); }); document.getElementById('exportBtn')?.addEventListener('click', exportToCSV); document.getElementById('exportGuardBtn')?.addEventListener('click', exportForGuard); document.getElementById('logoutBtn')?.addEventListener('click', async () => { await fetch('/api/logout', { method: 'POST' }); window.location.href = '/'; }); }