Files
WhatIDo/public/dashboard.html
T

179 lines
7.4 KiB
HTML
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.
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Дашборд — Админ-панель</title>
<link rel="stylesheet" href="admin.css">
</head>
<body data-page="dashboard">
<div class="layout">
<div class="sidebar" id="sidebar"></div>
<div class="main">
<div class="page-head">
<h2>Дашборд</h2>
<p class="page-sub">Сводка по журналу и группам</p>
</div>
<div class="stats" id="stats"></div>
<div class="dash-grid">
<div class="dash-col">
<div class="dash-card">
<h3><span class="ic">📊</span> Динамика записей (14 дней)</h3>
<div class="chart" id="activityChart" title=""></div>
<div class="dash-link-row"><a href="journal.html">Открыть журнал →</a></div>
</div>
<div class="dash-card">
<h3><span class="ic">🕒</span> Последние записи</h3>
<div class="recent" id="recentList"></div>
<div class="dash-link-row"><a href="journal.html">Все записи →</a></div>
</div>
<div class="dash-card">
<h3><span class="ic">📸</span> Фото групп</h3>
<div class="photos-grid-dash" id="dashPhotos"></div>
<div class="dash-link-row"><a href="groups.html">Фото групп →</a></div>
</div>
</div>
<div class="dash-col">
<div class="dash-card">
<h3><span class="ic">⚡</span> Быстрые действия</h3>
<div class="actions-grid">
<a class="action-btn" href="journal.html"><span class="aic">📖</span>Журнал</a>
<a class="action-btn" href="files.html"><span class="aic">📎</span>Файлы</a>
<a class="action-btn" href="groups.html"><span class="aic">👥</span>Группы</a>
<a class="action-btn" href="links.html"><span class="aic">🔗</span>Ссылки</a>
<a class="action-btn" href="trash.html"><span class="aic">🗑️</span>Корзина</a>
<a class="action-btn" href="settings.html"><span class="aic">🛡️</span>Бэкап</a>
</div>
</div>
<div class="dash-card">
<h3><span class="ic">🟢</span> Активные сейчас группы</h3>
<div class="active-list" id="activeList"></div>
</div>
<div class="dash-card">
<h3><span class="ic">🏆</span> Топ учеников</h3>
<div class="top-list" id="topList"></div>
</div>
</div>
</div>
</div>
</div>
<div class="modal-overlay" id="imgModal" onclick="this.classList.remove('open')">
<img id="imgModalSrc" alt="">
</div>
<div class="toast" id="toast"></div>
<script src="admin.js"></script>
<script>
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) {
document.getElementById('stats').innerHTML = `
<div class="stat"><div class="num">${s.entries}</div><div class="label">Всего записей</div></div>
<div class="stat"><div class="num">${s.students}</div><div class="label">Учеников</div></div>
<div class="stat"><div class="num">${s.groups}</div><div class="label">Групп</div></div>
<div class="stat"><div class="num">${s.today}</div><div class="label">Сегодня</div></div>
<div class="stat"><div class="num" style="color:${s.trash ? '#ef4444' : 'var(--muted)'}">${s.trash}</div><div class="label"><a href="trash.html">В корзине →</a></div></div>
`;
}
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 => `
<div class="bar-col" title="${x.iso} — ${x.n} ${plural(x.n, 'запись', 'записи', 'записей')}">
<div class="bar ${x.iso === todayIso ? 'today' : ''}" style="height:${Math.round(x.n / max * 100)}%"></div>
<div class="bar-day">${x.label}</div>
</div>`).join('');
}
function renderRecent(rows) {
const el = document.getElementById('recentList');
if (!rows.length) { el.innerHTML = '<div class="dash-empty">Записей пока нет</div>'; return; }
el.innerHTML = rows.map(e => `
<div class="recent-item">
${e.photo_path
? `<img src="${API}${e.photo_path}" onclick="showImg('${API}${e.photo_path}')" alt="">`
: `<div class="thumb">🎓</div>`}
<div class="r-info">
<div class="r-name">${esc(e.student_name)}</div>
<div class="r-meta">${esc(e.group_name)} · ${new Date(e.created_at).toLocaleString('ru', { day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit' })}</div>
</div>
<a class="r-link" href="journal.html" title="В журнал">→</a>
</div>`).join('');
}
function renderActive(rows) {
const el = document.getElementById('activeList');
if (!rows.length) { el.innerHTML = '<div class="dash-empty">Сейчас нет идущих групп</div>'; return; }
el.innerHTML = rows.map(g => `
<div class="active-item">
<span class="a-name">${esc(g.name)}</span>
<span class="a-right">
<span class="a-time">${g.day_label} ${g.time_start}–${g.time_end}</span>
<span class="a-now">сейчас</span>
</span>
</div>`).join('');
}
function renderTop(rows) {
const el = document.getElementById('topList');
if (!rows.length) { el.innerHTML = '<div class="dash-empty">Пока нет данных</div>'; return; }
el.innerHTML = rows.map((x, i) => `
<div class="top-item">
<span class="top-rank ${i > 2 ? 'alt' : ''}">${i + 1}</span>
<span class="top-name">${esc(x.student_name)}</span>
<span class="top-count">${x.n}</span>
</div>`).join('');
}
function renderPhotos(rows) {
const el = document.getElementById('dashPhotos');
if (!rows.length) { el.innerHTML = '<div class="dash-empty">Фото групп пока нет</div>'; return; }
el.innerHTML = rows.map(p => `
<div class="dash-photo" title="${esc(p.group_name)}">
<img src="${API}${p.photo_path}" onclick="showImg('${API}${p.photo_path}')" alt="">
${p.caption ? `<div class="pcap">${esc(p.caption)}</div>` : ''}
</div>`).join('');
}
async function loadDashboard() {
const res = await fetch(`${API}/api/dashboard`, { headers: hdr() });
if (!res.ok) { document.getElementById('stats').innerHTML = '<div class="dash-empty">Не удалось загрузить данные</div>'; return; }
const d = await res.json();
renderStats(d.stats);
renderActivity(d.activity);
renderRecent(d.recent_entries);
renderActive(d.active_groups);
renderTop(d.top_students);
renderPhotos(d.photos);
}
(async () => {
if (await checkAuth()) {
buildSidebar(document.body.dataset.page);
loadDashboard();
}
})();
</script>
</body>
</html>