Files
OpenLesson/public/info.js
2026-04-14 14:27:45 +05:00

209 lines
8.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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 = `<option value="">${defaultLabel}</option>`;
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 = '<tr><td colspan="9">Ошибка загрузки</td></tr>';
}
}
function renderTable(registrations) {
const tbody = document.getElementById('tableBody');
if (!registrations.length) {
tbody.innerHTML = '<tr><td colspan="9">Нет записей</td></tr>';
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 `
<tr>
<td>${escapeHtml(reg.parent_name)}</td>
<td>${escapeHtml(reg.parent_phone)}</td>
<td>${escapeHtml(reg.class_name)}</td>
<td>${escapeHtml(reg.subject)}</td>
<td>${escapeHtml(reg.teacher)}</td>
<td>${escapeHtml(reg.topic || '—')}</td>
<td>${dateStr}</td>
<td>${timeStr}</td>
<td>${new Date(reg.created_at).toLocaleString()}</td>
</tr>
`;
}).join('');
}
function escapeHtml(str) {
if (!str) return '';
return str.replace(/[&<>]/g, function(m) {
if (m === '&') return '&amp;';
if (m === '<') return '&lt;';
if (m === '>') return '&gt;';
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 = '/';
});
}