Files
Learn_System/backend/src/db/migrations/034_achievement_required_feature.sql
T
Maxim Dolgolyov 41ca41d69c feat(gamification): hide locked achievements of disabled modules
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>
2026-05-29 20:40:16 +03:00

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.)