You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Data-driven achievement system that automatically awards badges when users reach predefined milestones. Spans backend (evaluation engine + API) and frontend (achievements page + toast notifications).
Architecture
Data-Driven Design
All badge definitions are stored as static data and evaluated by a centralized engine. No hardcoded badge checks scattered across the codebase. New badges can be added by simply appending to the definitions array.
Badge Categories (7)
Category
Focus
Streak
Consecutive days of verified activity
Workout
Total workouts completed
Academic
Total quizzes completed and passed
Plant
Plant growth stage milestones
Staking
G$ staked on commitments
Harvest
G$ claimed from harvests
Points
Total points earned
Rarity Levels (5)
Rarity
Color
Description
Common
Gray
Early milestones, easy to achieve
Rare
Blue
Moderate dedication required
Epic
Purple
Significant commitment
Legendary
Orange
Elite achievement
Mythic
Red/Pink
Maximum dedication
Backend
Files
File
Purpose
backend/badgeDefinitions.js
All 27 badge definitions as static data
backend/achievementService.js
Core evaluation engine
backend/db/badge-db.js
SQLite schema (badge_definitions, user_badges)
backend/server.js
API routes + trigger points
Requirement Types (9)
streak_days — consecutive days of verified activity
workouts_total — lifetime workout count
quizzes_total — lifetime quiz completions
points_total — total points accumulated
plant_stage — current plant growth stage (0-4)
has_stake — has at least one active stake
has_claimed — has claimed G$ at least once
total_staked — total G$ staked across all commitments
total_claimed — total G$ claimed from harvests
API Endpoints
GET /api/achievements/:address
Returns all badges (unlocked + locked) with progress for a wallet.
Returns most recently earned badges (default 5, configurable via ?limit=).
POST /api/admin/migrate-badges
Backfill badges for existing users who have on-chain data.
Automatic Triggers
Badge evaluation runs automatically after:
POST /api/workout/record — badge awards returned in badgeAwards[] + message
POST /api/quiz/submit — badge awards returned in badgeAwards[] + message
Duplicate Prevention
UNIQUE(walletAddress, badgeId) constraint in SQLite. awardBadge() checks before inserting.
Frontend
Files
File
Purpose
frontend/src/config/badges.ts
TypeScript types, rarity/category config
frontend/src/hooks/useAchievements.ts
useAchievements() and useRecentAchievements() hooks
frontend/src/app/components/BadgeCard.tsx
Badge card with icon, rarity badge, progress bar
frontend/src/app/components/BadgeNotification.tsx
Toast notification with slide-in animation
frontend/src/app/achievements/page.tsx
Full achievements page
frontend/src/app/page.tsx
Recent achievements widget (home page)
Pages
/achievements — Stat cards, category filters, unlocked/locked badge grids
Home page widget — Last 5 unlocked badges with rarity tags
Toast Notifications
BadgeNotification component auto-dismisses after 6 seconds. Wired into health/page.tsx and academics/page.tsx — triggers when data.badgeAwards is present in workout/quiz submission response.
Harvest badges use G$ amount, not harvest event count (contract only exposes totalClaimed amount)
Staking/harvesting via direct wallet interaction doesn't trigger badge evaluation — only workouts and quizzes do (they go through the backend verifier). Frontend-side triggers not yet implemented.
Single toast — only 1 badge notification per action even if multiple unlock (all awarded silently server-side)
Plant stage is volatile — badges derived from current points can be "lost" if points drop below threshold