Commit Graph

33 Commits

Author SHA1 Message Date
Maxim Dolgolyov d5fbd0168e feat(permissions): +10 прав ролей с энфорсом (Доступ · роли)
Реестр (registry.js) пополнен правами, которыми раньше нельзя было управлять:
• Учитель: classroom.host (онлайн-уроки), livequiz.host (живые викторины),
  simbuilder.use (конструктор симуляций), flashcards.manage (общие колоды).
• Ученик: homework.submit (сдача ДЗ), materials.save («Мои материалы»),
  assistant.use (ИИ-ассистент), games.play (учебные игры),
  flashcards.access / exam.access (доступ к разделам).
Все default=1 → текущее поведение сохранено; админ может выключить по роли/классу/юзеру.

Энфорс на роутах: учительские — requirePermission (роуты уже teacher-only);
ученические на ОБЩИХ роутах (assistant/materials/games/flashcards/exam-prep) —
новый requirePermissionForStudents(key) (учитель/админ проходят всегда, проверка
только ученику — иначе isEnabled=false сломал бы учителя). PERM_DEFAULTS строится
из реестра → фолбэк до сидирования = enabled, никто не блокируется. Группы UI —
существующие (новых ярлыков нет). seedDefaults авто-сидит новые ключи на чтении.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 17:31:00 +03:00
Maxim Dolgolyov dd69c026ec content(ctmath): вариант 121 — ЦТ-2011 (А1–А18 + В1–В12, 30 заданий)
Перенабор Вариант 1 из ЦТ 2011 В1-В10.pdf (тест полный, не только В), все 30
ответов сверены с официальной таблицей (полное совпадение). Уточнения по таблице:
A6 степень 3x+4 (→15·2^3x), A9=(3^-2)^-5→1/9, A7 корень -3, A8=10, A10=10π.
Фигурные A1/A2/B6 реконструированы (B6: y=x²-6x+9 ∩ y=1,25 → 4x₁x₂=31). Все В
числовые. Без авторских ссылок. Дедуп-гейт 0, KaTeX 30/30, DRY-RUN 30/30. Метка 121='ЦТ-2011'.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 16:53:06 +03:00
Maxim Dolgolyov 84625cd72a content(ctmath): вариант 120 — ЦТ-2012 (А1–А18 + В1–В12, 30 заданий)
Перенабор Вариант 1 из ЦТ 2012.pdf, все 30 ответов сверены с официальной
таблицей (полное совпадение). Фигурные A1/A13/B6 реконструированы с явными
данными (A1 — углы 70/40→равнобедренный; B6 — середины сторон→S=4). A15
уточнена по таблице: √(5⁵·20)=250, знаменатель ⁴√10 → 25·⁴√10. Все В числовые.
Без авторских ссылок. Дедуп-гейт 0, KaTeX 30/30, DRY-RUN 30/30. Метка 120='ЦТ-2012'.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 16:42:27 +03:00
Maxim Dolgolyov 0fb16ef85e content(ctmath): вариант 119 — ЦТ-2013 (А1–А18 + В1–В12, 30 заданий)
Перенабор Вариант 1 из ЦТ2013.pdf, все 30 ответов сверены с официальной
таблицей (полное совпадение). Фигурные A2/A3/A6/A16 реконструированы с явными
описаниями (A2 — образующая=AD, A6 — порядок лучей→40°, A16 — сечение 12×6=72).
Все В-задания числовые (long нет). Без авторских ссылок. Дедуп-гейт 0,
KaTeX 30/30, DRY-RUN 30/30. VARIANT_LABEL: 119='ЦТ-2013'.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 16:35:28 +03:00
Maxim Dolgolyov b9a82c326e content(ctmath): вариант 118 — ЦТ-2017 (А1–А18 + В1–В12, 30 заданий)
Перенабор Вариант 1 из CT-2017.pdf, все 30 ответов сверены с официальной
таблицей (полное совпадение). Фигурные A1/A3/A9/A11/A14 реконструированы с
явными числами (A9 — биссектриса, AM/MC=AB/BC→13,8). Без авторских ссылок.
Прогнан через дедуп-гейт (0 совпадений с пулом) + KaTeX-структуру + DRY-RUN 30/30.
VARIANT_LABEL: 118='ЦТ-2017'.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 16:28:06 +03:00
Maxim Dolgolyov 59ae4c1dea fix(exam-prep): практика/тренажёр берут только выверенные варианты (дедуп)
Тренажёр-по-темам и практика брали FROM exam_tasks без фильтра по варианту — в пул
попадали год-пачки (variant=год≥2011) и variant=0, которые ДУБЛИРУЮТ выверенные
варианты-пробники (51 дубль чистый↔пачка, 20 через variant=0). Ученик мог получить
одну задачу дважды.

