90c8464356
Achievements gain four new columns: group_slug, track, tier, sort_order.
Existing 36 are backfilled into 5 groups (onboarding/volume/mastery/
consistency/exploration) by migration 030; 'social' stays empty until
Phase 3 adds class/leaderboard/live-quiz tracks.
Tracks bundle escalating thresholds into one progression (tests_10/50/
100 → track='tests', tiers 1-3), so the UI can show '★★★' on the top
tier and the user understands the relationship. sort_order is reserved
in blocks of 10 inside groups of 100, leaving room for inserts without
renumbering.
Backend:
• migration 030 adds the columns + index + backfill UPDATEs
• _shared.ACHIEVEMENT_DEFS gains group/track/tier/sort_order per row
• _shared exports new ACHIEVEMENT_GROUPS metadata for the UI
• service.seedAchievements writes the new fields on insert AND
backfills them via UPDATE on existing rows (fresh installs +
pre-migration installs both end up consistent)
• _shared.stmts.getAllAchs SELECT updated, ORDER BY sort_order
• gamification/api.getAchievements forwards the new fields
Frontend:
• profile.html groups achievements by group_slug with a per-section
header (icon + title + 'unlocked / total' chip) and a tier-star
badge (★★ etc.) on tier ≥ 2 items
• Hard-coded ACH_GROUPS mirror of the backend list (small, stable)
• New CSS for .ach-group / .ach-group-head / .ach-tier
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
93 lines
7.0 KiB
SQL
93 lines
7.0 KiB
SQL
-- ═══════════════════════════════════════════════════════════════
|
|
-- 030: Two-level taxonomy for achievements
|
|
--
|
|
-- The existing `category` field stays for backward compatibility, but
|
|
-- the UI now groups achievements by `group_slug` (six top-level
|
|
-- buckets) and orders them by `(group_slug, sort_order)`. Inside a
|
|
-- bucket related achievements with escalating thresholds share a
|
|
-- `track` (e.g. tests_10/50/100 → track='tests') and a `tier`
|
|
-- (1..5). Singletons leave tier NULL.
|
|
--
|
|
-- New groups:
|
|
-- onboarding — first steps in the app
|
|
-- volume — quantity (tests, assignments, XP totals)
|
|
-- mastery — quality: streaks/levels were folded into consistency
|
|
-- and mastery respectively; pure mastery = score/speed
|
|
-- /perfect/course completion
|
|
-- consistency — streaks, daily goals, plan adherence
|
|
-- exploration — feature discovery: lab, biochem, red book
|
|
-- social — class membership, leaderboard (filled in Phase 3)
|
|
--
|
|
-- Sort order convention: groups are spaced by 100s (onboarding=100,
|
|
-- volume=200, ...), achievements inside use offsets so we can insert
|
|
-- new ones later without renumbering.
|
|
-- ═══════════════════════════════════════════════════════════════
|
|
|
|
ALTER TABLE achievements ADD COLUMN group_slug TEXT;
|
|
ALTER TABLE achievements ADD COLUMN track TEXT;
|
|
ALTER TABLE achievements ADD COLUMN tier INTEGER;
|
|
ALTER TABLE achievements ADD COLUMN sort_order INTEGER NOT NULL DEFAULT 0;
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_ach_group_sort ON achievements(group_slug, sort_order);
|
|
|
|
-- ── onboarding (group=100) ─────────────────────────────────────
|
|
UPDATE achievements SET group_slug='onboarding', track='first_test', tier=1, sort_order=110 WHERE slug='first_test';
|
|
UPDATE achievements SET group_slug='onboarding', track='first_perfect', tier=1, sort_order=120 WHERE slug='first_perfect';
|
|
UPDATE achievements SET group_slug='onboarding', track='first_class', tier=1, sort_order=130 WHERE slug='first_class';
|
|
|
|
-- ── volume (group=200): quantity of things done ────────────────
|
|
-- track=tests (10/50/100)
|
|
UPDATE achievements SET group_slug='volume', track='tests', tier=1, sort_order=210 WHERE slug='tests_10';
|
|
UPDATE achievements SET group_slug='volume', track='tests', tier=2, sort_order=220 WHERE slug='tests_50';
|
|
UPDATE achievements SET group_slug='volume', track='tests', tier=3, sort_order=230 WHERE slug='tests_100';
|
|
-- track=assign (first/10)
|
|
UPDATE achievements SET group_slug='volume', track='assign', tier=1, sort_order=240 WHERE slug='assign_first';
|
|
UPDATE achievements SET group_slug='volume', track='assign', tier=2, sort_order=250 WHERE slug='assign_10';
|
|
-- track=xp_total (1000/5000/10000)
|
|
UPDATE achievements SET group_slug='volume', track='xp_total', tier=1, sort_order=260 WHERE slug='xp_1000';
|
|
UPDATE achievements SET group_slug='volume', track='xp_total', tier=2, sort_order=270 WHERE slug='xp_5000';
|
|
UPDATE achievements SET group_slug='volume', track='xp_total', tier=3, sort_order=280 WHERE slug='xp_10000';
|
|
|
|
-- ── mastery (group=300): quality / completion ──────────────────
|
|
-- track=level (3/5/10/20)
|
|
UPDATE achievements SET group_slug='mastery', track='level', tier=1, sort_order=310 WHERE slug='level_3';
|
|
UPDATE achievements SET group_slug='mastery', track='level', tier=2, sort_order=320 WHERE slug='level_5';
|
|
UPDATE achievements SET group_slug='mastery', track='level', tier=3, sort_order=330 WHERE slug='level_10';
|
|
UPDATE achievements SET group_slug='mastery', track='level', tier=4, sort_order=340 WHERE slug='level_20';
|
|
-- track=score (90% streak, perfect, speed)
|
|
UPDATE achievements SET group_slug='mastery', track='score', tier=1, sort_order=350 WHERE slug='score_90';
|
|
UPDATE achievements SET group_slug='mastery', track='speed', tier=1, sort_order=360 WHERE slug='speed_demon';
|
|
-- track=theory_progress (first/10/course)
|
|
UPDATE achievements SET group_slug='mastery', track='theory', tier=1, sort_order=370 WHERE slug='theory_first';
|
|
UPDATE achievements SET group_slug='mastery', track='theory', tier=2, sort_order=380 WHERE slug='theory_10';
|
|
UPDATE achievements SET group_slug='mastery', track='theory', tier=3, sort_order=390 WHERE slug='theory_course';
|
|
|
|
-- ── consistency (group=400): streaks, plan, goals ──────────────
|
|
UPDATE achievements SET group_slug='consistency', track='streak', tier=1, sort_order=410 WHERE slug='streak_3';
|
|
UPDATE achievements SET group_slug='consistency', track='streak', tier=2, sort_order=420 WHERE slug='streak_7';
|
|
UPDATE achievements SET group_slug='consistency', track='streak', tier=3, sort_order=430 WHERE slug='streak_30';
|
|
|
|
-- ── exploration (group=500): feature discovery ─────────────────
|
|
-- track=lab_experiments (first/5/20/50)
|
|
UPDATE achievements SET group_slug='exploration', track='lab', tier=1, sort_order=510 WHERE slug='lab_first';
|
|
UPDATE achievements SET group_slug='exploration', track='lab', tier=2, sort_order=520 WHERE slug='lab_5';
|
|
UPDATE achievements SET group_slug='exploration', track='lab', tier=3, sort_order=530 WHERE slug='lab_20';
|
|
UPDATE achievements SET group_slug='exploration', track='lab', tier=4, sort_order=540 WHERE slug='lab_50';
|
|
-- track=lab_reactions (10/30)
|
|
UPDATE achievements SET group_slug='exploration', track='lab_reactions', tier=1, sort_order=550 WHERE slug='lab_reactions_10';
|
|
UPDATE achievements SET group_slug='exploration', track='lab_reactions', tier=2, sort_order=560 WHERE slug='lab_reactions_30';
|
|
-- track=red_book (first/10/25/50/all)
|
|
UPDATE achievements SET group_slug='exploration', track='red_book', tier=1, sort_order=570 WHERE slug='rb_first';
|
|
UPDATE achievements SET group_slug='exploration', track='red_book', tier=2, sort_order=580 WHERE slug='rb_10';
|
|
UPDATE achievements SET group_slug='exploration', track='red_book', tier=3, sort_order=590 WHERE slug='rb_25';
|
|
UPDATE achievements SET group_slug='exploration', track='red_book', tier=4, sort_order=600 WHERE slug='rb_50';
|
|
UPDATE achievements SET group_slug='exploration', track='red_book', tier=5, sort_order=610 WHERE slug='rb_all_cr';
|
|
-- track=red_book_quests (first/5)
|
|
UPDATE achievements SET group_slug='exploration', track='red_book_quest',tier=1, sort_order=620 WHERE slug='rb_quest_first';
|
|
UPDATE achievements SET group_slug='exploration', track='red_book_quest',tier=2, sort_order=630 WHERE slug='rb_quest_5';
|
|
-- singletons
|
|
UPDATE achievements SET group_slug='exploration', track='red_book_obs', tier=1, sort_order=640 WHERE slug='rb_sighting';
|
|
|
|
-- ── social (group=600): empty for now, filled in Phase 3 ───────
|
|
-- (no UPDATEs here — placeholder for class/leaderboard/live-quiz tracks)
|