feat(teacher-guide): admin-only блок руководства (главы A1-A6)
Видимость по роли:
- Teacher: главы 1-17 без admin-секций (убраны 14.4/16.3/17 → перенесены в A3/A4)
- Admin: дополнительный блок A1-A6 (isAdmin → display:none → show)
Руководство администратора (6 глав):
- A1: Командный центр — KPI, очередь триажа, лента завершений
- A2: Пользователи — список, карточка (роль/блок/история/удаление), Ctrl+K поиск
- A3: Контент и доступ — allowlist учебников, симуляции, feature flags
- A4: Геймификация — статистика, начисление XP/монет с пресетами, сброс прогресса
- A5: Аудит и безопасность — аудит-лог, RBAC, модерация аватаров
- A6: System Health — CPU/RAM/event loop, HTTP-статистика, журнал ошибок
Технические изменения:
- initPage → const { isAdmin }
- ALL_CHAPTERS() = CHAPTERS + (isAdmin ? ADMIN_CHAPTERS : [])
- admin nav в sidebar (tg-nav-admin), admin chapters в tg-admin-content
- scrollToSection/updateReadUI/initHash используют ALL_CHAPTERS()
- прогресс-бар считает все главы (17 или 23 в зависимости от роли)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+406
-13
@@ -375,6 +375,42 @@
|
||||
.tg-section.search-hidden { display: none; }
|
||||
.tg-chapter.search-hidden { display: none; }
|
||||
|
||||
/* Admin guide block */
|
||||
.tg-admin-divider {
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
margin: 22px 0 10px;
|
||||
font-size: 0.62rem; font-weight: 700; letter-spacing: 0.1em;
|
||||
text-transform: uppercase; color: var(--text-3);
|
||||
}
|
||||
.tg-admin-divider::before, .tg-admin-divider::after {
|
||||
content: ''; flex: 1; height: 1px; background: rgba(15,23,42,0.09);
|
||||
}
|
||||
.tg-nav-chapter.admin .tg-nav-ch-btn { color: #c0306a; }
|
||||
.tg-nav-chapter.admin .tg-nav-ch-btn .tg-nav-ch-icon svg { stroke: #c0306a; }
|
||||
.tg-nav-chapter.admin .tg-nav-ch-btn:hover,
|
||||
.tg-nav-chapter.admin .tg-nav-ch-btn.active { background: rgba(241,91,181,0.07); }
|
||||
.tg-admin-badge {
|
||||
display: inline-flex; align-items: center; gap: 5px;
|
||||
padding: 3px 10px; border-radius: 99px;
|
||||
background: rgba(241,91,181,0.1); color: #c0306a;
|
||||
font-size: 0.68rem; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase;
|
||||
margin-left: auto;
|
||||
}
|
||||
.tg-admin-badge svg { width: 12px; height: 12px; stroke: currentColor; }
|
||||
.tg-chapter.admin-chapter .tg-chapter-header { background: linear-gradient(135deg,rgba(241,91,181,0.04),rgba(155,93,229,0.04)); border-radius: 16px; margin-bottom: 4px; }
|
||||
.tg-chapter.admin-chapter .tg-chapter-icon svg { stroke: #c0306a; }
|
||||
.tg-chapter.admin-chapter .tg-chapter-num { color: #c0306a; }
|
||||
.tg-chapter.admin-chapter .tg-chapter-try { background: rgba(241,91,181,0.1); color: #c0306a; border: 1.5px solid rgba(241,91,181,0.25); }
|
||||
.tg-chapter.admin-chapter .tg-chapter-try:hover { background: #c0306a; color: #fff; }
|
||||
.tg-chapter-admin-header {
|
||||
display: inline-flex; align-items: center; gap: 7px;
|
||||
padding: 6px 14px; border-radius: 99px;
|
||||
background: rgba(241,91,181,0.1); color: #c0306a;
|
||||
font-size: 0.76rem; font-weight: 700;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
.tg-chapter-admin-header svg { width: 14px; height: 14px; stroke: currentColor; }
|
||||
|
||||
/* Mobile */
|
||||
@media (max-width: 1024px) {
|
||||
.tg-layout { padding: 0 16px 60px; gap: 0; }
|
||||
@@ -409,6 +445,10 @@
|
||||
<input type="text" id="tg-search" placeholder="Поиск по руководству..." />
|
||||
</div>
|
||||
<div id="tg-nav-chapters"></div>
|
||||
<div id="tg-nav-admin" style="display:none">
|
||||
<div class="tg-admin-divider"><i data-lucide="shield" style="width:13px;height:13px;stroke:#c0306a;vertical-align:-2px"></i> Только для администратора</div>
|
||||
<div id="tg-nav-admin-chapters"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- ══ CONTENT ══ -->
|
||||
@@ -1248,9 +1288,6 @@
|
||||
</div>
|
||||
<div class="tg-tip"><div class="tg-box-icon"><i data-lucide="monitor"></i></div><div class="tg-box-body"><div class="tg-box-label">На онлайн-уроке</div>Откройте симуляцию в соседней вкладке и включите демонстрацию экрана — ученики увидят 3D-вращение в реальном времени. Или используйте режим аннотации прямо поверх симуляции на доске.</div></div>
|
||||
</div>
|
||||
|
||||
<div class="tg-section" id="s-14-4">
|
||||
<div class="tg-section-title">14.4 Управление симуляциями (для администратора)</div>
|
||||
<p>В панели администратора → вкладка «Симуляции»:</p>
|
||||
<div class="tg-steps">
|
||||
<div class="tg-step"><div class="tg-step-num">—</div><div class="tg-step-body">Включить/выключить конкретную симуляцию из каталога.</div></div>
|
||||
@@ -1354,8 +1391,6 @@
|
||||
<div class="tg-tip"><div class="tg-box-icon"><i data-lucide="star"></i></div><div class="tg-box-body"><div class="tg-box-label">Радужный ошейник</div>При стрике 7+ дней подряд питомец получает анимированный радужный ошейник. Отличный мотиватор для регулярной работы!</div></div>
|
||||
</div>
|
||||
|
||||
<div class="tg-section" id="s-16-3">
|
||||
<div class="tg-section-title">16.3 Начисление XP администратором</div>
|
||||
<p>В панели администратора → вкладка <b>«Геймификация»</b> → раздел <b>«Начислить XP / Монеты»</b>:</p>
|
||||
<div class="tg-steps">
|
||||
<div class="tg-step"><div class="tg-step-num">1</div><div class="tg-step-body">Выберите ученика из списка (есть фильтр по имени).</div></div>
|
||||
@@ -1437,6 +1472,322 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ═══ ADMIN GUIDE (только для роли admin) ═══ -->
|
||||
<div id="tg-admin-content" style="display:none">
|
||||
|
||||
<!-- ═══ CHAPTER A1 — OVERVIEW ═══ -->
|
||||
<div class="tg-chapter admin-chapter" id="ch-a1">
|
||||
<div class="tg-chapter-header">
|
||||
<div class="tg-chapter-icon"><i data-lucide="layout-dashboard"></i></div>
|
||||
<div class="tg-chapter-meta">
|
||||
<div class="tg-chapter-num" style="color:#c0306a">Глава A1</div>
|
||||
<div class="tg-chapter-title">Командный центр</div>
|
||||
</div>
|
||||
<a href="/dashboard" class="tg-chapter-try" target="_blank"><i data-lucide="external-link"></i> Дашборд</a>
|
||||
</div>
|
||||
<div class="tg-chapter-admin-header"><i data-lucide="shield"></i> Только для администратора</div>
|
||||
|
||||
<div class="tg-section" id="s-a1-1">
|
||||
<div class="tg-section-title">A1.1 Главная страница администратора</div>
|
||||
<p>После входа администратор попадает на <b>командный центр</b> — специальный дашборд вместо ученического. Показывает состояние платформы за последние 24 часа.</p>
|
||||
<div class="tg-tools-grid">
|
||||
<div class="tg-tool-card"><div class="tg-tool-icon"><i data-lucide="play-circle"></i></div><div><div class="tg-tool-name">Сессий запущено</div><div class="tg-tool-desc">Со спарклайном за 7 дней</div></div></div>
|
||||
<div class="tg-tool-card"><div class="tg-tool-icon"><i data-lucide="activity"></i></div><div><div class="tg-tool-name">Активные пользователи</div><div class="tg-tool-desc">За последние 24 ч</div></div></div>
|
||||
<div class="tg-tool-card"><div class="tg-tool-icon"><i data-lucide="user-plus"></i></div><div><div class="tg-tool-name">Новые регистрации</div><div class="tg-tool-desc">За последние 24 ч</div></div></div>
|
||||
<div class="tg-tool-card"><div class="tg-tool-icon"><i data-lucide="check-circle"></i></div><div><div class="tg-tool-name">Завершаемость</div><div class="tg-tool-desc">Доля незаброшенных сессий</div></div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tg-section" id="s-a1-2">
|
||||
<div class="tg-section-title">A1.2 Очередь триажа «Требует внимания»</div>
|
||||
<p>Центральный блок дашборда — единая очередь событий, требующих действия. Фильтруется по вкладкам:</p>
|
||||
<div class="tg-steps">
|
||||
<div class="tg-step"><div class="tg-step-num">—</div><div class="tg-step-body"><b>Блокировки</b> — пользователи, заблокированные за неделю. Кнопка «Открыть» ведёт на карточку пользователя.</div></div>
|
||||
<div class="tg-step"><div class="tg-step-num">—</div><div class="tg-step-body"><b>Зависшие</b> — тестовые сессии, которые не завершились более 1 часа. Кнопка «Сессия» ведёт к ней.</div></div>
|
||||
<div class="tg-step"><div class="tg-step-num">—</div><div class="tg-step-body"><b>Брошенные</b> — всплеск прерванных сессий за 24ч (сигнал проблемы).</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tg-section" id="s-a1-3">
|
||||
<div class="tg-section-title">A1.3 Лента завершений и статистика контента</div>
|
||||
<p><b>Лента завершений</b> (правая колонка) — поток последних завершённых сессий с % и распределением по предметам.</p>
|
||||
<p><b>Плитки контента</b> ниже: число вопросов, тестов, курсов и классов в системе.</p>
|
||||
<p><b>Результаты дня</b>: Топ-5 (лучшие результаты) и «Нужна помощь» (ниже 50%) — быстрый способ выявить учеников, которым сложно.</p>
|
||||
</div>
|
||||
|
||||
<div class="tg-chapter-nav">
|
||||
<div class="tg-ch-nav-btn next" onclick="scrollToChapter('ch-a2')" style="text-align:right">
|
||||
<div class="tg-ch-nav-icon"><i data-lucide="arrow-right"></i></div>
|
||||
<div><div class="tg-ch-nav-label">Следующая глава</div><div class="tg-ch-nav-title">Управление пользователями</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ═══ CHAPTER A2 — USERS ═══ -->
|
||||
<div class="tg-chapter admin-chapter" id="ch-a2">
|
||||
<div class="tg-chapter-header">
|
||||
<div class="tg-chapter-icon"><i data-lucide="users"></i></div>
|
||||
<div class="tg-chapter-meta">
|
||||
<div class="tg-chapter-num" style="color:#c0306a">Глава A2</div>
|
||||
<div class="tg-chapter-title">Управление пользователями</div>
|
||||
</div>
|
||||
<a href="/admin#users" class="tg-chapter-try" target="_blank"><i data-lucide="external-link"></i> Пользователи</a>
|
||||
</div>
|
||||
<div class="tg-chapter-admin-header"><i data-lucide="shield"></i> Только для администратора</div>
|
||||
|
||||
<div class="tg-section" id="s-a2-1">
|
||||
<div class="tg-section-title">A2.1 Список пользователей</div>
|
||||
<p>Панель администратора → вкладка <b>«Пользователи»</b>. Отображает всех зарегистрированных пользователей: имя, email, роль, дата регистрации, последний вход, количество тестов, средний балл.</p>
|
||||
<p>Фильтры: по роли (admin/teacher/student/free_student), поиск по имени/email, пагинация (cursor-based).</p>
|
||||
</div>
|
||||
|
||||
<div class="tg-section" id="s-a2-2">
|
||||
<div class="tg-section-title">A2.2 Карточка пользователя</div>
|
||||
<p>Нажмите на пользователя → откроется его детальная карточка:</p>
|
||||
<div class="tg-steps">
|
||||
<div class="tg-step"><div class="tg-step-num">—</div><div class="tg-step-body"><b>Смена роли</b> — student / teacher / admin / free_student. Изменение инвалидирует токен, пользователь перелогинится.</div></div>
|
||||
<div class="tg-step"><div class="tg-step-num">—</div><div class="tg-step-body"><b>Блокировка / разблокировка</b> — заблокированный пользователь не может войти.</div></div>
|
||||
<div class="tg-step"><div class="tg-step-num">—</div><div class="tg-step-body"><b>История сессий</b> — все тестовые сессии пользователя с результатами.</div></div>
|
||||
<div class="tg-step"><div class="tg-step-num">—</div><div class="tg-step-body"><b>Удаление</b> — полное удаление пользователя и всех его данных.</div></div>
|
||||
</div>
|
||||
<div class="tg-warning"><div class="tg-box-icon"><i data-lucide="alert-triangle"></i></div><div class="tg-box-body"><div class="tg-box-label">Нельзя изменить свою роль</div>Администратор не может сменить роль самому себе — защита от случайного лишения доступа.</div></div>
|
||||
</div>
|
||||
|
||||
<div class="tg-section" id="s-a2-3">
|
||||
<div class="tg-section-title">A2.3 Глобальный поиск (Ctrl+K)</div>
|
||||
<p>В любом разделе админ-панели нажмите <b>Ctrl+K</b> (или кнопку в шапке) — откроется command palette:</p>
|
||||
<div class="tg-steps">
|
||||
<div class="tg-step"><div class="tg-step-num">—</div><div class="tg-step-body">Поиск по пользователям (имя / email) — результаты кликабельны, ведут на карточку.</div></div>
|
||||
<div class="tg-step"><div class="tg-step-num">—</div><div class="tg-step-body">Поиск по тестам — открывает редактирование теста.</div></div>
|
||||
<div class="tg-step"><div class="tg-step-num">—</div><div class="tg-step-body">Поиск по классам — открывает карточку класса.</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tg-chapter-nav">
|
||||
<div class="tg-ch-nav-btn prev" onclick="scrollToChapter('ch-a1')">
|
||||
<div class="tg-ch-nav-icon"><i data-lucide="arrow-left"></i></div>
|
||||
<div><div class="tg-ch-nav-label">Предыдущая глава</div><div class="tg-ch-nav-title">Командный центр</div></div>
|
||||
</div>
|
||||
<div class="tg-ch-nav-btn next" onclick="scrollToChapter('ch-a3')" style="text-align:right">
|
||||
<div class="tg-ch-nav-icon"><i data-lucide="arrow-right"></i></div>
|
||||
<div><div class="tg-ch-nav-label">Следующая глава</div><div class="tg-ch-nav-title">Контент и доступ</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ═══ CHAPTER A3 — CONTENT & ACCESS ═══ -->
|
||||
<div class="tg-chapter admin-chapter" id="ch-a3">
|
||||
<div class="tg-chapter-header">
|
||||
<div class="tg-chapter-icon"><i data-lucide="book-lock"></i></div>
|
||||
<div class="tg-chapter-meta">
|
||||
<div class="tg-chapter-num" style="color:#c0306a">Глава A3</div>
|
||||
<div class="tg-chapter-title">Контент и доступ к учебникам</div>
|
||||
</div>
|
||||
<a href="/admin#access" class="tg-chapter-try" target="_blank"><i data-lucide="external-link"></i> Доступ</a>
|
||||
</div>
|
||||
<div class="tg-chapter-admin-header"><i data-lucide="shield"></i> Только для администратора</div>
|
||||
|
||||
<div class="tg-section" id="s-a3-1">
|
||||
<div class="tg-section-title">A3.1 Allowlist — открытие учебников</div>
|
||||
<p>Вкладка <b>«Доступ к контенту»</b>. Ученики видят только те учебники/экзамены, которые администратор явно открыл.</p>
|
||||
<div class="tg-steps">
|
||||
<div class="tg-step"><div class="tg-step-num">1</div><div class="tg-step-body">Выберите учебник или экзамен.</div></div>
|
||||
<div class="tg-step"><div class="tg-step-num">2</div><div class="tg-step-body">Добавьте <b>класс</b> — все ученики класса получат доступ.</div></div>
|
||||
<div class="tg-step"><div class="tg-step-num">3</div><div class="tg-step-body">Или добавьте <b>конкретного ученика</b> — приоритет выше, чем у класса.</div></div>
|
||||
</div>
|
||||
<div class="tg-tip"><div class="tg-box-icon"><i data-lucide="shield-check"></i></div><div class="tg-box-body"><div class="tg-box-label">Администраторы и учителя</div>Всегда видят весь контент независимо от allowlist.</div></div>
|
||||
</div>
|
||||
|
||||
<div class="tg-section" id="s-a3-2">
|
||||
<div class="tg-section-title">A3.2 Управление симуляциями</div>
|
||||
<p>Вкладка <b>«Симуляции»</b> — каталог всех 40 симуляций лаборатории:</p>
|
||||
<div class="tg-steps">
|
||||
<div class="tg-step"><div class="tg-step-num">—</div><div class="tg-step-body"><b>Включить/выключить</b> — отключённая симуляция не показывается ученикам.</div></div>
|
||||
<div class="tg-step"><div class="tg-step-num">—</div><div class="tg-step-body"><b>Featured</b> — отметить как рекомендованную (выводится вперёд).</div></div>
|
||||
<div class="tg-step"><div class="tg-step-num">—</div><div class="tg-step-body"><b>Редактор связей</b> — привязать симуляцию к параграфам учебников (кнопка «В лабораторию» в учебнике).</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tg-section" id="s-a3-3">
|
||||
<div class="tg-section-title">A3.3 Feature Flags</div>
|
||||
<p>Включение/отключение целых модулей платформы. Изменения вступают в силу немедленно — без перезапуска сервера.</p>
|
||||
<div class="tg-steps">
|
||||
<div class="tg-step"><div class="tg-step-num">—</div><div class="tg-step-body"><b>Биохимия, Учебники, Флэшкарты, Доска</b> — основные образовательные модули.</div></div>
|
||||
<div class="tg-step"><div class="tg-step-num">—</div><div class="tg-step-body"><b>Live-квиз</b> — по умолчанию выключен, включите если нужен.</div></div>
|
||||
<div class="tg-step"><div class="tg-step-num">—</div><div class="tg-step-body"><b>Экзамен-9</b> — 80 вариантов по математике 9 класса.</div></div>
|
||||
<div class="tg-step"><div class="tg-step-num">—</div><div class="tg-step-body"><b>Симуляции отдельно</b> — отключить весь модуль лаборатории или конкретные симуляции по id.</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tg-chapter-nav">
|
||||
<div class="tg-ch-nav-btn prev" onclick="scrollToChapter('ch-a2')">
|
||||
<div class="tg-ch-nav-icon"><i data-lucide="arrow-left"></i></div>
|
||||
<div><div class="tg-ch-nav-label">Предыдущая глава</div><div class="tg-ch-nav-title">Управление пользователями</div></div>
|
||||
</div>
|
||||
<div class="tg-ch-nav-btn next" onclick="scrollToChapter('ch-a4')" style="text-align:right">
|
||||
<div class="tg-ch-nav-icon"><i data-lucide="arrow-right"></i></div>
|
||||
<div><div class="tg-ch-nav-label">Следующая глава</div><div class="tg-ch-nav-title">Геймификация (admin)</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ═══ CHAPTER A4 — GAMIFICATION ADMIN ═══ -->
|
||||
<div class="tg-chapter admin-chapter" id="ch-a4">
|
||||
<div class="tg-chapter-header">
|
||||
<div class="tg-chapter-icon"><i data-lucide="zap"></i></div>
|
||||
<div class="tg-chapter-meta">
|
||||
<div class="tg-chapter-num" style="color:#c0306a">Глава A4</div>
|
||||
<div class="tg-chapter-title">Геймификация — панель администратора</div>
|
||||
</div>
|
||||
<a href="/admin#gam" class="tg-chapter-try" target="_blank"><i data-lucide="external-link"></i> Геймификация</a>
|
||||
</div>
|
||||
<div class="tg-chapter-admin-header"><i data-lucide="shield"></i> Только для администратора</div>
|
||||
|
||||
<div class="tg-section" id="s-a4-1">
|
||||
<div class="tg-section-title">A4.1 Статистика геймификации</div>
|
||||
<p>Вкладка <b>«Геймификация»</b> — сводка по всей платформе:</p>
|
||||
<div class="tg-steps">
|
||||
<div class="tg-step"><div class="tg-step-num">—</div><div class="tg-step-body"><b>Суммарный XP</b> и монеты — накоплено за всё время.</div></div>
|
||||
<div class="tg-step"><div class="tg-step-num">—</div><div class="tg-step-body"><b>Средний уровень</b> — средний уровень всех пользователей.</div></div>
|
||||
<div class="tg-step"><div class="tg-step-num">—</div><div class="tg-step-body"><b>Достижений выдано</b> — сколько ачивок получено суммарно.</div></div>
|
||||
<div class="tg-step"><div class="tg-step-num">—</div><div class="tg-step-body"><b>Топ-10 по XP</b> — рейтинговая таблица. <b>Последние начисления XP</b> — с читаемыми подписями (не raw-коды).</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tg-section" id="s-a4-2">
|
||||
<div class="tg-section-title">A4.2 Начисление XP и монет</div>
|
||||
<p>Раздел «Начислить XP / Монеты»:</p>
|
||||
<div class="tg-steps">
|
||||
<div class="tg-step"><div class="tg-step-num">1</div><div class="tg-step-body">Выберите пользователя из списка. Используйте поле <b>фильтра</b> для быстрого поиска по имени.</div></div>
|
||||
<div class="tg-step"><div class="tg-step-num">2</div><div class="tg-step-body">Задайте <b>XP</b> через пресеты (0 / +10 / +25 / +50 / +100 / +250) или введите вручную. <b>Значение 0 — не начисляется.</b></div></div>
|
||||
<div class="tg-step"><div class="tg-step-num">3</div><div class="tg-step-body">Задайте <b>Монеты</b> аналогично (0 / +10 / +25 / +50).</div></div>
|
||||
<div class="tg-step"><div class="tg-step-num">4</div><div class="tg-step-body">Выберите причину (быстрые кнопки: «Ручное начисление», «Мероприятие», «Активность», «Бонус», «Компенсация») или введите свою.</div></div>
|
||||
<div class="tg-step"><div class="tg-step-num">5</div><div class="tg-step-body">Нажмите <b>«Начислить»</b>. Пользователь получит XP/монеты немедленно; статистика обновится.</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tg-section" id="s-a4-3">
|
||||
<div class="tg-section-title">A4.3 Сброс прогресса</div>
|
||||
<p>Раздел «Сбросить прогресс пользователя» — удаляет весь XP, монеты, достижения и историю начислений выбранного пользователя.</p>
|
||||
<div class="tg-warning"><div class="tg-box-icon"><i data-lucide="alert-triangle"></i></div><div class="tg-box-body"><div class="tg-box-label">Необратимо</div>Данные удаляются безвозвратно. Система запросит подтверждение перед сбросом. Используйте только при явной необходимости (тест-аккаунты, ошибочные начисления).</div></div>
|
||||
</div>
|
||||
|
||||
<div class="tg-chapter-nav">
|
||||
<div class="tg-ch-nav-btn prev" onclick="scrollToChapter('ch-a3')">
|
||||
<div class="tg-ch-nav-icon"><i data-lucide="arrow-left"></i></div>
|
||||
<div><div class="tg-ch-nav-label">Предыдущая глава</div><div class="tg-ch-nav-title">Контент и доступ</div></div>
|
||||
</div>
|
||||
<div class="tg-ch-nav-btn next" onclick="scrollToChapter('ch-a5')" style="text-align:right">
|
||||
<div class="tg-ch-nav-icon"><i data-lucide="arrow-right"></i></div>
|
||||
<div><div class="tg-ch-nav-label">Следующая глава</div><div class="tg-ch-nav-title">Аудит и безопасность</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ═══ CHAPTER A5 — AUDIT & SECURITY ═══ -->
|
||||
<div class="tg-chapter admin-chapter" id="ch-a5">
|
||||
<div class="tg-chapter-header">
|
||||
<div class="tg-chapter-icon"><i data-lucide="file-text"></i></div>
|
||||
<div class="tg-chapter-meta">
|
||||
<div class="tg-chapter-num" style="color:#c0306a">Глава A5</div>
|
||||
<div class="tg-chapter-title">Аудит и безопасность</div>
|
||||
</div>
|
||||
<a href="/admin#sublog" class="tg-chapter-try" target="_blank"><i data-lucide="external-link"></i> Аудит-лог</a>
|
||||
</div>
|
||||
<div class="tg-chapter-admin-header"><i data-lucide="shield"></i> Только для администратора</div>
|
||||
|
||||
<div class="tg-section" id="s-a5-1">
|
||||
<div class="tg-section-title">A5.1 Аудит-лог</div>
|
||||
<p>Вкладка <b>«Аудит-лог»</b> — хронология всех административных действий: кто, что, когда. Записываются:</p>
|
||||
<div class="tg-steps">
|
||||
<div class="tg-step"><div class="tg-step-num">—</div><div class="tg-step-body">Смены роли пользователей.</div></div>
|
||||
<div class="tg-step"><div class="tg-step-num">—</div><div class="tg-step-body">Блокировки и разблокировки.</div></div>
|
||||
<div class="tg-step"><div class="tg-step-num">—</div><div class="tg-step-body">Начисления XP/монет администратором.</div></div>
|
||||
<div class="tg-step"><div class="tg-step-num">—</div><div class="tg-step-body">Сброс прогресса пользователей.</div></div>
|
||||
<div class="tg-step"><div class="tg-step-num">—</div><div class="tg-step-body">Изменения разрешений (RBAC).</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tg-section" id="s-a5-2">
|
||||
<div class="tg-section-title">A5.2 Разрешения (RBAC)</div>
|
||||
<p>Вкладка <b>«Разрешения»</b> — гранулярный контроль доступа. Два уровня:</p>
|
||||
<div class="tg-steps">
|
||||
<div class="tg-step"><div class="tg-step-num">—</div><div class="tg-step-body"><b>Per-role</b> — правило для роли целиком (например, разрешить teachers доступ к аналитике).</div></div>
|
||||
<div class="tg-step"><div class="tg-step-num">—</div><div class="tg-step-body"><b>Per-user</b> — правило для конкретного пользователя (приоритет выше, чем у роли).</div></div>
|
||||
</div>
|
||||
<div class="tg-tip"><div class="tg-box-icon"><i data-lucide="info"></i></div><div class="tg-box-body"><div class="tg-box-label">free_student</div>Роль с минимальными правами — гибко настраивается через разрешения. Используйте для демо-аккаунтов или ограниченного доступа.</div></div>
|
||||
</div>
|
||||
|
||||
<div class="tg-section" id="s-a5-3">
|
||||
<div class="tg-section-title">A5.3 Модерация аватаров</div>
|
||||
<p>Ученики загружают фото профиля — оно не показывается другим до подтверждения.</p>
|
||||
<div class="tg-steps">
|
||||
<div class="tg-step"><div class="tg-step-num">1</div><div class="tg-step-body">В <b>«Пользователи»</b> → карточка ученика → вкладка «Аватар».</div></div>
|
||||
<div class="tg-step"><div class="tg-step-num">2</div><div class="tg-step-body"><b>Принять</b> — аватар начнёт отображаться везде. <b>Отклонить</b> — аватар сбрасывается, ученик получает уведомление.</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tg-chapter-nav">
|
||||
<div class="tg-ch-nav-btn prev" onclick="scrollToChapter('ch-a4')">
|
||||
<div class="tg-ch-nav-icon"><i data-lucide="arrow-left"></i></div>
|
||||
<div><div class="tg-ch-nav-label">Предыдущая глава</div><div class="tg-ch-nav-title">Геймификация (admin)</div></div>
|
||||
</div>
|
||||
<div class="tg-ch-nav-btn next" onclick="scrollToChapter('ch-a6')" style="text-align:right">
|
||||
<div class="tg-ch-nav-icon"><i data-lucide="arrow-right"></i></div>
|
||||
<div><div class="tg-ch-nav-label">Следующая глава</div><div class="tg-ch-nav-title">System Health</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ═══ CHAPTER A6 — SYSTEM HEALTH ═══ -->
|
||||
<div class="tg-chapter admin-chapter" id="ch-a6">
|
||||
<div class="tg-chapter-header">
|
||||
<div class="tg-chapter-icon"><i data-lucide="activity"></i></div>
|
||||
<div class="tg-chapter-meta">
|
||||
<div class="tg-chapter-num" style="color:#c0306a">Глава A6</div>
|
||||
<div class="tg-chapter-title">System Health</div>
|
||||
</div>
|
||||
<a href="/admin#health" class="tg-chapter-try" target="_blank"><i data-lucide="external-link"></i> Health</a>
|
||||
</div>
|
||||
<div class="tg-chapter-admin-header"><i data-lucide="shield"></i> Только для администратора</div>
|
||||
|
||||
<div class="tg-section" id="s-a6-1">
|
||||
<div class="tg-section-title">A6.1 Метрики сервера в реальном времени</div>
|
||||
<p>Вкладка <b>«System Health»</b> — состояние сервера Node.js:</p>
|
||||
<div class="tg-tools-grid">
|
||||
<div class="tg-tool-card"><div class="tg-tool-icon"><i data-lucide="cpu"></i></div><div><div class="tg-tool-name">CPU</div><div class="tg-tool-desc">Текущая загрузка процессора</div></div></div>
|
||||
<div class="tg-tool-card"><div class="tg-tool-icon"><i data-lucide="database"></i></div><div><div class="tg-tool-name">RAM</div><div class="tg-tool-desc">Heap used / total</div></div></div>
|
||||
<div class="tg-tool-card"><div class="tg-tool-icon"><i data-lucide="clock"></i></div><div><div class="tg-tool-name">Event Loop Lag</div><div class="tg-tool-desc">Задержка обработки запросов</div></div></div>
|
||||
<div class="tg-tool-card"><div class="tg-tool-icon"><i data-lucide="bar-chart-3"></i></div><div><div class="tg-tool-name">Тренды</div><div class="tg-tool-desc">Canvas-графики нагрузки</div></div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tg-section" id="s-a6-2">
|
||||
<div class="tg-section-title">A6.2 HTTP-статистика и журнал ошибок</div>
|
||||
<div class="tg-steps">
|
||||
<div class="tg-step"><div class="tg-step-num">—</div><div class="tg-step-body"><b>Самые медленные роуты</b> — найдите узкие места по avgMs.</div></div>
|
||||
<div class="tg-step"><div class="tg-step-num">—</div><div class="tg-step-body"><b>Самые частые роуты</b> — понимание нагрузки на API.</div></div>
|
||||
<div class="tg-step"><div class="tg-step-num">—</div><div class="tg-step-body"><b>Статус-коды</b> — соотношение 2xx / 4xx / 5xx.</div></div>
|
||||
<div class="tg-step"><div class="tg-step-num">—</div><div class="tg-step-body"><b>Журнал ошибок</b> — последние ошибки сервера с стектрейсом. Ищите здесь, если что-то сломалось.</div></div>
|
||||
</div>
|
||||
<div class="tg-tip"><div class="tg-box-icon"><i data-lucide="alert-circle"></i></div><div class="tg-box-body"><div class="tg-box-label">Event Loop Lag > 200ms</div>Сигнал перегрузки: возможно блокирующая операция или слишком много одновременных запросов. Проверьте журнал ошибок и самые медленные роуты.</div></div>
|
||||
<div class="tg-success"><div class="tg-box-icon"><i data-lucide="check-circle"></i></div><div class="tg-box-body"><div class="tg-box-label">Готово!</div>Вы изучили полное руководство администратора (A1–A6). Удачи в работе!</div></div>
|
||||
</div>
|
||||
|
||||
<div class="tg-chapter-nav">
|
||||
<div class="tg-ch-nav-btn prev" onclick="scrollToChapter('ch-a5')">
|
||||
<div class="tg-ch-nav-icon"><i data-lucide="arrow-left"></i></div>
|
||||
<div><div class="tg-ch-nav-label">Предыдущая глава</div><div class="tg-ch-nav-title">Аудит и безопасность</div></div>
|
||||
</div>
|
||||
<div class="tg-ch-nav-btn next" onclick="scrollToChapter('ch-1')" style="text-align:right">
|
||||
<div class="tg-ch-nav-icon"><i data-lucide="rotate-ccw"></i></div>
|
||||
<div><div class="tg-ch-nav-label">Вернуться к началу</div><div class="tg-ch-nav-title">Быстрый старт</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div><!-- #tg-admin-content -->
|
||||
|
||||
</main>
|
||||
</div>
|
||||
</div><!-- .sb-content -->
|
||||
@@ -1448,9 +1799,15 @@
|
||||
<script src="/js/search.js"></script>
|
||||
<script src="/js/mobile.js"></script>
|
||||
<script>
|
||||
LS.initPage();
|
||||
const { isAdmin } = LS.initPage();
|
||||
lucide.createIcons();
|
||||
|
||||
/* ── Show admin guide block if admin ── */
|
||||
if (isAdmin) {
|
||||
document.getElementById('tg-admin-content').style.display = '';
|
||||
document.getElementById('tg-nav-admin').style.display = '';
|
||||
}
|
||||
|
||||
/* ── Chapter metadata ── */
|
||||
const CHAPTERS = [
|
||||
{ id:'ch-1', label:'Быстрый старт', icon:'rocket', sections:['s-1-1','s-1-2','s-1-3','s-1-4'], sLabels:['Первый вход','Интерфейс','Первый класс','Чеклист'] },
|
||||
@@ -1466,12 +1823,23 @@
|
||||
{ id:'ch-11', label:'Учебники', icon:'book-open-text', sections:['s-11-1','s-11-2','s-11-3','s-11-4'], sLabels:['Каталог','Чтение и отметки','Назначить как ДЗ','Прогресс класса'] },
|
||||
{ id:'ch-12', label:'Экзамен 9 класс', icon:'clipboard-check', sections:['s-12-1','s-12-2'], sLabels:['Что внутри','Назначить вариант'] },
|
||||
{ id:'ch-13', label:'Мои ученики', icon:'user-plus', sections:['s-13-1','s-13-2','s-13-3','s-13-4'], sLabels:['Когда нужно','Добавить ученика','Назначение','Удаление и заметки'] },
|
||||
{ id:'ch-14', label:'Виртуальная лаборатория', icon:'flask-conical', sections:['s-14-1','s-14-2','s-14-3','s-14-4'], sLabels:['40 симуляций','Связь с учебниками','Стереометрия 3D','Управление (admin)'] },
|
||||
{ id:'ch-14', label:'Виртуальная лаборатория', icon:'flask-conical', sections:['s-14-1','s-14-2','s-14-3'], sLabels:['40 симуляций','Связь с учебниками','Стереометрия 3D'] },
|
||||
{ id:'ch-15', label:'Биохимия', icon:'atom', sections:['s-15-1','s-15-2','s-15-3'], sLabels:['Молекулярный редактор','Библиотека и свойства','Реакции и пути'] },
|
||||
{ id:'ch-16', label:'Геймификация и питомец', icon:'zap', sections:['s-16-1','s-16-2','s-16-3'], sLabels:['XP и достижения','Виртуальный питомец','Начисление XP (admin)'] },
|
||||
{ id:'ch-17', label:'Доступ к контенту', icon:'shield-check', sections:['s-17-1','s-17-2','s-17-3'], sLabels:['Открытие учебников классу','Feature Flags','System Health'] },
|
||||
{ id:'ch-16', label:'Геймификация и питомец', icon:'zap', sections:['s-16-1','s-16-2'], sLabels:['XP и достижения','Виртуальный питомец'] },
|
||||
];
|
||||
|
||||
/* ── Admin chapters (only rendered for admin role) ── */
|
||||
const ADMIN_CHAPTERS = [
|
||||
{ id:'ch-a1', label:'Командный центр', icon:'layout-dashboard', sections:['s-a1-1','s-a1-2','s-a1-3'], sLabels:['Дашборд админа','Очередь триажа','Лента и статистика'] },
|
||||
{ id:'ch-a2', label:'Управление пользователями', icon:'users', sections:['s-a2-1','s-a2-2','s-a2-3'], sLabels:['Список пользователей','Карточка пользователя','Глобальный поиск'] },
|
||||
{ id:'ch-a3', label:'Контент и доступ', icon:'book-lock', sections:['s-a3-1','s-a3-2','s-a3-3'], sLabels:['Allowlist учебников','Симуляции','Feature Flags'] },
|
||||
{ id:'ch-a4', label:'Геймификация (admin)', icon:'zap', sections:['s-a4-1','s-a4-2','s-a4-3'], sLabels:['Статистика','Начисление XP/монет','Сброс прогресса'] },
|
||||
{ id:'ch-a5', label:'Аудит и безопасность', icon:'file-text', sections:['s-a5-1','s-a5-2','s-a5-3'], sLabels:['Аудит-лог','Разрешения RBAC','Модерация аватаров'] },
|
||||
{ id:'ch-a6', label:'System Health', icon:'activity', sections:['s-a6-1','s-a6-2'], sLabels:['Метрики сервера','HTTP-статистика и ошибки'] },
|
||||
];
|
||||
|
||||
const ALL_CHAPTERS = () => isAdmin ? [...CHAPTERS, ...ADMIN_CHAPTERS] : CHAPTERS;
|
||||
|
||||
/* ── Render nav ── */
|
||||
const navContainer = document.getElementById('tg-nav-chapters');
|
||||
CHAPTERS.forEach((ch, ci) => {
|
||||
@@ -1490,6 +1858,30 @@
|
||||
</div>`;
|
||||
navContainer.appendChild(div);
|
||||
});
|
||||
|
||||
|
||||
/* ── Render admin nav ── */
|
||||
if (isAdmin) {
|
||||
const adminNavContainer = document.getElementById('tg-nav-admin-chapters');
|
||||
ADMIN_CHAPTERS.forEach((ch, ci) => {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'tg-nav-chapter admin';
|
||||
div.dataset.ch = ch.id;
|
||||
div.innerHTML = `
|
||||
<button class="tg-nav-ch-btn" onclick="navChapterClick('${ch.id}',this)">
|
||||
<span class="tg-nav-ch-icon"><i data-lucide="${ch.icon}"></i></span>
|
||||
<span class="tg-nav-ch-label">A${ci+1}. ${ch.label}</span>
|
||||
<span class="tg-nav-ch-status"><i data-lucide="check"></i></span>
|
||||
<span class="tg-nav-ch-chevron"><i data-lucide="chevron-right"></i></span>
|
||||
</button>
|
||||
<div class="tg-nav-sections">
|
||||
${ch.sections.map((sid, si) => `<a class="tg-nav-sec-link" data-sec="${sid}" onclick="scrollToSection('${sid}')">${ch.sLabels[si]}</a>`).join('')}
|
||||
</div>`;
|
||||
adminNavContainer.appendChild(div);
|
||||
});
|
||||
lucide.createIcons({ nodes: [adminNavContainer] });
|
||||
}
|
||||
|
||||
lucide.createIcons();
|
||||
|
||||
/* ── Chapter switching ── */
|
||||
@@ -1530,7 +1922,7 @@
|
||||
|
||||
function scrollToChapter(chId) { showChapter(chId); }
|
||||
function scrollToSection(secId) {
|
||||
const ch = CHAPTERS.find(c => c.sections.includes(secId));
|
||||
const ch = ALL_CHAPTERS().find(c => c.sections.includes(secId));
|
||||
if (ch) showChapter(ch.id, secId);
|
||||
}
|
||||
|
||||
@@ -1556,8 +1948,9 @@
|
||||
div.querySelector('.tg-nav-ch-btn').classList.toggle('read', readChapters.includes(div.dataset.ch));
|
||||
});
|
||||
const n = readChapters.length;
|
||||
document.getElementById('tg-prog-text').textContent = `${n} из ${CHAPTERS.length} глав прочитано`;
|
||||
progBar.style.width = Math.round(n / CHAPTERS.length * 100) + '%';
|
||||
const total = ALL_CHAPTERS().length;
|
||||
document.getElementById('tg-prog-text').textContent = `${n} из ${total} глав прочитано`;
|
||||
progBar.style.width = Math.round(n / total * 100) + '%';
|
||||
}
|
||||
updateReadUI();
|
||||
|
||||
@@ -1610,7 +2003,7 @@
|
||||
|
||||
/* ── Init from hash or default ch-1 ── */
|
||||
const initHash = location.hash.replace('#', '');
|
||||
showChapter(CHAPTERS.find(c => c.id === initHash) ? initHash : 'ch-1');
|
||||
showChapter(ALL_CHAPTERS().find(c => c.id === initHash) ? initHash : 'ch-1');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user