Добавлен фильтр variant BETWEEN MV_LO..MV_HI (тот же [101;1999], что у пикера) во все
7 запросов выборки/счёта задач: practiceRandom, practiceUnsolved, topicTasksUnsolved,
topicTasksAny, listTopicsWithCounts (счётчик подтем), weakBatchTasks, pickRandomByDifficulty
(×2). Хелперы MV_LO/MV_HI (для math9 без диапазона — всё, кроме variant=0).

Результат: практика ctmath = только варианты 101–117 (496 задач, 0 дублей между собой),
год-пачки (714 задач) остаются в БД для возможного будущего, но не показываются.
Обратимо, без удаления данных. Рантайм-проверка: 5 эндпоинтов практики/тем → 200.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 13:29:02 +03:00
Maxim Dolgolyov de41b77ae3 feat(ctmath): вариант 117 — ЦТ-2021 (32 задания, А1-А18 + В1-В14)
Пробник ЦТ по математике 2021, Вариант 1. Формат: 32 задания (А1–А18 + В1–В14), А12/А16 —
с несколькими верными (тип open, ответ цифрами), В2/В3 множ.выбор, В1 на соответствие.
Источник: чистый PDF ЦТ 2021.pdf. ВСЕ 32 ответа решены и сверены с официальной таблицей
(стр.45, столбец Вариант 1) — полное совпадение, включая B9=324, B11=960, B13=460 (пары чисел),
B14=1375 (описанный четырёхугольник, r=60/7). Фигурные A7/A17/B1/B4 реконструированы; B5: в скане
∛(-7) → на деле ∛(-343)=-7 (иначе нецелый), ответ -98. Без авторских ссылок.
VARIANT_LABEL 117 -> 'ЦТ-2021'. DRY-RUN 32/32, self-check и структурный KaTeX — зелёные.
Запись в БД — пользователь: node backend/scripts/seed_ctmath_ct2021_v1.js --apply

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 12:22:42 +03:00
Maxim Dolgolyov 59c691dcfc feat(ctmath): вариант 116 — ЦТ-2020 (32 задания, формат А1-А20)
Пробник ЦТ по математике 2020, Вариант 1. ⚠️ Новый формат: 32 задания (А1–А20 + В1–В12),
В1 на соответствие, В2 множ.выбор. Машинерия параметризована N_TASKS=32. Источник: чистый
PDF ЦТ 2020.pdf. ВСЕ 32 ответа решены и сверены с официальной таблицей (стр.44, столбец
Вариант 1) — полное совпадение, включая A20=37√13/3, B5=-335, B8=-320, B9=160, B10=577,
B11=-16, B12=336 (сфера через 4 точки куба). Фигурные A9/A11 реконструированы; без авторских
ссылок (политика «все учебники наши»). VARIANT_LABEL 116 -> 'ЦТ-2020'.
DRY-RUN 32/32, self-check и структурный KaTeX — зелёные.
Запись в БД — пользователь: node backend/scripts/seed_ctmath_ct2020_v1.js --apply

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 12:09:17 +03:00
Maxim Dolgolyov 5881787492 feat(ctmath): вариант 115 — ЦТ-2019 (30 заданий)
Пробник ЦТ по математике 2019, Вариант 1 (А1–А18 + В1–В12; В1 на соответствие, В2 множ.выбор)
для трека exam-prep ctmath. Источник: чистый PDF ЦТ 2019.pdf. Все 30 решены и сверены: столбец
Вариант 1 в таблице (стр.45) затемнён, но читаемые ячейки совпали; методы B5/B6/B7/B11/B12
перекрёстно подтверждены на Варианте 10 (его задания на стр.43-44, ответы читаемы → 81/56/-1071/
624/540 ровно по таблице). Тяжёлые: B7=-264 (период+нечётность), B11=288, B12=110 (т.Гульдина).
Фигурные A1/A7/A9 разрешены по столбцу 1 (D/2√34/2,4); B1/B2 — данные текстом.
VARIANT_LABEL 115 -> 'ЦТ-2019'. DRY-RUN 30/30, self-check и структурный KaTeX — зелёные.
Запись в БД — пользователь: node backend/scripts/seed_ctmath_ct2019_v1.js --apply

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 11:17:46 +03:00
Maxim Dolgolyov 7990b33fd0 feat(ctmath): вариант 114 — ЦТ-2018 (30 заданий)
Пробник ЦТ по математике 2018, Вариант 1 (А1–А18 + В1–В12, В1 на соответствие) для трека
exam-prep ctmath. Источник: чистый PDF ЦТ 2018.pdf. ВСЕ 30 ответов решены и сверены с
официальной таблицей (стр.32, столбец Вариант 1) — полное совпадение, включая B8=-18,
B9=-130 (двугранный угол), B11=32, B12=45 (координатный метод, PT=5/2).
Фигурные/несогласованные (А3 точки, А9 графики→пути, А11 квадраты, В1/В2 функция по узлам,
В8 экв. показательное, В10 множитель (6-x)² по ответу) реконструированы/адаптированы.
VARIANT_LABEL 114 -> 'ЦТ-2018'. DRY-RUN 30/30, self-check и структурный KaTeX — зелёные.
Запись в БД — пользователь: node backend/scripts/seed_ctmath_ct2018_v1.js --apply

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 11:04:12 +03:00
Maxim Dolgolyov c86d5b9ad4 feat(ctmath): вариант 113 — ЦТ-2016 (30 заданий)
Пробник ЦТ по математике 2016, Вариант 1 (А1–А18 + В1–В12) для трека exam-prep ctmath.
Источник: чистый PDF ЦТ 2016.pdf. ВСЕ 30 ответов решены и сверены с официальной таблицей
(стр.35, столбец Вариант 1) — полное совпадение, включая B5=-22, B9=712, B11=56, B12=724.
Фигурные задания (А2 угол через MN||BC, А3 числа на прямой, А6 таблица, А7 площадь по
координатам, А8 область значений, А11 круговая диаграмма) реконструированы/адаптированы
в самодостаточные авто-проверяемые формы. VARIANT_LABEL 113 -> 'ЦТ-2016'.
DRY-RUN 30/30, self-check и структурный KaTeX — зелёные.
Запись в БД — пользователь: node backend/scripts/seed_ctmath_ct2016_v1.js --apply

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 10:37:13 +03:00
Maxim Dolgolyov 7e8082bda6 feat(ctmath): вариант 112 — ЦТ-2015 (30 заданий)
Пробник ЦТ по математике 2015, Вариант 1 (А1–А18 + В1–В12) для трека exam-prep ctmath.
Источник: чистый PDF ЦТ 2015.pdf (10 изоморфных вариантов, таблица ответов стр.35).
Ответы решены и сверены: читаемые ячейки столбца В1 (B2=-15,B3=7,B7=147,B8=-6 совпали)
+ изоморфные варианты 2–10. Фигурные задания (А4 центр.симметрия, А6 множество решений,
А11 таблица-данные, А12 парабола, А15 координаты, В11 лог-выражение) адаптированы в
самодостаточные авто-проверяемые формы с сохранением ответа (как в ЦТ-2014/вар.110).
VARIANT_LABEL 112 -> 'ЦТ-2015'. DRY-RUN 30/30, self-check и структурный KaTeX — зелёные.
Запись в БД — пользователь: node backend/scripts/seed_ctmath_ct2015_v1.js --apply

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 10:24:59 +03:00
Maxim Dolgolyov 5193fd8252 feat(ctmath): пробник ЦЭ-2024 Вариант 1 (вариант 111)
Актуальный формат экзамена: А1–А10 (8 mc + 2 open) + В1–В20 (1 long + 19 open).
Перенабор по PDF сборника РИКЗ; решений в источнике нет — решено вручную,
ВСЕ 30 ответов сверены с официальным ключом (стр.35, столбец Вариант 1).
Адаптации: А1 (точки на прямой) → равные промежутки в тексте; А6 (промежуток
на рисунке) → описан словами. Метка 111 (ЦЭ-2024) в VARIANT_LABEL.
Идемпотентный seed, --apply — пользователь.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 12:50:47 +03:00
Maxim Dolgolyov f4d20ff10f feat(ctmath): пробник ЦТ-2014 Вариант 1 (вариант 110)
Первый из ЦТ-годовых. Формат ЦТ: А1–А18 (18 mc) + В1–В12 (12 open) = 30.
Перенабор по PDF; решений в источнике НЕТ — решено вручную, ответы
сверены с официальным ключом (стр.34 сборника). Адаптации картинок:
А2 (симметричные фигуры, неразборчивы) → MC о симметрии точки; А6
(параллелограмм на сетке) → координаты вершин; А10/А15 — текстом.
Метка 110 (ЦТ-2014) в VARIANT_LABEL. Идемпотентный seed, --apply — пользователь.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 12:32:53 +03:00
Maxim Dolgolyov d2d379c5f5 feat(ctmath): пробник РТ-2022/23 этап I (вариант 107)
30 заданий А1–А10 + В1–В20, перенабор по PDF РИКЗ.
8 mc + 20 open + 2 long. Геометрия — текстом. Адаптации заданий
с картинкой: А5 (выбор графика) → MC о параболе y=x²−4; В1
(промежуток↔рисунок) → сопоставление со словесными описаниями
(ответ А3Б6В2); В3 (диаграмма) → данные таблицей в figure_html
(ответ А1Б2В6). Метки 107/108/109 (РТ-2022/23) в VARIANT_LABEL.
Идемпотентный seed, --apply — пользователь.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 10:52:54 +03:00
Maxim Dolgolyov 17c1c92490 feat(ctmath): эталонный вариант-пробник РТ-2023/24 Этап I (variant 104)
30 заданий (А1–А10 + В1–В20), перенабрано вручную в KaTeX по PDF РИКЗ
(РТ-1 23/24 В1). Геометрия закодирована текстом — чертежи не нужны.
Идемпотентный upsert, DRY-RUN по умолчанию, запись с --apply.
Верификация: node --check, валидация 30/30, KaTeX-рендер 413/413 сегментов.
+ метки вариантов 104–106 (РТ-2023/24 этап I/II/III) в routes/exam-prep.js.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 09:47:44 +03:00
Maxim Dolgolyov 9a13a19e63 feat(ctmath): человекочитаемые подписи вариантов-пробников
Вместо «Вариант 101/102/103» (технические номера) показываем источник:
«РТ-2024/25 · этап I/II/III». examVariantLabel() в exam-prep.js — единый
источник подписи: listVariants (пикер/dropdown) + variant_label в ответе
mock/:id (строка прохождения и результата). Номера в БД остаются 101+
(нужны для фильтра-диапазона [101;1999] и провенанса). math9 — fallback
«Вариант N» (не затронут). Новые варианты (104+) — дописывать в VARIANT_LABEL.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 08:31:45 +03:00
Maxim Dolgolyov 68817cc612 fix(ctmath): чистка банка — год-пачки убраны из пикера пробников
- exam-prep.js: MOCK_VARIANT_RANGE — для ctmath показываем как пробники
  только чистые 30-задачные варианты [101;1999]; год-пачки (variant=год
  2011-2024 и 0, до 114 задач) остаются пулом для тренажёра по темам,
  но скрыты из пикера/mock-start/просмотра вариантов. math9 (1..80) не затронут
  (диапазон только для ctmath).
