feat(biochem): Фаза 4 (срез) — персистентность прогресса путей + награда

Learn-режим метаболических путей теперь сохраняет прохождение на пользователя
(раньше прогресс терялся).

- migration 044_bio_user_pathway: таблица bio_user_pathway(user_id, pathway,
  step, completed) с upsert.
- biochemController: getPathwayProgress / savePathwayProgress; XP (+80)
  начисляется один раз при первом завершении пути (completed «липкий» через
  MAX), затем checkAchievements. Роуты GET/POST /biochem/pathways/progress.
- js/api.js: biochemGetPathwayProgress / biochemSavePathwayProgress.
- biochem-pathways.html: загрузка прогресса в init (галочка-SVG на пройденных
  путях), сохранение + тост «+XP» при завершении пути.

Полный перенос данных путей в БД (4.1-4.3) отложен — хардкод путей работает,
ценность миграции архитектурная; здесь доставлена пользовательская часть.

Проверено: upsert, XP-once, completed-sticky на реальной БД.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Maxim Dolgolyov
2026-05-30 17:25:18 +03:00
parent 045eb2646e
commit e2ff28a482
5 changed files with 94 additions and 4 deletions
+4
View File
@@ -943,6 +943,8 @@ async function biochemSolveChallenge(id,payload) { return req('POST',`/bioche
async function biochemGetSaved() { return req('GET', '/biochem/saved'); }
async function biochemSave(atoms,bonds,name){ return req('POST','/biochem/saved',{atoms,bonds,name}); }
async function biochemDeleteSaved(id) { return req('DELETE',`/biochem/saved/${id}`); }
async function biochemGetPathwayProgress() { return req('GET', '/biochem/pathways/progress'); }
async function biochemSavePathwayProgress(pathway,step,completed){ return req('POST','/biochem/pathways/progress',{pathway,step,completed}); }
/* ── LS.prefs — server-synced user preferences ──────────────────────────
Keys use dot-notation: 'wb.color', 'dashboard.hidden', etc.
@@ -1056,6 +1058,7 @@ window.LS = {
patch: (path, body) => apiFetch(path, { method: 'PATCH', body: JSON.stringify(body) }),
applyCosmetics: applyCosmetics,
refreshNavAvatar,
renderNavAvatar,
isGamificationEnabled,
loadFeatures,
clearFeaturesCache,
@@ -1064,6 +1067,7 @@ window.LS = {
biochemGetElements, biochemGetMolecules, biochemGetMolecule, biochemValidate,
biochemGetReactions, biochemGetChallenges, biochemSolveChallenge,
biochemGetSaved, biochemSave, biochemDeleteSaved,
biochemGetPathwayProgress, biochemSavePathwayProgress,
prefs: lsPrefs,
};