41ca41d69c
When a teacher / admin turns off a module (per-class, per-role, or
globally), the matching achievements no longer clutter the user's
'Достижения' tab — but only the ones the user hasn't earned yet.
Already-unlocked achievements stay visible forever. We never take a
reward away after the fact.
Backend:
• migration 034 adds achievements.required_feature + backfills 42
rows (9 exam9, 8 red_book, 6 lab, 5 classroom, 4 textbooks, 3 each
of biochem/flashcards, 2 live_quiz, 2 pet). 32 core rows stay
NULL = always visible.
• middleware/features.js gains computeFeaturesForUser(userId, role)
+ isFeatureEnabledForUser — extracted from server.js#/api/features
so multiple consumers (gam achievements, future shop filter, etc.)
apply the same global+class+free_student merge.
• service.seedAchievements derives required_feature from track/group
when ACHIEVEMENT_DEFS doesn't spell one out, and UPDATE-syncs it on
every boot — keeps catalogue consistent across upgrades.
• _shared.getAllAchs SELECT now returns required_feature.
• gamification/api.getAchievements filters: drop locked rows whose
required_feature is === false for this user. Missing flag = ON
(opt-in disable model).
Verified: with exam9 + pet disabled, 12 locked achievements vanish from
the response while unlocked ones in those tracks remain.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
65 lines
2.7 KiB
SQL
65 lines
2.7 KiB
SQL
-- ═══════════════════════════════════════════════════════════════
|
|
-- 034: Per-achievement feature dependency
|
|
--
|
|
-- When admin (or a class teacher) turns a module off, the matching
|
|
-- achievements should disappear from the user's "Достижения" tab —
|
|
-- but ONLY when the user hasn't unlocked them yet. Already-earned
|
|
-- achievements stay visible (never take a reward away).
|
|
--
|
|
-- This migration adds `required_feature` to achievements. NULL = no
|
|
-- gating (always visible). The value matches the slug used by
|
|
-- /api/features (e.g. 'exam9', 'red_book', 'pet').
|
|
--
|
|
-- The actual filter lives in gamification/api.getAchievements: it
|
|
-- reads the user's merged features (global + class + free_student)
|
|
-- and drops every locked row whose required_feature is disabled.
|
|
-- ═══════════════════════════════════════════════════════════════
|
|
|
|
ALTER TABLE achievements ADD COLUMN required_feature TEXT;
|
|
|
|
-- ── Backfill ──────────────────────────────────────────────────
|
|
-- Group by module so future inserts inherit the pattern through
|
|
-- seedAchievements (which mirrors these defaults).
|
|
|
|
-- exam-prep (math9 + future exam tracks)
|
|
UPDATE achievements SET required_feature = 'exam9'
|
|
WHERE group_slug = 'exam';
|
|
|
|
-- red book (collection of species)
|
|
UPDATE achievements SET required_feature = 'red_book'
|
|
WHERE track LIKE 'red_book%';
|
|
|
|
-- biochem
|
|
UPDATE achievements SET required_feature = 'biochem'
|
|
WHERE track = 'biochem';
|
|
|
|
-- lab (no feature_lab_enabled flag exists today — set anyway so the
|
|
-- linkage is correct if/when one is introduced)
|
|
UPDATE achievements SET required_feature = 'lab'
|
|
WHERE track IN ('lab', 'lab_reactions');
|
|
|
|
-- classroom (joining lessons + teacher class size)
|
|
UPDATE achievements SET required_feature = 'classroom'
|
|
WHERE track IN ('classroom', 'teacher');
|
|
|
|
-- live quiz
|
|
UPDATE achievements SET required_feature = 'live_quiz'
|
|
WHERE track = 'live_quiz';
|
|
|
|
-- flashcards
|
|
UPDATE achievements SET required_feature = 'flashcards'
|
|
WHERE track = 'flashcards';
|
|
|
|
-- pet
|
|
UPDATE achievements SET required_feature = 'pet'
|
|
WHERE track = 'pet';
|
|
|
|
-- textbooks (paragraph reads, chapter completion)
|
|
UPDATE achievements SET required_feature = 'textbooks'
|
|
WHERE track = 'tb_progress';
|
|
|
|
-- Minigames (hangman/crossword) — either of two features could grant
|
|
-- progress, so 'hangman' alone is too narrow. Leave NULL to keep them
|
|
-- visible even if one game is disabled; the other can still earn them.
|
|
-- (Explicit: no update needed.)
|