- mock.js: пикер «По варианту» — выпадающий список реальных вариантов
  (через listVariants) вместо number-input 1..N; раньше для ctmath он
  предлагал 1..18 и не доходил до 101 → пробник по варианту не запускался.
- cleanup_ctmath_bank.js: идемпотентный скрипт — ретайр битого id=1419
  (mc с противоречивым ответом → long), variants_count → 3 (чистых вариантов).
- seed_*: variants_count считается по диапазону [101;1999] (консистентно с роутом).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 22:22:32 +03:00
Maxim Dolgolyov 6fed18f819 feat(admin): тумблер вкл/выкл для экзамен-модулей (exam-prep)
Не было UI для управления exam_tracks.enabled (только флаг в БД, ставился
миграцией). Добавлена админ-секция «Экзамен-модули»:
- backend exam-prep.js: GET /admin/tracks (все треки, вкл. выключенные, + число
  заданий) и PATCH /admin/track (exam_key, enabled), обе requireRole('admin').
  Пути без :examKey, чтобы не задеть гейт content_access.
- frontend: секция sections/exams.js (список треков + переключатель enabled),
  вкладка в admin.html (admin-only через ADMIN_ONLY_TABS, locked для не-админов),
  регистрация в admin.js (ROUTE_TO_SECTION).

