1196 lines
30 KiB
JSON
1196 lines
30 KiB
JSON
{
|
|
"workflow_version": "v001",
|
|
"feature": "a complete to earn app",
|
|
"created_at": "2025-12-18",
|
|
"status": "draft",
|
|
"revision": 1,
|
|
"data_models": [
|
|
{
|
|
"id": "model_user",
|
|
"name": "User",
|
|
"description": "User account and authentication",
|
|
"file_path": "app/lib/db/schema/user.ts",
|
|
"status": "PENDING",
|
|
"fields": [
|
|
{
|
|
"name": "id",
|
|
"type": "string",
|
|
"required": true,
|
|
"description": "Unique user identifier (UUID)",
|
|
"constraints": [
|
|
"primary_key"
|
|
]
|
|
},
|
|
{
|
|
"name": "email",
|
|
"type": "string",
|
|
"required": true,
|
|
"description": "User email address (unique)"
|
|
},
|
|
{
|
|
"name": "password_hash",
|
|
"type": "string",
|
|
"required": true,
|
|
"description": "Hashed password using bcrypt"
|
|
},
|
|
{
|
|
"name": "name",
|
|
"type": "string",
|
|
"required": true,
|
|
"description": "User display name"
|
|
},
|
|
{
|
|
"name": "created_at",
|
|
"type": "timestamp",
|
|
"required": true,
|
|
"description": "Account creation timestamp"
|
|
},
|
|
{
|
|
"name": "updated_at",
|
|
"type": "timestamp",
|
|
"required": true,
|
|
"description": "Last update timestamp"
|
|
}
|
|
],
|
|
"indexes": [
|
|
{
|
|
"fields": [
|
|
"email"
|
|
],
|
|
"unique": true
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"id": "model_points",
|
|
"name": "Points",
|
|
"description": "Points transaction history",
|
|
"file_path": "app/lib/db/schema/points.ts",
|
|
"status": "PENDING",
|
|
"fields": [
|
|
{
|
|
"name": "id",
|
|
"type": "string",
|
|
"required": true,
|
|
"description": "Unique transaction identifier (UUID)",
|
|
"constraints": [
|
|
"primary_key"
|
|
]
|
|
},
|
|
{
|
|
"name": "user_id",
|
|
"type": "string",
|
|
"required": true,
|
|
"description": "Foreign key to User"
|
|
},
|
|
{
|
|
"name": "amount",
|
|
"type": "integer",
|
|
"required": true,
|
|
"description": "Points amount (positive for earned, negative for spent)"
|
|
},
|
|
{
|
|
"name": "type",
|
|
"type": "enum",
|
|
"required": true,
|
|
"description": "Transaction type",
|
|
"enum_values": [
|
|
"earned",
|
|
"spent"
|
|
]
|
|
},
|
|
{
|
|
"name": "source",
|
|
"type": "string",
|
|
"required": true,
|
|
"description": "Source of points (task_id, purchase_id, etc.)"
|
|
},
|
|
{
|
|
"name": "created_at",
|
|
"type": "timestamp",
|
|
"required": true,
|
|
"description": "Transaction timestamp"
|
|
}
|
|
],
|
|
"indexes": [
|
|
{
|
|
"fields": [
|
|
"user_id",
|
|
"created_at"
|
|
],
|
|
"unique": false
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"id": "model_task",
|
|
"name": "Task",
|
|
"description": "Available tasks for earning points",
|
|
"file_path": "app/lib/db/schema/task.ts",
|
|
"status": "PENDING",
|
|
"fields": [
|
|
{
|
|
"name": "id",
|
|
"type": "string",
|
|
"required": true,
|
|
"description": "Unique task identifier (UUID)",
|
|
"constraints": [
|
|
"primary_key"
|
|
]
|
|
},
|
|
{
|
|
"name": "type",
|
|
"type": "enum",
|
|
"required": true,
|
|
"description": "Task category",
|
|
"enum_values": [
|
|
"checkin",
|
|
"ad",
|
|
"survey",
|
|
"referral",
|
|
"quiz",
|
|
"video",
|
|
"reading"
|
|
]
|
|
},
|
|
{
|
|
"name": "title",
|
|
"type": "string",
|
|
"required": true,
|
|
"description": "Task display title"
|
|
},
|
|
{
|
|
"name": "description",
|
|
"type": "string",
|
|
"required": true,
|
|
"description": "Task description"
|
|
},
|
|
{
|
|
"name": "points_reward",
|
|
"type": "integer",
|
|
"required": true,
|
|
"description": "Points awarded upon completion"
|
|
},
|
|
{
|
|
"name": "is_active",
|
|
"type": "boolean",
|
|
"required": true,
|
|
"description": "Whether task is currently available"
|
|
}
|
|
],
|
|
"indexes": [
|
|
{
|
|
"fields": [
|
|
"type",
|
|
"is_active"
|
|
],
|
|
"unique": false
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"id": "model_user_task",
|
|
"name": "UserTask",
|
|
"description": "User task completion records",
|
|
"file_path": "app/lib/db/schema/user_task.ts",
|
|
"status": "PENDING",
|
|
"fields": [
|
|
{
|
|
"name": "id",
|
|
"type": "string",
|
|
"required": true,
|
|
"description": "Unique completion record identifier (UUID)",
|
|
"constraints": [
|
|
"primary_key"
|
|
]
|
|
},
|
|
{
|
|
"name": "user_id",
|
|
"type": "string",
|
|
"required": true,
|
|
"description": "Foreign key to User"
|
|
},
|
|
{
|
|
"name": "task_id",
|
|
"type": "string",
|
|
"required": true,
|
|
"description": "Foreign key to Task"
|
|
},
|
|
{
|
|
"name": "completed_at",
|
|
"type": "timestamp",
|
|
"required": true,
|
|
"description": "Task completion timestamp"
|
|
},
|
|
{
|
|
"name": "points_earned",
|
|
"type": "integer",
|
|
"required": true,
|
|
"description": "Points awarded for this completion"
|
|
}
|
|
],
|
|
"indexes": [
|
|
{
|
|
"fields": [
|
|
"user_id",
|
|
"task_id",
|
|
"completed_at"
|
|
],
|
|
"unique": false
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"id": "model_badge",
|
|
"name": "Badge",
|
|
"description": "Available badges and achievements",
|
|
"file_path": "app/lib/db/schema/badge.ts",
|
|
"status": "PENDING",
|
|
"fields": [
|
|
{
|
|
"name": "id",
|
|
"type": "string",
|
|
"required": true,
|
|
"description": "Unique badge identifier (UUID)",
|
|
"constraints": [
|
|
"primary_key"
|
|
]
|
|
},
|
|
{
|
|
"name": "name",
|
|
"type": "string",
|
|
"required": true,
|
|
"description": "Badge display name"
|
|
},
|
|
{
|
|
"name": "description",
|
|
"type": "string",
|
|
"required": true,
|
|
"description": "Badge description"
|
|
},
|
|
{
|
|
"name": "icon",
|
|
"type": "string",
|
|
"required": true,
|
|
"description": "Badge icon identifier or URL"
|
|
},
|
|
{
|
|
"name": "requirement_type",
|
|
"type": "enum",
|
|
"required": true,
|
|
"description": "Type of requirement to earn badge",
|
|
"enum_values": [
|
|
"points_total",
|
|
"tasks_completed",
|
|
"streak_days",
|
|
"referrals"
|
|
]
|
|
},
|
|
{
|
|
"name": "requirement_value",
|
|
"type": "integer",
|
|
"required": true,
|
|
"description": "Threshold value for requirement"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"id": "model_user_badge",
|
|
"name": "UserBadge",
|
|
"description": "User earned badges",
|
|
"file_path": "app/lib/db/schema/user_badge.ts",
|
|
"status": "PENDING",
|
|
"fields": [
|
|
{
|
|
"name": "id",
|
|
"type": "string",
|
|
"required": true,
|
|
"description": "Unique user badge identifier (UUID)",
|
|
"constraints": [
|
|
"primary_key"
|
|
]
|
|
},
|
|
{
|
|
"name": "user_id",
|
|
"type": "string",
|
|
"required": true,
|
|
"description": "Foreign key to User"
|
|
},
|
|
{
|
|
"name": "badge_id",
|
|
"type": "string",
|
|
"required": true,
|
|
"description": "Foreign key to Badge"
|
|
},
|
|
{
|
|
"name": "earned_at",
|
|
"type": "timestamp",
|
|
"required": true,
|
|
"description": "Badge earned timestamp"
|
|
}
|
|
],
|
|
"indexes": [
|
|
{
|
|
"fields": [
|
|
"user_id",
|
|
"badge_id"
|
|
],
|
|
"unique": true
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"id": "model_quiz",
|
|
"name": "Quiz",
|
|
"description": "Quiz questions for learning tasks",
|
|
"file_path": "app/lib/db/schema/quiz.ts",
|
|
"status": "PENDING",
|
|
"fields": [
|
|
{
|
|
"name": "id",
|
|
"type": "string",
|
|
"required": true,
|
|
"description": "Unique quiz identifier (UUID)",
|
|
"constraints": [
|
|
"primary_key"
|
|
]
|
|
},
|
|
{
|
|
"name": "task_id",
|
|
"type": "string",
|
|
"required": true,
|
|
"description": "Foreign key to Task"
|
|
},
|
|
{
|
|
"name": "question",
|
|
"type": "string",
|
|
"required": true,
|
|
"description": "Quiz question text"
|
|
},
|
|
{
|
|
"name": "options",
|
|
"type": "json",
|
|
"required": true,
|
|
"description": "Array of answer options"
|
|
},
|
|
{
|
|
"name": "correct_answer",
|
|
"type": "string",
|
|
"required": true,
|
|
"description": "Correct answer identifier"
|
|
}
|
|
],
|
|
"indexes": [
|
|
{
|
|
"fields": [
|
|
"task_id"
|
|
],
|
|
"unique": false
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"id": "model_referral",
|
|
"name": "Referral",
|
|
"description": "Referral tracking and rewards",
|
|
"file_path": "app/lib/db/schema/referral.ts",
|
|
"status": "PENDING",
|
|
"fields": [
|
|
{
|
|
"name": "id",
|
|
"type": "string",
|
|
"required": true,
|
|
"description": "Unique referral identifier (UUID)",
|
|
"constraints": [
|
|
"primary_key"
|
|
]
|
|
},
|
|
{
|
|
"name": "referrer_id",
|
|
"type": "string",
|
|
"required": true,
|
|
"description": "Foreign key to User (who referred)"
|
|
},
|
|
{
|
|
"name": "referred_id",
|
|
"type": "string",
|
|
"required": true,
|
|
"description": "Foreign key to User (who was referred)"
|
|
},
|
|
{
|
|
"name": "bonus_points",
|
|
"type": "integer",
|
|
"required": true,
|
|
"description": "Points awarded to referrer"
|
|
},
|
|
{
|
|
"name": "created_at",
|
|
"type": "timestamp",
|
|
"required": true,
|
|
"description": "Referral creation timestamp"
|
|
}
|
|
],
|
|
"indexes": [
|
|
{
|
|
"fields": [
|
|
"referrer_id"
|
|
],
|
|
"unique": false
|
|
},
|
|
{
|
|
"fields": [
|
|
"referred_id"
|
|
],
|
|
"unique": true
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"api_endpoints": [
|
|
{
|
|
"id": "api_auth_register",
|
|
"path": "/api/auth/register",
|
|
"method": "POST",
|
|
"description": "Create new user account",
|
|
"file_path": "app/api/auth/register/route.ts",
|
|
"status": "PENDING",
|
|
"request_body": {
|
|
"email": "string (required)",
|
|
"password": "string (required, min 8 chars)",
|
|
"name": "string (required)"
|
|
},
|
|
"responses": [
|
|
{
|
|
"status": 201,
|
|
"description": "Success",
|
|
"schema": {
|
|
"user": {
|
|
"id": "string",
|
|
"email": "string",
|
|
"name": "string"
|
|
},
|
|
"token": "string (JWT)"
|
|
}
|
|
},
|
|
{
|
|
"status": 400,
|
|
"description": "Error",
|
|
"schema": {
|
|
"error": "string (validation error or email exists)"
|
|
}
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"id": "api_auth_login",
|
|
"path": "/api/auth/login",
|
|
"method": "POST",
|
|
"description": "Login existing user",
|
|
"file_path": "app/api/auth/login/route.ts",
|
|
"status": "PENDING",
|
|
"request_body": {
|
|
"email": "string (required)",
|
|
"password": "string (required)"
|
|
},
|
|
"responses": [
|
|
{
|
|
"status": 200,
|
|
"description": "Success",
|
|
"schema": {
|
|
"user": {
|
|
"id": "string",
|
|
"email": "string",
|
|
"name": "string"
|
|
},
|
|
"token": "string (JWT)"
|
|
}
|
|
},
|
|
{
|
|
"status": 401,
|
|
"description": "Error",
|
|
"schema": {
|
|
"error": "Invalid credentials"
|
|
}
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"id": "api_auth_me",
|
|
"path": "/api/auth/me",
|
|
"method": "GET",
|
|
"description": "Get current authenticated user",
|
|
"file_path": "app/api/auth/me/route.ts",
|
|
"status": "PENDING",
|
|
"auth_required": true,
|
|
"responses": [
|
|
{
|
|
"status": 200,
|
|
"description": "Success",
|
|
"schema": {
|
|
"id": "string",
|
|
"email": "string",
|
|
"name": "string",
|
|
"created_at": "string (ISO date)"
|
|
}
|
|
},
|
|
{
|
|
"status": 401,
|
|
"description": "Error",
|
|
"schema": {
|
|
"error": "Unauthorized"
|
|
}
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"id": "api_users_points",
|
|
"path": "/api/users/me/points",
|
|
"method": "GET",
|
|
"description": "Get user points balance and transaction history",
|
|
"file_path": "app/api/users/me/points/route.ts",
|
|
"status": "PENDING",
|
|
"auth_required": true,
|
|
"responses": [
|
|
{
|
|
"status": 200,
|
|
"description": "Success",
|
|
"schema": {
|
|
"balance": "integer",
|
|
"transactions": [
|
|
{
|
|
"id": "string",
|
|
"amount": "integer",
|
|
"type": "string",
|
|
"source": "string",
|
|
"created_at": "string (ISO date)"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"id": "api_users_badges",
|
|
"path": "/api/users/me/badges",
|
|
"method": "GET",
|
|
"description": "Get user earned badges",
|
|
"file_path": "app/api/users/me/badges/route.ts",
|
|
"status": "PENDING",
|
|
"auth_required": true,
|
|
"responses": [
|
|
{
|
|
"status": 200,
|
|
"description": "Success",
|
|
"schema": {
|
|
"badges": [
|
|
{
|
|
"id": "string",
|
|
"name": "string",
|
|
"description": "string",
|
|
"icon": "string",
|
|
"earned_at": "string (ISO date)"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"id": "api_tasks_list",
|
|
"path": "/api/tasks",
|
|
"method": "GET",
|
|
"description": "List all available tasks",
|
|
"file_path": "app/api/tasks/route.ts",
|
|
"status": "PENDING",
|
|
"auth_required": true,
|
|
"responses": [
|
|
{
|
|
"status": 200,
|
|
"description": "Success",
|
|
"schema": {
|
|
"tasks": [
|
|
{
|
|
"id": "string",
|
|
"type": "string",
|
|
"title": "string",
|
|
"description": "string",
|
|
"points_reward": "integer",
|
|
"is_completed_today": "boolean"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"id": "api_tasks_checkin",
|
|
"path": "/api/tasks/checkin",
|
|
"method": "POST",
|
|
"description": "Complete daily check-in",
|
|
"file_path": "app/api/tasks/checkin/route.ts",
|
|
"status": "PENDING",
|
|
"auth_required": true,
|
|
"responses": [
|
|
{
|
|
"status": 200,
|
|
"description": "Success",
|
|
"schema": {
|
|
"points_earned": "integer",
|
|
"streak_days": "integer",
|
|
"new_balance": "integer"
|
|
}
|
|
},
|
|
{
|
|
"status": 400,
|
|
"description": "Error",
|
|
"schema": {
|
|
"error": "Already checked in today"
|
|
}
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"id": "api_tasks_complete",
|
|
"path": "/api/tasks/:id/complete",
|
|
"method": "POST",
|
|
"description": "Complete a specific task",
|
|
"file_path": "app/api/tasks/[id]/complete/route.ts",
|
|
"status": "PENDING",
|
|
"auth_required": true,
|
|
"responses": [
|
|
{
|
|
"status": 200,
|
|
"description": "Success",
|
|
"schema": {
|
|
"points_earned": "integer",
|
|
"new_balance": "integer"
|
|
}
|
|
},
|
|
{
|
|
"status": 400,
|
|
"description": "Error",
|
|
"schema": {
|
|
"error": "string (task not found or already completed)"
|
|
}
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"id": "api_quizzes_get",
|
|
"path": "/api/quizzes/:taskId",
|
|
"method": "GET",
|
|
"description": "Get quiz questions for a task",
|
|
"file_path": "app/api/quizzes/[taskId]/route.ts",
|
|
"status": "PENDING",
|
|
"auth_required": true,
|
|
"responses": [
|
|
{
|
|
"status": 200,
|
|
"description": "Success",
|
|
"schema": {
|
|
"questions": [
|
|
{
|
|
"id": "string",
|
|
"question": "string",
|
|
"options": [
|
|
"string"
|
|
]
|
|
}
|
|
]
|
|
}
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"id": "api_quizzes_submit",
|
|
"path": "/api/quizzes/:taskId/submit",
|
|
"method": "POST",
|
|
"description": "Submit quiz answers",
|
|
"file_path": "app/api/quizzes/[taskId]/submit/route.ts",
|
|
"status": "PENDING",
|
|
"auth_required": true,
|
|
"request_body": {
|
|
"answers": "object (question_id -> answer mapping)"
|
|
},
|
|
"responses": [
|
|
{
|
|
"status": 200,
|
|
"description": "Success",
|
|
"schema": {
|
|
"score": "integer (0-100)",
|
|
"passed": "boolean",
|
|
"points_earned": "integer",
|
|
"new_balance": "integer"
|
|
}
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"id": "api_leaderboard",
|
|
"path": "/api/leaderboard",
|
|
"method": "GET",
|
|
"description": "Get top users by points",
|
|
"file_path": "app/api/leaderboard/route.ts",
|
|
"status": "PENDING",
|
|
"auth_required": true,
|
|
"query_params": {
|
|
"limit": "integer (optional, default 100)"
|
|
},
|
|
"responses": [
|
|
{
|
|
"status": 200,
|
|
"description": "Success",
|
|
"schema": {
|
|
"leaderboard": [
|
|
{
|
|
"rank": "integer",
|
|
"user_id": "string",
|
|
"name": "string",
|
|
"total_points": "integer"
|
|
}
|
|
],
|
|
"current_user_rank": "integer"
|
|
}
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"id": "api_referrals_create",
|
|
"path": "/api/referrals",
|
|
"method": "POST",
|
|
"description": "Create referral code",
|
|
"file_path": "app/api/referrals/route.ts",
|
|
"status": "PENDING",
|
|
"auth_required": true,
|
|
"responses": [
|
|
{
|
|
"status": 200,
|
|
"description": "Success",
|
|
"schema": {
|
|
"referral_code": "string",
|
|
"referral_url": "string"
|
|
}
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"id": "api_referrals_claim",
|
|
"path": "/api/referrals/claim",
|
|
"method": "POST",
|
|
"description": "Claim referral bonus",
|
|
"file_path": "app/api/referrals/claim/route.ts",
|
|
"status": "PENDING",
|
|
"auth_required": true,
|
|
"request_body": {
|
|
"referral_code": "string (required)"
|
|
},
|
|
"responses": [
|
|
{
|
|
"status": 200,
|
|
"description": "Success",
|
|
"schema": {
|
|
"bonus_points": "integer",
|
|
"new_balance": "integer"
|
|
}
|
|
},
|
|
{
|
|
"status": 400,
|
|
"description": "Error",
|
|
"schema": {
|
|
"error": "string (invalid code or already used)"
|
|
}
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"pages": [
|
|
{
|
|
"id": "page_login",
|
|
"name": "Login Page",
|
|
"description": "User login page",
|
|
"file_path": "app/login/page.tsx",
|
|
"status": "PENDING",
|
|
"components_used": [
|
|
"AuthForm"
|
|
],
|
|
"features": [
|
|
"Email/password login form",
|
|
"Link to register page",
|
|
"Form validation",
|
|
"Error display",
|
|
"Redirect to dashboard on success"
|
|
],
|
|
"path": "/login"
|
|
},
|
|
{
|
|
"id": "page_register",
|
|
"name": "Register Page",
|
|
"description": "User registration page",
|
|
"file_path": "app/register/page.tsx",
|
|
"status": "PENDING",
|
|
"components_used": [
|
|
"AuthForm"
|
|
],
|
|
"features": [
|
|
"Registration form with email, password, name",
|
|
"Link to login page",
|
|
"Form validation (password min 8 chars)",
|
|
"Error display",
|
|
"Redirect to dashboard on success"
|
|
],
|
|
"path": "/register"
|
|
},
|
|
{
|
|
"id": "page_dashboard",
|
|
"name": "Dashboard",
|
|
"description": "Main dashboard with overview",
|
|
"file_path": "app/page.tsx",
|
|
"status": "PENDING",
|
|
"auth_required": true,
|
|
"components_used": [
|
|
"PointsDisplay",
|
|
"TaskCard",
|
|
"DailyCheckinButton",
|
|
"BadgeCard",
|
|
"Navbar"
|
|
],
|
|
"features": [
|
|
"Points balance display",
|
|
"Daily check-in button with streak",
|
|
"Quick task overview (3-5 featured tasks)",
|
|
"Recent badges earned",
|
|
"Navigation to other sections"
|
|
],
|
|
"path": "/"
|
|
},
|
|
{
|
|
"id": "page_tasks",
|
|
"name": "Tasks Page",
|
|
"description": "List of all available tasks",
|
|
"file_path": "app/tasks/page.tsx",
|
|
"status": "PENDING",
|
|
"auth_required": true,
|
|
"components_used": [
|
|
"TaskList",
|
|
"TaskCard",
|
|
"Navbar"
|
|
],
|
|
"features": [
|
|
"List all available tasks",
|
|
"Filter by task type",
|
|
"Show completion status",
|
|
"Click to complete or view task",
|
|
"Points reward display"
|
|
],
|
|
"path": "/tasks"
|
|
},
|
|
{
|
|
"id": "page_quiz",
|
|
"name": "Quiz Page",
|
|
"description": "Quiz interface for learning tasks",
|
|
"file_path": "app/quiz/[id]/page.tsx",
|
|
"status": "PENDING",
|
|
"auth_required": true,
|
|
"components_used": [
|
|
"QuizQuestion",
|
|
"Navbar"
|
|
],
|
|
"features": [
|
|
"Display quiz questions one by one",
|
|
"Multiple choice answers",
|
|
"Submit button",
|
|
"Score display after completion",
|
|
"Points earned display"
|
|
],
|
|
"path": "/quiz/[id]"
|
|
},
|
|
{
|
|
"id": "page_profile",
|
|
"name": "Profile Page",
|
|
"description": "User profile with points history",
|
|
"file_path": "app/profile/page.tsx",
|
|
"status": "PENDING",
|
|
"auth_required": true,
|
|
"components_used": [
|
|
"PointsDisplay",
|
|
"TransactionHistory",
|
|
"BadgeCard",
|
|
"Navbar"
|
|
],
|
|
"features": [
|
|
"User info display",
|
|
"Total points balance",
|
|
"Points transaction history",
|
|
"Earned badges display",
|
|
"Account statistics"
|
|
],
|
|
"path": "/profile"
|
|
},
|
|
{
|
|
"id": "page_leaderboard",
|
|
"name": "Leaderboard Page",
|
|
"description": "Rankings of top users",
|
|
"file_path": "app/leaderboard/page.tsx",
|
|
"status": "PENDING",
|
|
"auth_required": true,
|
|
"components_used": [
|
|
"LeaderboardTable",
|
|
"Navbar"
|
|
],
|
|
"features": [
|
|
"Top 100 users by points",
|
|
"User rank display",
|
|
"Current user highlight",
|
|
"Points totals",
|
|
"Update in real-time"
|
|
],
|
|
"path": "/leaderboard"
|
|
},
|
|
{
|
|
"id": "page_referral",
|
|
"name": "Referral Page",
|
|
"description": "Referral code sharing",
|
|
"file_path": "app/referral/page.tsx",
|
|
"status": "PENDING",
|
|
"auth_required": true,
|
|
"components_used": [
|
|
"Navbar"
|
|
],
|
|
"features": [
|
|
"Generate referral code",
|
|
"Display shareable link",
|
|
"Copy to clipboard button",
|
|
"Show referral statistics",
|
|
"Bonus points display"
|
|
],
|
|
"path": "/referral"
|
|
}
|
|
],
|
|
"components": [
|
|
{
|
|
"id": "component_auth_form",
|
|
"name": "AuthForm",
|
|
"description": "Reusable login/register form component",
|
|
"file_path": "app/components/AuthForm.tsx",
|
|
"status": "PENDING",
|
|
"props": {
|
|
"mode": "'login' | 'register'",
|
|
"onSubmit": "(data: AuthData) => Promise<void>",
|
|
"error": "string | null"
|
|
},
|
|
"features": [
|
|
"Email and password inputs",
|
|
"Name input (register mode only)",
|
|
"Client-side validation",
|
|
"Error display",
|
|
"Loading state",
|
|
"Submit button"
|
|
]
|
|
},
|
|
{
|
|
"id": "component_points_display",
|
|
"name": "PointsDisplay",
|
|
"description": "Display current points balance",
|
|
"file_path": "app/components/PointsDisplay.tsx",
|
|
"status": "PENDING",
|
|
"props": {
|
|
"balance": "number",
|
|
"showAnimation": "boolean (optional)"
|
|
},
|
|
"features": [
|
|
"Large points number display",
|
|
"Coin/points icon",
|
|
"Optional animation on change",
|
|
"Dark theme styling"
|
|
]
|
|
},
|
|
{
|
|
"id": "component_task_card",
|
|
"name": "TaskCard",
|
|
"description": "Display a single task",
|
|
"file_path": "app/components/TaskCard.tsx",
|
|
"status": "PENDING",
|
|
"props": {
|
|
"task": "Task object",
|
|
"onComplete": "() => void",
|
|
"isCompleted": "boolean"
|
|
},
|
|
"features": [
|
|
"Task title and description",
|
|
"Points reward badge",
|
|
"Task type icon",
|
|
"Completion status",
|
|
"Complete button",
|
|
"Dark theme card styling"
|
|
]
|
|
},
|
|
{
|
|
"id": "component_task_list",
|
|
"name": "TaskList",
|
|
"description": "List of task cards with filtering",
|
|
"file_path": "app/components/TaskList.tsx",
|
|
"status": "PENDING",
|
|
"props": {
|
|
"tasks": "Task[]",
|
|
"onTaskComplete": "(taskId: string) => void"
|
|
},
|
|
"features": [
|
|
"Grid layout of TaskCard components",
|
|
"Filter by task type",
|
|
"Empty state message",
|
|
"Loading state"
|
|
]
|
|
},
|
|
{
|
|
"id": "component_quiz_question",
|
|
"name": "QuizQuestion",
|
|
"description": "Quiz question with multiple choice",
|
|
"file_path": "app/components/QuizQuestion.tsx",
|
|
"status": "PENDING",
|
|
"props": {
|
|
"question": "string",
|
|
"options": "string[]",
|
|
"onAnswer": "(answer: string) => void",
|
|
"selectedAnswer": "string | null"
|
|
},
|
|
"features": [
|
|
"Question text display",
|
|
"Radio button options",
|
|
"Highlight selected answer",
|
|
"Dark theme styling"
|
|
]
|
|
},
|
|
{
|
|
"id": "component_badge_card",
|
|
"name": "BadgeCard",
|
|
"description": "Display a badge or achievement",
|
|
"file_path": "app/components/BadgeCard.tsx",
|
|
"status": "PENDING",
|
|
"props": {
|
|
"badge": "Badge object",
|
|
"earned": "boolean",
|
|
"earnedAt": "Date | null"
|
|
},
|
|
"features": [
|
|
"Badge icon display",
|
|
"Badge name and description",
|
|
"Earned/locked state",
|
|
"Earned date (if earned)",
|
|
"Tooltip with requirement"
|
|
]
|
|
},
|
|
{
|
|
"id": "component_leaderboard_table",
|
|
"name": "LeaderboardTable",
|
|
"description": "Rankings table",
|
|
"file_path": "app/components/LeaderboardTable.tsx",
|
|
"status": "PENDING",
|
|
"props": {
|
|
"entries": "LeaderboardEntry[]",
|
|
"currentUserId": "string"
|
|
},
|
|
"features": [
|
|
"Table with rank, name, points columns",
|
|
"Highlight current user row",
|
|
"Top 3 special styling",
|
|
"Responsive design",
|
|
"Dark theme table styling"
|
|
]
|
|
},
|
|
{
|
|
"id": "component_daily_checkin_button",
|
|
"name": "DailyCheckinButton",
|
|
"description": "Check-in button with streak display",
|
|
"file_path": "app/components/DailyCheckinButton.tsx",
|
|
"status": "PENDING",
|
|
"props": {
|
|
"onCheckIn": "() => Promise<void>",
|
|
"isCheckedInToday": "boolean",
|
|
"streakDays": "number"
|
|
},
|
|
"features": [
|
|
"Large prominent button",
|
|
"Streak counter display",
|
|
"Disabled state if already checked in",
|
|
"Success animation",
|
|
"Points earned display"
|
|
]
|
|
},
|
|
{
|
|
"id": "component_transaction_history",
|
|
"name": "TransactionHistory",
|
|
"description": "List of points transactions",
|
|
"file_path": "app/components/TransactionHistory.tsx",
|
|
"status": "PENDING",
|
|
"props": {
|
|
"transactions": "Transaction[]"
|
|
},
|
|
"features": [
|
|
"List of transactions with date, amount, source",
|
|
"Color coding (green for earned, red for spent)",
|
|
"Pagination or infinite scroll",
|
|
"Empty state message",
|
|
"Dark theme styling"
|
|
]
|
|
},
|
|
{
|
|
"id": "component_navbar",
|
|
"name": "Navbar",
|
|
"description": "Main navigation bar",
|
|
"file_path": "app/components/Navbar.tsx",
|
|
"status": "PENDING",
|
|
"props": {
|
|
"currentPath": "string"
|
|
},
|
|
"features": [
|
|
"Links to Dashboard, Tasks, Leaderboard, Profile, Referral",
|
|
"Active link highlighting",
|
|
"User menu with logout",
|
|
"Points balance display",
|
|
"Responsive mobile menu",
|
|
"Dark theme styling"
|
|
]
|
|
},
|
|
{
|
|
"id": "component_dark_theme_layout",
|
|
"name": "DarkThemeLayout",
|
|
"description": "Root layout with dark theme",
|
|
"file_path": "app/components/DarkThemeLayout.tsx",
|
|
"status": "PENDING",
|
|
"props": {
|
|
"children": "React.ReactNode"
|
|
},
|
|
"features": [
|
|
"Dark background colors",
|
|
"Global dark theme CSS variables",
|
|
"Consistent spacing and typography",
|
|
"Tailwind CSS dark mode utilities"
|
|
]
|
|
}
|
|
],
|
|
"technical_notes": {
|
|
"authentication": {
|
|
"method": "JWT tokens",
|
|
"storage": "HTTP-only cookies",
|
|
"library": "jsonwebtoken + bcrypt"
|
|
},
|
|
"database": {
|
|
"type": "PostgreSQL (recommended) or SQLite for dev",
|
|
"orm": "Prisma",
|
|
"migrations": "Prisma migrate"
|
|
},
|
|
"styling": {
|
|
"framework": "Tailwind CSS 4",
|
|
"theme": "Dark mode by default",
|
|
"responsive": "Mobile-first design"
|
|
},
|
|
"state_management": {
|
|
"method": "React Context API or Zustand",
|
|
"server_state": "SWR or TanStack Query"
|
|
},
|
|
"validation": {
|
|
"frontend": "Zod schemas",
|
|
"backend": "Zod schemas (shared)"
|
|
}
|
|
}
|
|
} |