function plural(n, one, few, many) { n = Math.abs(n) % 100; const n1 = n % 10; if (n > 10 && n < 20) return many; if (n1 > 1 && n1 < 5) return few; if (n1 === 1) return one; return many; } function renderStats(s, disk) { let diskHtml = ''; if (disk) { const pct = disk.used_pct; const color = pct > 90 ? '#ef4444' : pct > 70 ? '#f59e0b' : '#16a34a'; diskHtml = `
${disk.used} / ${disk.total}
Диск: свободно ${disk.free} (${100 - pct}%)
`; } document.getElementById('stats').innerHTML = `
${s.entries}
Всего записей
${s.students}
Учеников
${s.groups}
Групп
${s.today}
Сегодня
${s.trash}
В корзине →
${diskHtml} `; } function last14days() { const out = []; for (let i = 13; i >= 0; i--) { const d = new Date(); d.setDate(d.getDate() - i); out.push(d); } return out; } function renderActivity(activity) { const map = {}; activity.forEach(a => { map[a.d] = a.n; }); const days = last14days().map(d => { const iso = d.toISOString().slice(0, 10); return { iso, label: d.toLocaleDateString('ru', { day: '2-digit', month: '2-digit' }), n: map[iso] || 0 }; }); const max = Math.max(1, ...days.map(x => x.n)); const todayIso = new Date().toISOString().slice(0, 10); document.getElementById('activityChart').innerHTML = days.map(x => `
${x.label}
`).join(''); } function renderRecent(rows) { const el = document.getElementById('recentList'); if (!rows.length) { el.innerHTML = '
Записей пока нет
'; return; } el.innerHTML = rows.map(e => `
${e.photo_path ? `` : `
`}
${esc(e.student_name)}
${esc(e.group_name)} · ${new Date(e.created_at).toLocaleString('ru', { day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit' })}
→
`).join(''); } function renderActive(rows) { const el = document.getElementById('activeList'); if (!rows.length) { el.innerHTML = '
Сейчас нет идущих групп
'; return; } el.innerHTML = rows.map(g => `
${esc(g.name)} ${g.day_label} ${g.time_start}–${g.time_end} сейчас
`).join(''); } function renderTop(rows) { const el = document.getElementById('topList'); if (!rows.length) { el.innerHTML = '
Пока нет данных
'; return; } el.innerHTML = rows.map((x, i) => `
${i + 1} ${esc(x.student_name)} ${x.n}
`).join(''); } function renderPhotos(rows) { const el = document.getElementById('dashPhotos'); if (!rows.length) { el.innerHTML = '
Фото групп пока нет
'; return; } el.innerHTML = rows.map(p => `
${p.caption ? `
${esc(p.caption)}
` : ''}
`).join(''); } function renderLessons(rows) { const el = document.getElementById('lessonList'); if (!el) return; if (!rows.length) { el.innerHTML = '
Отчётов пока нет
'; return; } el.innerHTML = rows.map(r => { const date = String(r.lesson_date || '').slice(0, 10).split('-').reverse().join('.'); const time = r.lesson_time ? String(r.lesson_time).slice(0, 5) : ''; return `
${esc(r.group_name)} ${esc(date + (time ? ' · ' + time : ''))}
${esc(r.text)}
`; }).join(''); } async function loadDashboard() { const res = await fetch(`${API}/api/dashboard`, { headers: hdr() }); if (!res.ok) { document.getElementById('stats').innerHTML = '
Не удалось загрузить данные
'; return; } const d = await res.json(); renderStats(d.stats, d.disk); renderActivity(d.activity); renderRecent(d.recent_entries); renderActive(d.active_groups); renderTop(d.top_students); renderPhotos(d.photos); renderLessons(d.recent_lessons || []); renderIcons(); } document.getElementById('imgModal').addEventListener('click', e => e.currentTarget.classList.remove('open')); const lessonBtn = document.getElementById('lessonReportBtn'); if (lessonBtn) lessonBtn.addEventListener('click', () => openLessonModal()); function onLessonReportSaved() { loadDashboard(); } document.addEventListener('click', e => { const item = e.target.closest('[data-lesson]'); if (item && !e.target.closest('button')) openLessonModal({ id: item.dataset.lesson }); }); (async () => { if (await checkAuth()) { buildSidebar(document.body.dataset.page); loadDashboard(); } })();