Выключенный трек скрыт у учеников и пропадает из каталога прав доступа (тот
берёт exam_tracks WHERE enabled=1). Доступ ученикам по-прежнему в «Доступ · контент».
Требует перезапуска бэкенда + Ctrl+F5.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 12:32:01 +03:00
Maxim Dolgolyov 8091b48e1c fix(ct-math): практика возвращала меньше count + перенос заголовков в навигации урока
1) exam-prep practice (strategy=random) возвращал около 0.6 от count: функция
   distributeByDifficulty раскладывает count по 5 уровням сложности, а у трека
   ctmath задания только уровней 1-3 (уровни 4-5 пустые) -> часть выборки терялась
   (20 -> 12, 15 -> 10, 10 -> 6). В pickRandomByDifficulty добавлен добор до count
   из доступных уровней. Трек math9 не затронут (там добор не требуется).
2) lesson.html: .lesson-nav-btn-title был inline-span, поэтому max-width и ellipsis
   игнорировались и длинные заголовки вылезали за кнопку. Добавлен display:block.

Бэкенд-правка требует перезапуска сервера; фронт-правка видна после Ctrl+F5.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 12:09:50 +03:00
Maxim Dolgolyov a096f3bcd9 feat(exam): Phase 4 — контроллер использует task-level textbook_slug/paragraph
- SELECT добавляет t.textbook_slug, t.textbook_paragraph во все 7 запросов
  (getVariantTasks, practiceRandom, practiceUnsolved, pickRandomByDifficulty,
   topicTasksUnsolved/Any, weakBatchTasks, getTasksByIds)
- shapeTask() предпочитает task-level ссылку, fallback на refMap subtopic
- /variants/:n/tasks аналогично использует per-task поля
- getParaTitle() строит map chapter:para -> title из g9_textbook_sections.json

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 16:18:41 +03:00
Maxim Dolgolyov 471171b77c feat(access): доступ к учебникам и экзаменам по классам/ученикам из админ-панели
Модель allowlist (закрыто по умолчанию), правило ученика важнее класса.
Управляют админ (все) и учителя (свои классы/ученики).

- миграция 040: таблица content_access + непрерывный переход
  (всем существующим классам открыт текущий контент)
- сервис contentAccess: резолвинг доступа, главы наследуют хаб
- API /api/access (catalog/targets/rules) для admin+teacher
- гейты: каталог учебников, router.param slug/examKey, фильтр tracks
- клиентские редиректы на /403 (textbook-tracker, exam-prep boot)
- раздел админки «Доступ к учебникам»: классы + ученики (tri-state)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 12:33:05 +03:00
Maxim Dolgolyov 268ea31bb8 feat(gamification): Phase 4 — standalone coin events + coin_log
Coins were always 1:10 of XP. Now they have their own event log + a
helper that dedups by reason within a configurable window.

Backend:
  • migration 032 creates coin_log (user_id, amount, reason, created_at)
    with indices for the 'fired today?' check
  • awardCoins now records into coin_log on every call (reason defaults
    to 'xp_bonus' for the legacy XP-proportional path)
  • awardCoinsOnce(userId, amount, reason, window) — fires the bonus
    only if no row matches in the window:
      'day'     → DATE(created_at) = today
      'week'    → ISO week match
      'forever' → never twice

Wired events (Phase 4 subset of the plan):
  • Daily login — 10 coins, once/day. Hooked in updateStreak so the
    bonus rides on the existing 'daily_activity' XP trigger.
  • Daily goal completion — 15/25/40 coins (easy/medium/hard), once/day.
    Sits next to the existing tier XP bonus in updateDailyGoal.
  • Variant clear — 30 coins, once per (user, variant) forever. Fires
    from the exam-prep attempts endpoint when the user's final correct
    answer fills out a math9 variant.

Deferred (need invasive trigger hooks): weekly goal, paragraph close,
boss defeated, referral.

Verified end-to-end: awardCoinsOnce returns true→false on repeated
calls, coin_log records the first, coins balance moves once.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 20:30:14 +03:00
Maxim Dolgolyov c3d6af8757 fix(exam-prep): normalize LaTeX in dashboard preview text
Recent-attempts widget on /exam-prep/:examKey was showing raw LaTeX
like '\dfrac{7}{9}' because stripPreview only removed HTML tags.
Now it also converts common LaTeX to readable unicode (fractions →
a/b, \sqrt → √, \cdot → ·, comparisons → ≤≥≠, Greek letters, etc.)
before truncating.

KaTeX rendering would be overkill for a 100-char preview row; this
just makes the existing text legible.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 16:45:59 +03:00
Maxim Dolgolyov 3cc52e21b0 feat(exam9): link tasks to textbook + difficulty-ordered random + topic exclusion
Practice (random) now picks tasks by ascending difficulty so the first
slot is always level 1 and the session ramps up. Adds ?exclude= to drop
specific subtopics from the random pool, with a per-section checkbox
modal in the UI.

Each task carries a topic_ref (textbook chapter + paragraph) shown as
a 'Учить тему · §N' button next to the solution, deep-linking to the
right section of /textbook/<slug>. Mapping seeded for all 15 math9
subtopics in migration 028.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 14:55:47 +03:00
Maxim Dolgolyov 54b8d06c61 feat(exam-prep F8): слабые темы на дашборде + strategy=weak в тренажёре 2026-05-29 11:38:55 +03:00
Maxim Dolgolyov fe7d44aa83 feat(exam-prep F7): карта тем + тематический тренажёр (API /topics + /topics/:slug/tasks + UI) 2026-05-29 11:35:28 +03:00
Maxim Dolgolyov c590c32b41 feat(exam-prep F10): план по дате экзамена — виджет на дашборде + модалка + GET/PUT/DELETE /plan 2026-05-29 11:17:28 +03:00
Maxim Dolgolyov 294b3622b5 feat(exam-prep F4): живой дашборд — streak + последние попытки + точность 7д + хитмап активности + пробники 2026-05-29 11:12:23 +03:00
Maxim Dolgolyov cfcb233b6c feat(exam-prep F9): пробный экзамен — setup/active/result + таймер + балл по сетке + серверный чекер 2026-05-29 11:06:57 +03:00
Maxim Dolgolyov 4652f9a73d feat(exam-prep F5): тренажёр случайных задач + /practice/next API (random|unsolved) 2026-05-29 10:57:22 +03:00
Maxim Dolgolyov 7c33d4ce11 feat(exam-prep F2): порт браузера вариантов + API /variants + POST /attempts + редирект /exam9 2026-05-29 10:43:10 +03:00
Maxim Dolgolyov 1b79965fce feat(geom9 ch3 wave2 + final): §12 «Герон» + Финал Главы 3 2026-05-29 10:13:29 +03:00