2201 lines
79 KiB
Markdown
2201 lines
79 KiB
Markdown
# Backend Development Tasks - Interview Quiz Application
|
||
|
||
## Project Setup Phase
|
||
|
||
### Task 1: Project Initialization
|
||
**Priority**: High | **Status**: ✅ Completed | **Estimated Time**: 1-2 hours
|
||
|
||
#### Subtasks:
|
||
- [x] Create backend folder structure
|
||
- [x] Initialize Node.js project with `npm init`
|
||
- [x] Install core dependencies
|
||
```bash
|
||
npm install express sequelize mysql2 dotenv bcrypt jsonwebtoken
|
||
npm install express-validator cors helmet morgan
|
||
npm install --save-dev nodemon jest supertest
|
||
```
|
||
- [x] Install Sequelize CLI globally: `npm install -g sequelize-cli`
|
||
- [x] Create `.gitignore` file
|
||
- [x] Create `.env.example` file with all required variables
|
||
- [x] Setup basic Express server in `server.js`
|
||
- [x] Configure port and basic middleware (CORS, JSON parser, helmet)
|
||
|
||
#### Acceptance Criteria:
|
||
- ✅ Server starts successfully on specified port
|
||
- ✅ Environment variables load correctly
|
||
- ✅ Basic middleware functions properly
|
||
|
||
---
|
||
|
||
### Task 2: Database Setup
|
||
**Priority**: High | **Status**: ✅ Completed | **Estimated Time**: 2-3 hours
|
||
|
||
#### Subtasks:
|
||
- [x] Install MySQL 8.0+ locally
|
||
- [x] Create database: `interview_quiz_db`
|
||
- [x] Initialize Sequelize: `npx sequelize-cli init`
|
||
- [x] Create `.sequelizerc` configuration file
|
||
- [x] Configure `config/database.js` with connection settings
|
||
- [x] Test database connection
|
||
- [x] Setup connection pooling configuration
|
||
- [x] Create `models/index.js` for model initialization
|
||
|
||
#### Acceptance Criteria:
|
||
- ✅ Database connection successful
|
||
- ✅ Sequelize properly configured
|
||
- ✅ Connection pool working
|
||
- ✅ Can execute test query
|
||
|
||
#### Files Created:
|
||
- ✅ `.sequelizerc`
|
||
- ✅ `config/database.js`
|
||
- ✅ `config/db.js`
|
||
- ✅ `models/index.js`
|
||
- ✅ `test-db-connection.js`
|
||
|
||
---
|
||
|
||
### Task 3: Environment Configuration
|
||
**Priority**: High | **Status**: ✅ Completed | **Estimated Time**: 1 hour
|
||
|
||
#### Subtasks:
|
||
- [x] Create `.env` file from `.env.example`
|
||
- [x] Configure database credentials
|
||
- [x] Generate JWT secret key
|
||
- [x] Setup NODE_ENV variables
|
||
- [x] Configure API prefix
|
||
- [x] Add rate limiting configuration
|
||
- [x] Add Redis configuration (optional for caching)
|
||
|
||
#### Acceptance Criteria:
|
||
- ✅ All required environment variables configured
|
||
- ✅ Secure JWT secret generated (128 characters)
|
||
- ✅ Environment validation passes
|
||
- ✅ Configuration centralized in config module
|
||
- ✅ Server validates environment on startup
|
||
|
||
#### Files Created:
|
||
- ✅ `.env` (configured with all variables)
|
||
- ✅ `generate-jwt-secret.js` (JWT secret generator)
|
||
- ✅ `validate-env.js` (environment validator)
|
||
- ✅ `config/config.js` (centralized configuration)
|
||
- ✅ `ENVIRONMENT_GUIDE.md` (complete documentation)
|
||
|
||
---
|
||
|
||
## Database Schema Phase
|
||
|
||
### Task 4: Create User Model & Migration
|
||
**Priority**: High | **Status**: ✅ Completed | **Estimated Time**: 2 hours
|
||
|
||
#### Subtasks:
|
||
- [x] Create migration: `npx sequelize-cli migration:generate --name create-users`
|
||
- [x] Define users table schema with UUID primary key
|
||
- [x] Add all user fields (username, email, password, role, stats)
|
||
- [x] Add indexes for email, username, role
|
||
- [x] Create `models/User.js` Sequelize model
|
||
- [x] Add model validations
|
||
- [x] Add password hashing hooks (beforeCreate, beforeUpdate)
|
||
- [x] Test migration: `npx sequelize-cli db:migrate`
|
||
|
||
#### Reference:
|
||
See `SAMPLE_MIGRATIONS.md` - Migration 1
|
||
|
||
#### Acceptance Criteria:
|
||
- ✅ Migration runs successfully
|
||
- ✅ Users table created with all fields
|
||
- ✅ Indexes applied (email, username, role, is_active, created_at)
|
||
- ✅ Model validations work (email, username, password)
|
||
- ✅ Password auto-hashing on create/update
|
||
- ✅ UUID primary key generation
|
||
- ✅ Helper methods (comparePassword, calculateAccuracy, etc.)
|
||
|
||
#### Files Created:
|
||
- ✅ `migrations/20251109214253-create-users.js`
|
||
- ✅ `models/User.js`
|
||
- ✅ `test-user-model.js`
|
||
|
||
---
|
||
|
||
### Task 5: Create Categories Model & Migration
|
||
**Priority**: High | **Status**: ✅ Completed | **Estimated Time**: 1.5 hours
|
||
|
||
#### Subtasks:
|
||
- [x] Create migration: `npx sequelize-cli migration:generate --name create-categories`
|
||
- [x] Define categories table schema
|
||
- [x] Add guest_accessible and count fields
|
||
- [x] Add indexes for slug, is_active, guest_accessible
|
||
- [x] Create `models/Category.js` Sequelize model
|
||
- [x] Add slug generation hook
|
||
- [x] Test migration
|
||
|
||
#### Reference:
|
||
See `SAMPLE_MIGRATIONS.md` - Migration 2
|
||
|
||
#### Acceptance Criteria:
|
||
- ✅ Categories table created with 13 fields and 6 indexes
|
||
- ✅ Slug auto-generation works (beforeValidate, beforeCreate, beforeUpdate hooks)
|
||
- ✅ Model associations defined (Question, QuizSession, GuestSettings)
|
||
- ✅ Helper methods implemented (incrementQuestionCount, findBySlug, etc.)
|
||
- ✅ All 15 tests passing
|
||
|
||
#### Files Created:
|
||
- ✅ `migrations/20251109214935-create-categories.js`
|
||
- ✅ `models/Category.js` (255 lines)
|
||
- ✅ `test-category-model.js` (15 comprehensive tests)
|
||
|
||
---
|
||
|
||
### Task 6: Create Questions Model & Migration
|
||
**Priority**: High | **Status**: ✅ Completed | **Estimated Time**: 3 hours
|
||
|
||
#### Subtasks:
|
||
- [x] Create migration: `npx sequelize-cli migration:generate --name create-questions`
|
||
- [x] Define questions table with JSON columns
|
||
- [x] Add foreign key to categories
|
||
- [x] Add foreign key to users (created_by)
|
||
- [x] Add multiple indexes
|
||
- [x] Add full-text index for search
|
||
- [x] Create `models/Question.js` Sequelize model
|
||
- [x] Handle JSON serialization for options, keywords, tags
|
||
- [x] Add model associations (belongsTo Category, belongsTo User)
|
||
- [x] Test migration
|
||
|
||
#### Reference:
|
||
See `SAMPLE_MIGRATIONS.md` - Migration 3
|
||
|
||
#### Acceptance Criteria:
|
||
- ✅ Questions table created with 20 fields including JSON columns (options, keywords, tags)
|
||
- ✅ Full-text search index created on question_text and explanation
|
||
- ✅ Foreign keys enforced (category_id RESTRICT, created_by SET NULL)
|
||
- ✅ JSON fields serialize/deserialize correctly with custom getters/setters
|
||
- ✅ 10 indexes created including composite indexes for query optimization
|
||
- ✅ Model validations implemented (question type, options, correct answer format)
|
||
- ✅ Helper methods: incrementAttempted, incrementCorrect, getAccuracy, toSafeJSON
|
||
- ✅ Class methods: findActiveQuestions, searchQuestions, getRandomQuestions, getQuestionsByCategory
|
||
- ✅ Auto-set points based on difficulty (easy: 10, medium: 20, hard: 30)
|
||
- ✅ All 18 tests passing
|
||
|
||
#### Files Created:
|
||
- ✅ `migrations/20251109220030-create-questions.js`
|
||
- ✅ `models/Question.js` (455 lines)
|
||
- ✅ `test-question-model.js` (18 comprehensive tests)
|
||
|
||
---
|
||
|
||
### Task 7: Create Guest Sessions Model & Migration
|
||
**Priority**: High | **Status**: ✅ Completed | **Estimated Time**: 1.5 hours
|
||
|
||
#### Subtasks:
|
||
- ✅ Create migration: `npx sequelize-cli migration:generate --name create-guest-sessions`
|
||
- ✅ Define guest_sessions table (14 fields, 7 indexes)
|
||
- ✅ Add indexes for guest_id (unique), session_token (unique), expires_at, is_converted, converted_user_id, device_id, created_at
|
||
- ✅ Create `models/GuestSession.js` model (340+ lines)
|
||
- ✅ Add JWT token generation and verification methods
|
||
- ✅ Add expiry validation and session extension
|
||
- ✅ Add guest-to-user conversion tracking
|
||
- ✅ Test migration (20 comprehensive tests - all passing)
|
||
|
||
#### Reference:
|
||
See `SAMPLE_MIGRATIONS.md` - Migration 4
|
||
|
||
#### Acceptance Criteria:
|
||
- ✅ Guest sessions table created with 14 fields
|
||
- ✅ JWT token generation works (format: guest_{timestamp}_{random})
|
||
- ✅ Expiry check functional with configurable duration
|
||
- ✅ Quiz limit tracking (default 3 quizzes per guest)
|
||
- ✅ Session extension capability (default 24 hours)
|
||
- ✅ Guest-to-user conversion tracking with foreign key
|
||
- ✅ Analytics methods: active count, conversion rate
|
||
- ✅ Test suite: 20 tests covering all functionality
|
||
|
||
#### Files Created:
|
||
- ✅ `migrations/20251109221034-create-guest-sessions.js`
|
||
- ✅ `models/GuestSession.js` (340+ lines with JWT management)
|
||
- ✅ `test-guest-session-model.js` (20 comprehensive tests)
|
||
- ✅ Added test script: `npm run test:guest`
|
||
|
||
---
|
||
|
||
### Task 8: Create Quiz Sessions Model & Migration
|
||
**Priority**: High | **Status**: ✅ Completed | **Estimated Time**: 2 hours
|
||
|
||
#### Subtasks:
|
||
- ✅ Create migration: `npx sequelize-cli migration:generate --name create-quiz-sessions`
|
||
- ✅ Define quiz_sessions table (21 fields, 11 indexes)
|
||
- ✅ Add foreign keys (user_id, guest_session_id, category_id) with proper CASCADE/SET NULL/RESTRICT
|
||
- ✅ Add indexes for user_id, guest_session_id, category_id, status, quiz_type, started_at, completed_at, created_at, is_passed
|
||
- ✅ Add composite indexes for user_status and guest_status
|
||
- ✅ Create `models/QuizSession.js` model (650+ lines)
|
||
- ✅ Add associations (belongsTo User, GuestSession, Category; hasMany QuizAnswer, QuizSessionQuestion)
|
||
- ✅ Implement quiz lifecycle methods (start, complete, abandon, timeout)
|
||
- ✅ Implement scoring and progress tracking
|
||
- ✅ Add validation (require either userId or guestSessionId, not both)
|
||
- ✅ Test migration (26 comprehensive tests - all passing)
|
||
|
||
#### Reference:
|
||
See `SAMPLE_MIGRATIONS.md` - Migration 5
|
||
|
||
#### Acceptance Criteria:
|
||
- ✅ Quiz sessions table created with 21 fields
|
||
- ✅ Support for both user and guest quizzes
|
||
- ✅ Multiple quiz types: practice, timed, exam
|
||
- ✅ Difficulty levels: easy, medium, hard, mixed
|
||
- ✅ Quiz lifecycle management (start, in_progress, complete, abandon, timeout)
|
||
- ✅ Automatic score calculation and pass/fail determination
|
||
- ✅ Time tracking with timeout support for timed quizzes
|
||
- ✅ Progress tracking (questions answered, remaining, accuracy)
|
||
- ✅ Statistics methods: user stats, category stats, history
|
||
- ✅ Cleanup method for abandoned sessions
|
||
- ✅ Test suite: 26 tests covering all functionality
|
||
|
||
#### Files Created:
|
||
- ✅ `migrations/20251110190953-create-quiz-sessions.js`
|
||
- ✅ `models/QuizSession.js` (650+ lines with comprehensive quiz management)
|
||
- ✅ `test-quiz-session-model.js` (26 comprehensive tests)
|
||
- ✅ Added test script: `npm run test:quiz`
|
||
|
||
---
|
||
|
||
### Task 9: Create Quiz Answers & Junction Tables
|
||
**Priority**: High | **Status**: ✅ Completed | **Estimated Time**: 2 hours
|
||
|
||
#### Subtasks:
|
||
- ✅ Create quiz_answers table migration (9 fields, 5 indexes)
|
||
- ✅ Create quiz_session_questions junction table migration (5 fields, 4 indexes)
|
||
- ✅ Create user_bookmarks junction table migration (5 fields, 4 indexes)
|
||
- ✅ Create achievements table migration (13 fields, 5 indexes)
|
||
- ✅ Create user_achievements junction table migration (6 fields, 5 indexes)
|
||
- ✅ Run all migrations successfully
|
||
- ✅ Verify foreign keys and cascade rules
|
||
|
||
#### Acceptance Criteria:
|
||
- ✅ Quiz answers table created - stores individual answers during quizzes
|
||
- ✅ Quiz session questions junction table - links quizzes with questions in order
|
||
- ✅ User bookmarks junction table - allows users to save questions with notes
|
||
- ✅ Achievements table - defines available achievements with requirements
|
||
- ✅ User achievements junction table - tracks earned achievements
|
||
- ✅ All junction tables have unique composite indexes
|
||
- ✅ Foreign key constraints properly enforced (CASCADE on delete/update)
|
||
- ✅ All tables use UTF8MB4 charset for full Unicode support
|
||
|
||
#### Files Created:
|
||
- ✅ `migrations/20251110191735-create-quiz-answers.js` - Quiz answers table
|
||
- ✅ `migrations/20251110191906-create-quiz-session-questions.js` - Quiz-question junction
|
||
- ✅ `migrations/20251110192000-create-user-bookmarks.js` - User bookmarks junction
|
||
- ✅ `migrations/20251110192043-create-achievements.js` - Achievements definitions
|
||
- ✅ `migrations/20251110192130-create-user-achievements.js` - User-achievement junction
|
||
|
||
#### Database Schema Summary:
|
||
**quiz_answers**: Stores each answer given during a quiz
|
||
- Links to quiz_sessions and questions
|
||
- Tracks correct/incorrect, points earned, time taken
|
||
- Unique constraint: one answer per question per session
|
||
|
||
**quiz_session_questions**: Links quiz sessions with questions
|
||
- Maintains question order in quiz
|
||
- Enables loading quiz questions in sequence
|
||
|
||
**user_bookmarks**: User-saved questions for review
|
||
- Optional notes field for user annotations
|
||
- Prevents duplicate bookmarks
|
||
|
||
**achievements**: Gamification system
|
||
- 6 categories: quiz, streak, score, speed, milestone, special
|
||
- 8 requirement types: quizzes_completed, quizzes_passed, perfect_score, streak_days, etc.
|
||
- Configurable points and display order
|
||
|
||
**user_achievements**: Tracks earned achievements
|
||
- Notification tracking
|
||
- Prevents duplicate awards
|
||
|
||
---
|
||
|
||
### Task 10: Database Seeding
|
||
**Priority**: Medium | **Status**: ✅ Completed | **Estimated Time**: 2 hours
|
||
|
||
#### Subtasks:
|
||
- [x] Create seeder for demo categories (7 categories)
|
||
- [x] Create seeder for admin user (admin@quiz.com)
|
||
- [x] Create seeder for sample questions (35 questions - 5 per category)
|
||
- [x] Create seeder for achievements (19 achievements across 6 categories)
|
||
- [x] Run all seeders: `npx sequelize-cli db:seed:all`
|
||
- [x] Test data integrity
|
||
|
||
#### Reference:
|
||
See `SAMPLE_MIGRATIONS.md` - Seeders section
|
||
|
||
#### Acceptance Criteria:
|
||
- ✅ All seed data inserted (7 categories, 1 admin user, 35 questions, 19 achievements)
|
||
- ✅ Relationships maintained (questions linked to categories, created_by admin user)
|
||
- ✅ Admin credentials: admin@quiz.com / Admin@123
|
||
- ✅ Guest-accessible categories: JavaScript, Angular, React (3/7)
|
||
- ✅ Auth-only categories: Node.js, TypeScript, SQL & Databases, System Design (4/7)
|
||
|
||
---
|
||
|
||
## Authentication & Authorization Phase
|
||
|
||
### Task 11: User Registration Endpoint
|
||
**Priority**: High | **Status**: ✅ Completed | **Estimated Time**: 3 hours
|
||
|
||
#### Subtasks:
|
||
- [x] Create `routes/auth.routes.js`
|
||
- [x] Create `controllers/auth.controller.js`
|
||
- [x] Implement `register` controller function
|
||
- [x] Add input validation (email, password strength, username)
|
||
- [x] Check for duplicate email/username
|
||
- [x] Hash password with bcrypt (via User model hook)
|
||
- [x] Generate JWT token
|
||
- [x] Handle guest migration (optional guestSessionId)
|
||
- [x] Add error handling with transactions
|
||
- [x] Write middleware: `validation.middleware.js` and `auth.middleware.js`
|
||
|
||
#### API Endpoint:
|
||
```
|
||
POST /api/auth/register
|
||
Body: {
|
||
username, email, password, guestSessionId (optional)
|
||
}
|
||
```
|
||
|
||
#### Reference:
|
||
See `interview_quiz_user_story.md` - User Story 1.1
|
||
|
||
#### Acceptance Criteria:
|
||
- ✅ User registered successfully with JWT token
|
||
- ✅ Password hashed automatically by User model beforeCreate hook
|
||
- ✅ JWT token returned with user data (password excluded)
|
||
- ✅ Duplicate emails/usernames rejected with 400 status
|
||
- ✅ Input validation works (username 3-50 chars, email format, password min 8 chars with uppercase/lowercase/number)
|
||
- ✅ Guest migration supported with transaction rollback on errors
|
||
- ✅ Login endpoint implemented
|
||
- ✅ Token verification endpoint implemented
|
||
- ✅ Logout endpoint implemented
|
||
- ✅ Auth middleware created (verifyToken, isAdmin, isOwnerOrAdmin)
|
||
|
||
---
|
||
|
||
### Task 12: User Login Endpoint
|
||
**Priority**: High | **Status**: ✅ Completed | **Estimated Time**: 2 hours
|
||
|
||
#### Subtasks:
|
||
- [x] Implement `login` controller function
|
||
- [x] Validate email and password
|
||
- [x] Compare password hash
|
||
- [x] Generate JWT token
|
||
- [x] Update last_login timestamp
|
||
- [x] Return user data (exclude password)
|
||
- [ ] Add rate limiting
|
||
- [x] Write unit tests
|
||
|
||
#### API Endpoint:
|
||
```
|
||
POST /api/auth/login
|
||
Body: { email, password }
|
||
```
|
||
|
||
#### Reference:
|
||
See `SEQUELIZE_QUICK_REFERENCE.md` - User Operations
|
||
|
||
#### Acceptance Criteria:
|
||
- ✅ Login successful with valid credentials
|
||
- ✅ JWT token generated with 24h expiration
|
||
- ✅ Invalid credentials rejected with 401 status
|
||
- ✅ Password comparison using User.comparePassword() method
|
||
- ✅ User data returned (password excluded via toSafeJSON)
|
||
- ✅ Only active users can login (isActive check)
|
||
- ✅ Email normalized to lowercase for case-insensitive login
|
||
- ✅ Tests included in auth.test.js and logout-verify.test.js
|
||
- ⏳ Rate limiting (pending - to be added in Task 44)
|
||
|
||
---
|
||
|
||
### Task 13: JWT Authentication Middleware
|
||
**Priority**: High | **Status**: ✅ Completed | **Estimated Time**: 2 hours
|
||
|
||
#### Subtasks:
|
||
- [x] Create `middleware/auth.middleware.js`
|
||
- [x] Implement `verifyToken` middleware
|
||
- [x] Extract token from Authorization header
|
||
- [x] Verify JWT signature
|
||
- [x] Attach user to request object
|
||
- [x] Handle expired tokens
|
||
- [x] Handle invalid tokens
|
||
- [x] Create `isAdmin` middleware
|
||
- [x] Write tests
|
||
|
||
#### Acceptance Criteria:
|
||
- ✅ Protected routes require valid token (Bearer format)
|
||
- ✅ User data available in req.user (userId, email, username, role)
|
||
- ✅ Expired tokens rejected with 401 status
|
||
- ✅ Admin-only routes protected with isAdmin middleware
|
||
- ✅ isOwnerOrAdmin middleware created for resource ownership checks
|
||
- ✅ Proper error handling (TokenExpiredError, JsonWebTokenError)
|
||
- ✅ Used in auth routes (GET /api/auth/verify)
|
||
- ✅ Tests included in auth.test.js and logout-verify.test.js
|
||
|
||
---
|
||
|
||
### Task 14: User Logout & Token Verification
|
||
**Priority**: Medium | **Status**: ✅ Completed | **Estimated Time**: 1 hour
|
||
|
||
#### Subtasks:
|
||
- [x] Implement `logout` endpoint (client-side token removal)
|
||
- [x] Implement `verifyToken` endpoint
|
||
- [x] Return user info if token valid
|
||
- [x] Write tests
|
||
|
||
#### API Endpoints:
|
||
```
|
||
POST /api/auth/logout
|
||
GET /api/auth/verify
|
||
```
|
||
|
||
#### Acceptance Criteria:
|
||
- ✅ Logout endpoint returns success (stateless JWT approach)
|
||
- ✅ Token verification validates JWT and returns user data
|
||
- ✅ Password excluded from response (toSafeJSON method)
|
||
- ✅ Invalid tokens rejected with 401 status
|
||
- ✅ Missing tokens rejected with 401 status
|
||
- ✅ Expired tokens rejected with 401 status
|
||
- ✅ Inactive users rejected with 404 status
|
||
- ✅ Comprehensive test suite created (15+ tests)
|
||
- ✅ Manual test script created for verification
|
||
- ✅ Token still valid after logout (client-side token removal pattern)
|
||
|
||
---
|
||
|
||
## Guest User Management Phase
|
||
|
||
### Task 15: Guest Session Creation ✅
|
||
**Priority**: High | **Status**: Completed | **Estimated Time**: 2 hours
|
||
|
||
#### Subtasks:
|
||
- [x] Create `routes/guest.routes.js`
|
||
- [x] Create `controllers/guest.controller.js`
|
||
- [x] Implement `startGuestSession` function
|
||
- [x] Generate unique guest_id (`guest_{timestamp}_{random}`)
|
||
- [x] Generate session token (JWT with 24h expiry)
|
||
- [x] Set expiry (24 hours default, configurable)
|
||
- [x] Store IP address and user agent
|
||
- [x] Return available categories (JavaScript, Angular, React)
|
||
- [x] Return quiz restrictions (max 3 quizzes, feature flags)
|
||
- [x] Write tests (7 test scenarios, all passing)
|
||
|
||
#### API Endpoints:
|
||
```
|
||
POST /api/guest/start-session
|
||
Body: { deviceId? }
|
||
Response: { guestId, sessionToken, expiresAt, restrictions, availableCategories }
|
||
|
||
GET /api/guest/session/:guestId
|
||
Response: { guestId, expiresAt, expiresIn, restrictions, availableCategories }
|
||
```
|
||
|
||
#### Reference:
|
||
See `SEQUELIZE_QUICK_REFERENCE.md` - Guest Session Operations
|
||
|
||
#### Acceptance Criteria:
|
||
- ✅ Guest session created with unique ID
|
||
- ✅ Token generated and valid for 24h
|
||
- ✅ Guest restrictions applied (max 3 quizzes)
|
||
- ✅ Feature restrictions set (no bookmarks, no progress tracking)
|
||
- ✅ Guest-accessible categories returned (JavaScript, Angular, React)
|
||
- ✅ Session retrieval and validation working
|
||
- ✅ All 7 tests passing
|
||
|
||
---
|
||
|
||
### Task 16: Guest Quiz Limit Check ✅
|
||
**Priority**: High | **Status**: Completed | **Estimated Time**: 1.5 hours
|
||
|
||
#### Subtasks:
|
||
- [x] Create guest authentication middleware (`middleware/guest.middleware.js`)
|
||
- [x] Implement `verifyGuestToken` middleware with session validation
|
||
- [x] Implement `checkQuizLimit` function in controller
|
||
- [x] Verify guest session exists and not expired (middleware)
|
||
- [x] Check quizzes_attempted vs max_quizzes
|
||
- [x] Return remaining quizzes and calculations
|
||
- [x] Calculate and return reset time (time until session expiry)
|
||
- [x] Return upgrade prompt when limit reached
|
||
- [x] Write comprehensive tests (8 scenarios)
|
||
|
||
#### API Endpoint:
|
||
```
|
||
GET /api/guest/quiz-limit
|
||
Headers: { X-Guest-Token: <token> }
|
||
Response: {
|
||
guestId,
|
||
quizLimit: { maxQuizzes, quizzesAttempted, quizzesRemaining, hasReachedLimit },
|
||
session: { expiresAt, timeRemaining, resetTime },
|
||
upgradePrompt?: { message, benefits[], callToAction }
|
||
}
|
||
```
|
||
|
||
#### Acceptance Criteria:
|
||
- ✅ Guest token middleware validates JWT and session
|
||
- ✅ Middleware checks session exists, not expired, not converted
|
||
- ✅ Quiz limit calculation accurate (max - attempted)
|
||
- ✅ Time remaining calculated correctly (hours and minutes)
|
||
- ✅ Upgrade prompt shown when limit reached (5 benefits listed)
|
||
- ✅ Proper error handling (401, 404, 410 status codes)
|
||
- ✅ All 8 tests passing (valid token, no token, invalid token, non-existent guest, structure validation, calculations, limit reached scenario)
|
||
|
||
---
|
||
|
||
### Task 17: Guest to User Conversion ✅
|
||
**Priority**: Medium | **Status**: Completed | **Estimated Time**: 3 hours
|
||
|
||
#### Subtasks:
|
||
- [x] Review existing guest migration logic in auth registration
|
||
- [x] Create standalone `convertGuestToUser` endpoint
|
||
- [x] Add guest middleware protection (verifies guest session)
|
||
- [x] Implement transaction-based data migration
|
||
- [x] Create new user account with password hashing
|
||
- [x] Migrate quiz sessions from guest to user
|
||
- [x] Calculate and update user stats from migrated sessions
|
||
- [x] Mark guest session as converted (isConverted=true)
|
||
- [x] Handle duplicate email/username validation
|
||
- [x] Handle rollback on error
|
||
- [x] Generate JWT token for new user
|
||
- [x] Write comprehensive tests (10 scenarios)
|
||
|
||
#### API Endpoint:
|
||
```
|
||
POST /api/guest/convert
|
||
Headers: { X-Guest-Token: <token> }
|
||
Body: { username, email, password }
|
||
Response: {
|
||
user: { id, username, email, role },
|
||
token: <JWT>,
|
||
migration: {
|
||
quizzesTransferred: number,
|
||
stats: { totalQuizzes, quizzesPassed, accuracy }
|
||
}
|
||
}
|
||
```
|
||
|
||
#### Reference:
|
||
See `SEQUELIZE_QUICK_REFERENCE.md` - Transaction Example
|
||
|
||
#### Acceptance Criteria:
|
||
- ✅ Guest session validated via middleware (must not be expired or converted)
|
||
- ✅ Input validation (username alphanumeric 3-50 chars, valid email, password 8+ chars)
|
||
- ✅ Duplicate email/username checks with 400 status
|
||
- ✅ User created with transaction rollback on error
|
||
- ✅ Quiz sessions migrated from guestSessionId to userId
|
||
- ✅ User stats calculated from migrated completed quizzes
|
||
- ✅ Guest session marked as converted with convertedUserId
|
||
- ✅ JWT token generated for immediate login
|
||
- ✅ Already converted sessions rejected with 410 status
|
||
- ✅ Converted user can login with new credentials
|
||
- ✅ All 10 tests passing (validation, success, duplicates, converted session, login)
|
||
|
||
#### Notes:
|
||
- Guest migration also supported in registration endpoint (Task 11) via optional guestSessionId parameter
|
||
- This standalone endpoint allows guests to convert without re-entering data
|
||
- Transaction ensures atomic operation - either all data migrates or nothing changes
|
||
- Password automatically hashed by User model beforeCreate hook
|
||
|
||
---
|
||
|
||
## Category Management Phase
|
||
|
||
### Task 18: Get All Categories ✅
|
||
**Priority**: High | **Status**: Completed | **Estimated Time**: 2 hours
|
||
|
||
#### Subtasks:
|
||
- [x] Create `routes/category.routes.js`
|
||
- [x] Create `controllers/category.controller.js`
|
||
- [x] Create optional auth middleware (`optionalAuth`)
|
||
- [x] Implement `getAllCategories` function
|
||
- [x] Filter by isActive
|
||
- [x] Include questionCount from model
|
||
- [x] Handle guest vs registered user view (guestAccessible filter)
|
||
- [x] Order by displayOrder and name
|
||
- [x] Write comprehensive tests (7 scenarios)
|
||
- [ ] Add caching (Redis optional - deferred)
|
||
|
||
#### API Endpoint:
|
||
```
|
||
GET /api/categories
|
||
Headers: { Authorization: Bearer <token> } (optional)
|
||
Response: {
|
||
success: true,
|
||
count: number,
|
||
data: [{ id, name, slug, description, icon, color, questionCount, displayOrder, guestAccessible }],
|
||
message: string
|
||
}
|
||
```
|
||
|
||
#### Reference:
|
||
See `SEQUELIZE_QUICK_REFERENCE.md` - Category Operations
|
||
|
||
#### Acceptance Criteria:
|
||
- ✅ Public endpoint accessible without authentication
|
||
- ✅ Optional auth middleware attaches user if token provided
|
||
- ✅ Guest users see only guest-accessible categories (3: JavaScript, Angular, React)
|
||
- ✅ Authenticated users see all active categories (7 total, including Node.js, TypeScript, SQL, System Design)
|
||
- ✅ Only active categories returned (isActive=true)
|
||
- ✅ Categories ordered by displayOrder, then name
|
||
- ✅ Response includes questionCount for each category
|
||
- ✅ Proper response structure with success, count, data, message
|
||
- ✅ All 7 tests passing (guest view, auth view, filtering, ordering, structure validation)
|
||
|
||
#### Notes:
|
||
- Created `optionalAuth` middleware in auth.middleware.js for public endpoints with optional authentication
|
||
- Guest users see 3 categories, authenticated users see 7 (4 additional auth-only categories)
|
||
- Redis caching deferred to Task 45 (Database Optimization)
|
||
|
||
---
|
||
|
||
### Task 19: Get Category Details ✅
|
||
**Priority**: Medium | **Status**: Completed | **Estimated Time**: 1 hour
|
||
|
||
#### Subtasks:
|
||
- [x] Implement `getCategoryById` function
|
||
- [x] Include related questions preview (first 5 questions)
|
||
- [x] Return category stats (difficulty breakdown, accuracy)
|
||
- [x] Write tests (9 comprehensive scenarios)
|
||
- [x] Add UUID validation for category IDs
|
||
- [x] Implement guest vs authenticated access control
|
||
|
||
#### API Endpoint:
|
||
```
|
||
GET /api/categories/:id
|
||
Headers: { Authorization: Bearer <token> } (optional)
|
||
Response: {
|
||
success: true,
|
||
data: {
|
||
category: { id, name, slug, description, icon, color, questionCount, displayOrder, guestAccessible },
|
||
questionPreview: [{ id, questionText, questionType, difficulty, points, accuracy }],
|
||
stats: {
|
||
totalQuestions,
|
||
questionsByDifficulty: { easy, medium, hard },
|
||
totalAttempts,
|
||
totalCorrect,
|
||
averageAccuracy
|
||
}
|
||
},
|
||
message: string
|
||
}
|
||
```
|
||
|
||
#### Acceptance Criteria:
|
||
- ✅ Category retrieved by UUID (not integer ID)
|
||
- ✅ Question preview limited to 5 questions ordered by creation date
|
||
- ✅ Stats calculated from all active questions in category
|
||
- ✅ Guest users can access guest-accessible categories
|
||
- ✅ Guest users blocked from auth-only categories with 403 status
|
||
- ✅ Authenticated users can access all active categories
|
||
- ✅ Invalid UUID format returns 400 status
|
||
- ✅ Non-existent category returns 404 status
|
||
- ✅ Question accuracy calculated per question
|
||
- ✅ Difficulty breakdown shows easy/medium/hard counts
|
||
- ✅ All 9 tests passing
|
||
|
||
#### Notes:
|
||
- Categories use UUID primary keys, not integers
|
||
- UUID validation accepts format: `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`
|
||
- Question displayOrder column doesn't exist, ordered by createdAt instead
|
||
- Test revealed login response structure: `response.data.data.token`
|
||
|
||
---
|
||
|
||
### Task 20: Create/Update/Delete Category (Admin) ✅
|
||
**Priority**: Medium | **Status**: Completed | **Estimated Time**: 3 hours
|
||
|
||
#### Subtasks:
|
||
- [x] Implement `createCategory` (admin only)
|
||
- [x] Auto-generate slug from name (via model hook)
|
||
- [x] Validate unique slug and name
|
||
- [x] Implement `updateCategory` (admin only)
|
||
- [x] Implement `deleteCategory` (soft delete)
|
||
- [x] Update guest_accessible flag
|
||
- [x] Add authorization middleware (verifyToken + isAdmin)
|
||
- [x] Write comprehensive tests (14 scenarios)
|
||
|
||
#### API Endpoints:
|
||
```
|
||
POST /api/categories (admin)
|
||
Body: { name, slug?, description?, icon?, color?, guestAccessible?, displayOrder? }
|
||
Response: { success, data: { id, name, slug, ... }, message }
|
||
|
||
PUT /api/categories/:id (admin)
|
||
Body: { name?, slug?, description?, icon?, color?, guestAccessible?, displayOrder?, isActive? }
|
||
Response: { success, data: { id, name, slug, ... }, message }
|
||
|
||
DELETE /api/categories/:id (admin)
|
||
Response: { success, data: { id, name, questionCount }, message }
|
||
```
|
||
|
||
#### Acceptance Criteria:
|
||
- ✅ Admin can create new categories with all fields
|
||
- ✅ Slug auto-generated from name if not provided
|
||
- ✅ Custom slug supported with uniqueness validation
|
||
- ✅ Duplicate name/slug rejected with 400 status
|
||
- ✅ Missing required name field rejected with 400 status
|
||
- ✅ Admin can update any category field
|
||
- ✅ Update validates name/slug uniqueness
|
||
- ✅ Non-existent category returns 404 status
|
||
- ✅ Soft delete sets isActive to false (not physical delete)
|
||
- ✅ Deleted category not shown in active category list
|
||
- ✅ Already deleted category cannot be deleted again
|
||
- ✅ Question count included in delete response
|
||
- ✅ Non-admin users blocked from all operations (403 status)
|
||
- ✅ Unauthenticated requests blocked (401 status)
|
||
- ✅ All 14 tests passing
|
||
|
||
#### Notes:
|
||
- Uses existing `isAdmin` middleware for authorization
|
||
- Soft delete preserves data integrity (questions still reference category)
|
||
- Slug generation handled by Category model beforeValidate hook
|
||
- Default color is '#3B82F6' (blue) if not provided
|
||
- displayOrder defaults to 0 if not provided
|
||
|
||
---
|
||
|
||
## Question Management Phase
|
||
|
||
### Task 21: Get Questions by Category ✅
|
||
**Priority**: High | **Status**: Completed | **Estimated Time**: 2 hours
|
||
|
||
#### Subtasks:
|
||
- [x] Create `routes/question.routes.js`
|
||
- [x] Create `controllers/question.controller.js`
|
||
- [x] Implement `getQuestionsByCategory` function
|
||
- [x] Filter by difficulty (optional: easy, medium, hard)
|
||
- [x] Filter by visibility (guest vs authenticated user)
|
||
- [x] Random selection support (random=true query param)
|
||
- [x] Pagination support (limit parameter, max 50)
|
||
- [x] Write comprehensive tests (14 scenarios)
|
||
|
||
#### API Endpoint:
|
||
```
|
||
GET /api/questions/category/:categoryId?difficulty=easy&limit=10&random=true
|
||
Headers: { Authorization: Bearer <token> } (optional)
|
||
Response: {
|
||
success: true,
|
||
count: number,
|
||
total: number,
|
||
category: { id, name, slug, icon, color },
|
||
filters: { difficulty, limit, random },
|
||
data: [{
|
||
id, questionText, questionType, options,
|
||
difficulty, points, timesAttempted, timesCorrect,
|
||
accuracy, explanation, tags, createdAt,
|
||
category: { id, name, slug, icon, color }
|
||
}],
|
||
message: string
|
||
}
|
||
```
|
||
|
||
#### Acceptance Criteria:
|
||
- ✅ Public endpoint with optional authentication
|
||
- ✅ Guest users can access guest-accessible categories (JavaScript, Angular, React)
|
||
- ✅ Guest users blocked from auth-only categories with 403 status
|
||
- ✅ Authenticated users can access all active categories
|
||
- ✅ UUID validation for category IDs (400 for invalid format)
|
||
- ✅ Non-existent category returns 404 status
|
||
- ✅ Difficulty filter works (easy, medium, hard)
|
||
- ✅ Invalid difficulty values ignored (defaults to 'all')
|
||
- ✅ Limit parameter enforced (default 10, max 50)
|
||
- ✅ Random selection works (random=true query param)
|
||
- ✅ Combined filters work (difficulty + limit + random)
|
||
- ✅ Question accuracy calculated (timesCorrect / timesAttempted * 100)
|
||
- ✅ Correct answer NOT exposed in response
|
||
- ✅ Category info included in response
|
||
- ✅ Total count returned (with filters applied)
|
||
- ✅ All 14 tests passing
|
||
|
||
#### Implementation Notes:
|
||
- Uses `optionalAuth` middleware for public access with auth benefits
|
||
- UUID regex validation: `/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i`
|
||
- Random ordering via `sequelize.random()`
|
||
- Default ordering by `createdAt ASC` when random=false
|
||
- Limit validation: `Math.min(Math.max(parseInt(limit) || 10, 1), 50)`
|
||
- Accuracy calculation per question with 0 default for unattempted questions
|
||
- Category association included via `include` with alias 'category'
|
||
- correctAnswer explicitly excluded from response attributes
|
||
|
||
#### Reference:
|
||
See `SEQUELIZE_QUICK_REFERENCE.md` - Question Operations
|
||
|
||
---
|
||
|
||
### Task 22: Get Question by ID ✅
|
||
**Priority**: High | **Status**: Completed | **Estimated Time**: 1 hour
|
||
|
||
#### Subtasks:
|
||
- [x] Implement `getQuestionById` function
|
||
- [x] Check visibility permissions (guest vs authenticated)
|
||
- [x] Don't expose correct_answer in response
|
||
- [x] Include category info with association
|
||
- [x] Write comprehensive tests (12 scenarios)
|
||
|
||
#### API Endpoint:
|
||
```
|
||
GET /api/questions/:id
|
||
Headers: { Authorization: Bearer <token> } (optional)
|
||
Response: {
|
||
success: true,
|
||
data: {
|
||
id, questionText, questionType, options,
|
||
difficulty, points, explanation, tags, keywords,
|
||
accuracy, createdAt, updatedAt,
|
||
statistics: { timesAttempted, timesCorrect, accuracy },
|
||
category: { id, name, slug, icon, color, guestAccessible }
|
||
},
|
||
message: string
|
||
}
|
||
```
|
||
|
||
#### Acceptance Criteria:
|
||
- ✅ Public endpoint with optional authentication
|
||
- ✅ UUID validation for question IDs (400 for invalid format)
|
||
- ✅ Non-existent question returns 404 status
|
||
- ✅ Guest users can access guest-accessible questions
|
||
- ✅ Guest users blocked from auth-only questions with 403 status
|
||
- ✅ Authenticated users can access all active questions
|
||
- ✅ Inactive category questions return 404 status
|
||
- ✅ Correct answer NOT exposed in response
|
||
- ✅ Category information included via association
|
||
- ✅ Question accuracy calculated (timesCorrect / timesAttempted * 100)
|
||
- ✅ Statistics object included (timesAttempted, timesCorrect, accuracy)
|
||
- ✅ All question types supported (multiple, trueFalse, written)
|
||
- ✅ Options array present for multiple choice questions
|
||
- ✅ Tags and keywords fields included (can be null or array)
|
||
- ✅ Points validated by difficulty (easy=5, medium=10, hard=15)
|
||
- ✅ All 12 tests passing
|
||
|
||
#### Implementation Notes:
|
||
- Added `getQuestionById` function to question.controller.js
|
||
- Uses `optionalAuth` middleware for public access with auth benefits
|
||
- UUID regex validation: `/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i`
|
||
- Category association includes guestAccessible flag for access control
|
||
- correctAnswer explicitly excluded from query attributes
|
||
- Statistics object added for clearer attempt/success tracking
|
||
- Category's isActive flag removed from response (internal use only)
|
||
- Route added to question.routes.js as `GET /:id`
|
||
|
||
---
|
||
|
||
### Task 23: Question Search (Full-Text) ✅
|
||
**Priority**: Medium | **Status**: Completed | **Estimated Time**: 2 hours
|
||
|
||
#### Subtasks:
|
||
- [x] Implement `searchQuestions` function
|
||
- [x] Use MySQL MATCH AGAINST for full-text search
|
||
- [x] Filter by category (optional UUID)
|
||
- [x] Filter by difficulty (optional: easy, medium, hard)
|
||
- [x] Highlight matching text with ** markers
|
||
- [x] Pagination support with page and limit
|
||
- [x] Write comprehensive tests (14 scenarios)
|
||
|
||
#### API Endpoint:
|
||
```
|
||
GET /api/questions/search?q=javascript&category=uuid&difficulty=medium&limit=20&page=1
|
||
Headers: { Authorization: Bearer <token> } (optional)
|
||
Response: {
|
||
success: true,
|
||
count: number,
|
||
total: number,
|
||
page: number,
|
||
totalPages: number,
|
||
limit: number,
|
||
query: string,
|
||
filters: { category: uuid|null, difficulty: string|null },
|
||
data: [{
|
||
id, questionText, highlightedText, questionType, options,
|
||
difficulty, points, accuracy, explanation, tags,
|
||
relevance, createdAt,
|
||
category: { id, name, slug, icon, color }
|
||
}],
|
||
message: string
|
||
}
|
||
```
|
||
|
||
#### Acceptance Criteria:
|
||
- ✅ Full-text search using MySQL MATCH AGAINST on question_text and explanation
|
||
- ✅ Search query required (400 if missing or empty)
|
||
- ✅ Guest users see only guest-accessible category results
|
||
- ✅ Authenticated users see all category results
|
||
- ✅ Category filter by UUID (optional, validated)
|
||
- ✅ Difficulty filter (easy, medium, hard) (optional)
|
||
- ✅ Combined filters work (category + difficulty)
|
||
- ✅ Invalid category UUID returns 400
|
||
- ✅ Pagination support with page and limit parameters
|
||
- ✅ Default limit 20, max limit 100
|
||
- ✅ Results ordered by relevance DESC, then createdAt DESC
|
||
- ✅ Relevance score included in each result
|
||
- ✅ Text highlighting applied with ** markers for matched terms
|
||
- ✅ Response includes total, totalPages, page metadata
|
||
- ✅ Correct answer NOT exposed in results
|
||
- ✅ All 14 tests passing
|
||
|
||
#### Implementation Notes:
|
||
- Added `searchQuestions` function to question.controller.js (240+ lines)
|
||
- Uses raw SQL with `sequelize.query()` for MATCH AGAINST full-text search
|
||
- Full-text index exists on questions(question_text, explanation)
|
||
- Text highlighting helper function splits search term into words
|
||
- Words shorter than 3 characters excluded from highlighting
|
||
- Pagination uses LIMIT and OFFSET for efficient paging
|
||
- Two queries: one for results, one for total count
|
||
- Route added as `GET /search` (must come before `/:id` to avoid conflicts)
|
||
- Guest accessibility checked via category join in WHERE clause
|
||
- JSON fields (options, tags) parsed from database strings
|
||
- Accuracy calculated per question (timesCorrect / timesAttempted * 100)
|
||
|
||
#### Reference:
|
||
See `SEQUELIZE_QUICK_REFERENCE.md` - Search Questions
|
||
|
||
---
|
||
|
||
### Task 24: Create Question (Admin) ✅
|
||
**Priority**: Medium | **Status**: Completed | **Estimated Time**: 3 hours
|
||
|
||
#### Subtasks:
|
||
- [x] Implement `createQuestion` (admin only)
|
||
- [x] Validate question type (multiple/trueFalse/written)
|
||
- [x] Validate options for multiple choice (2-6 options)
|
||
- [x] Store options, keywords, tags as JSON
|
||
- [x] Auto-calculate points based on difficulty
|
||
- [x] Validate category exists and is active
|
||
- [x] Increment category question count
|
||
- [x] Write comprehensive tests (16 scenarios)
|
||
|
||
#### API Endpoint:
|
||
```
|
||
POST /api/admin/questions
|
||
Headers: { Authorization: Bearer <admin-token> }
|
||
Body: {
|
||
questionText: string (required),
|
||
questionType: 'multiple' | 'trueFalse' | 'written' (required),
|
||
options: [{ id, text }] (required for multiple choice),
|
||
correctAnswer: string (required),
|
||
difficulty: 'easy' | 'medium' | 'hard' (required),
|
||
points: number (optional, auto-calculated if not provided),
|
||
explanation: string (optional),
|
||
categoryId: uuid (required),
|
||
tags: string[] (optional),
|
||
keywords: string[] (optional)
|
||
}
|
||
Response: {
|
||
success: true,
|
||
data: {
|
||
id, questionText, questionType, options,
|
||
difficulty, points, explanation, tags, keywords,
|
||
category: { id, name, slug, icon, color },
|
||
createdAt
|
||
},
|
||
message: "Question created successfully"
|
||
}
|
||
```
|
||
|
||
#### Acceptance Criteria:
|
||
- ✅ Admin-only access (verifyToken + isAdmin middleware)
|
||
- ✅ Non-admin users blocked with 403 status
|
||
- ✅ Unauthenticated requests blocked with 401 status
|
||
- ✅ Required fields validated (questionText, questionType, correctAnswer, difficulty, categoryId)
|
||
- ✅ Question type validated (multiple, trueFalse, written)
|
||
- ✅ Difficulty validated (easy, medium, hard)
|
||
- ✅ Category UUID format validated
|
||
- ✅ Category existence checked (404 if not found)
|
||
- ✅ Inactive categories rejected
|
||
- ✅ Multiple choice questions require options array
|
||
- ✅ Options must have 2-6 items with id and text fields
|
||
- ✅ Correct answer must match one of the option IDs
|
||
- ✅ True/False questions validate correctAnswer as 'true' or 'false'
|
||
- ✅ Points auto-calculated: easy=5, medium=10, hard=15
|
||
- ✅ Custom points supported (overrides auto-calculation)
|
||
- ✅ Tags and keywords stored as JSON arrays
|
||
- ✅ Category questionCount incremented
|
||
- ✅ Question includes category info in response
|
||
- ✅ Created by admin userId tracked
|
||
- ✅ Correct answer NOT exposed in response
|
||
- ✅ All 16 tests passing
|
||
|
||
#### Implementation Notes:
|
||
- Added `createQuestion` function to question.controller.js (260+ lines)
|
||
- Created admin.routes.js for admin-only endpoints
|
||
- Route: POST `/api/admin/questions` with verifyToken + isAdmin
|
||
- Registered admin routes in server.js at `/api/admin`
|
||
- Comprehensive validation for all question types
|
||
- Options validation: min 2, max 6 options
|
||
- Each option requires `id` and `text` fields
|
||
- True/False answers validated as string 'true' or 'false'
|
||
- Points auto-calculation based on difficulty
|
||
- Category.increment('questionCount') updates count
|
||
- Question reloaded with category association after creation
|
||
- createdBy field set to req.user.userId from JWT
|
||
- JSON fields (options, tags, keywords) handled by Sequelize getters/setters
|
||
|
||
#### Reference:
|
||
See `interview_quiz_user_story.md` - User Story 5.1
|
||
|
||
---
|
||
|
||
### Task 25: Update/Delete Question (Admin) ✅
|
||
**Priority**: Medium | **Status**: Completed | **Estimated Time**: 2 hours
|
||
|
||
#### Subtasks:
|
||
- [x] Implement `updateQuestion` (admin only)
|
||
- [x] Validate changes (partial updates supported)
|
||
- [x] Implement `deleteQuestion` (soft delete)
|
||
- [x] Update category counts on category change and delete
|
||
- [x] Write comprehensive tests (26 scenarios)
|
||
|
||
#### API Endpoints:
|
||
```
|
||
PUT /api/admin/questions/:id
|
||
Headers: { Authorization: Bearer <admin-token> }
|
||
Body: {
|
||
questionText?: string,
|
||
questionType?: 'multiple' | 'trueFalse' | 'written',
|
||
options?: [{ id, text }],
|
||
correctAnswer?: string,
|
||
difficulty?: 'easy' | 'medium' | 'hard',
|
||
points?: number,
|
||
explanation?: string,
|
||
categoryId?: uuid,
|
||
tags?: string[],
|
||
keywords?: string[],
|
||
isActive?: boolean
|
||
}
|
||
Response: {
|
||
success: true,
|
||
data: {
|
||
id, questionText, questionType, options,
|
||
difficulty, points, explanation, tags, keywords,
|
||
category: { id, name, slug, icon, color },
|
||
isActive, updatedAt
|
||
},
|
||
message: "Question updated successfully"
|
||
}
|
||
|
||
DELETE /api/admin/questions/:id
|
||
Headers: { Authorization: Bearer <admin-token> }
|
||
Response: {
|
||
success: true,
|
||
data: {
|
||
id, questionText,
|
||
category: { id, name }
|
||
},
|
||
message: "Question deleted successfully"
|
||
}
|
||
```
|
||
|
||
#### Acceptance Criteria:
|
||
- ✅ Admin-only access (verifyToken + isAdmin middleware)
|
||
- ✅ Non-admin users blocked with 403 status
|
||
- ✅ Unauthenticated requests blocked with 401 status
|
||
- ✅ Partial updates supported (only provided fields updated)
|
||
- ✅ Question text validation (non-empty after trim)
|
||
- ✅ Question type validation (multiple, trueFalse, written)
|
||
- ✅ Options validation for multiple choice (2-6 options)
|
||
- ✅ Correct answer validation matches option IDs
|
||
- ✅ True/False answer validation ('true' or 'false')
|
||
- ✅ Difficulty validation (easy, medium, hard)
|
||
- ✅ Auto-points calculation when difficulty changes
|
||
- ✅ Custom points override supported
|
||
- ✅ Category UUID validation and existence check
|
||
- ✅ Category counts updated when category changes
|
||
- ✅ Invalid UUID format returns 400
|
||
- ✅ Non-existent question returns 404
|
||
- ✅ Soft delete sets isActive to false (not physical delete)
|
||
- ✅ Already deleted question cannot be deleted again
|
||
- ✅ Category question count decremented on delete
|
||
- ✅ Deleted questions not accessible via API
|
||
- ✅ Correct answer NOT exposed in response
|
||
- ✅ All 26 tests passing (19 update + 7 delete scenarios)
|
||
|
||
#### Implementation Notes:
|
||
- Added `updateQuestion` function to question.controller.js (200+ lines)
|
||
- Added `deleteQuestion` function to question.controller.js (70+ lines)
|
||
- Routes added to admin.routes.js: PUT `/api/admin/questions/:id` and DELETE `/api/admin/questions/:id`
|
||
- Partial update pattern: only fields provided in request body are updated
|
||
- Effective type validation: uses updated questionType if provided, else existing
|
||
- Category count management: decrement old category, increment new category on change
|
||
- Soft delete preserves data integrity (questions still exist in database)
|
||
- Category.decrement('questionCount') on delete maintains accurate counts
|
||
- UUID validation for both question ID and category ID
|
||
- Empty/whitespace-only question text rejected
|
||
- Points auto-calculated when difficulty changes (unless custom points provided)
|
||
- Tags and keywords optional (can be null or arrays)
|
||
- isActive flag allows manual activation/deactivation
|
||
- Test suite covers all validation scenarios and edge cases
|
||
|
||
#### Reference:
|
||
See `SEQUELIZE_QUICK_REFERENCE.md` - Update/Delete Operations
|
||
|
||
---
|
||
|
||
## Quiz Session Management Phase
|
||
|
||
### Task 26: Start Quiz Session ✅
|
||
**Priority**: High | **Status**: Completed | **Estimated Time**: 3 hours
|
||
|
||
#### Subtasks:
|
||
- [x] Create `routes/quiz.routes.js`
|
||
- [x] Create `controllers/quiz.controller.js`
|
||
- [x] Create `models/QuizSessionQuestion.js` junction model
|
||
- [x] Implement `startQuizSession` function
|
||
- [x] Check guest quiz limit (if guest)
|
||
- [x] Create quiz session record with transaction
|
||
- [x] Select random questions from category
|
||
- [x] Create quiz_session_questions junction records
|
||
- [x] Return session ID and questions (without correct answers)
|
||
- [x] Increment guest quizzes_attempted
|
||
- [x] Write comprehensive tests (20 scenarios, all passing)
|
||
|
||
#### API Endpoint:
|
||
```
|
||
POST /api/quiz/start
|
||
Headers: { Authorization: Bearer <token> } OR { X-Guest-Token: <token> }
|
||
Body: {
|
||
categoryId: uuid (required),
|
||
questionCount: number (1-50, default 10),
|
||
difficulty: 'easy' | 'medium' | 'hard' | 'mixed' (default 'mixed'),
|
||
quizType: 'practice' | 'timed' | 'exam' (default 'practice')
|
||
}
|
||
Response: {
|
||
success: true,
|
||
data: {
|
||
sessionId: uuid,
|
||
category: { id, name, slug, icon, color },
|
||
quizType: string,
|
||
difficulty: string,
|
||
totalQuestions: number,
|
||
totalPoints: number,
|
||
timeLimit: number | null (minutes, 2 per question for timed/exam),
|
||
status: 'in_progress',
|
||
startedAt: timestamp,
|
||
questions: [{
|
||
id, questionText, questionType, options,
|
||
difficulty, points, tags, order
|
||
}]
|
||
},
|
||
message: "Quiz session started successfully"
|
||
}
|
||
```
|
||
|
||
#### Acceptance Criteria:
|
||
- ✅ Supports both authenticated users and guest sessions
|
||
- ✅ Dual authentication middleware (user JWT or guest token)
|
||
- ✅ Category validation (exists, active, guest-accessible check)
|
||
- ✅ Guest quiz limit checked before session creation
|
||
- ✅ Guest quizzes_attempted incremented on quiz start
|
||
- ✅ Guest session expiry validated
|
||
- ✅ Converted guest sessions rejected
|
||
- ✅ Question count validation (1-50, default 10)
|
||
- ✅ Difficulty validation (easy, medium, hard, mixed)
|
||
- ✅ Quiz type validation (practice, timed, exam)
|
||
- ✅ Random question selection from category
|
||
- ✅ Difficulty filtering applied (or mixed for all difficulties)
|
||
- ✅ Handles insufficient questions gracefully
|
||
- ✅ Total points calculated from selected questions
|
||
- ✅ Time limit set for timed/exam quizzes (2 minutes per question)
|
||
- ✅ Quiz session created with in_progress status
|
||
- ✅ Junction records created for quiz-question linkage
|
||
- ✅ Questions ordered sequentially (1-based)
|
||
- ✅ Correct answers NOT exposed in response
|
||
- ✅ Transaction rollback on errors
|
||
- ✅ UUID validation for category IDs
|
||
- ✅ Proper error handling and status codes
|
||
- ✅ All 20 tests passing (user quizzes, guest quizzes, validation, structure)
|
||
|
||
#### Implementation Notes:
|
||
- Created `models/QuizSessionQuestion.js` for junction table (58 lines)
|
||
- Added `controllers/quiz.controller.js` with startQuizSession function (274 lines)
|
||
- Created `routes/quiz.routes.js` with dual authentication middleware (58 lines)
|
||
- Registered quiz routes in server.js at `/api/quiz`
|
||
- Dual auth middleware: tries user JWT first, then guest token using async/await with Promise wrapping
|
||
- Fixed guest middleware to set both `req.guestId` (string) and `req.guestSessionId` (UUID for foreign keys)
|
||
- Fixed time limit to be stored in seconds (model validation requires min 60 seconds)
|
||
- Time limit calculation: totalQuestions × 2 minutes × 60 seconds = 120 seconds per question
|
||
- Time limit displayed in response as minutes for better UX
|
||
- Transaction ensures atomic quiz creation with rollback on errors
|
||
- Random question selection via `sequelize.random()`
|
||
- Questions returned with sequential order (1, 2, 3...)
|
||
- Guest limit enforcement integrated into quiz start flow
|
||
- Category guestAccessible flag checked for guest users
|
||
- Junction table stores questionOrder for maintaining quiz sequence
|
||
- Response excludes correctAnswer, createdBy, and other sensitive fields
|
||
- Test suite covers user quizzes (6 tests), guest quizzes (4 tests), validation (7 tests), structure (3 tests)
|
||
|
||
#### Reference:
|
||
See `SEQUELIZE_QUICK_REFERENCE.md` - Start Quiz Session
|
||
|
||
---
|
||
|
||
### Task 27: Submit Answer
|
||
**Priority**: High | **Status**: ✅ Completed | **Estimated Time**: 2 hours
|
||
|
||
#### Subtasks:
|
||
- [x] Create QuizAnswer model with UUID, foreign keys, validations
|
||
- [x] Implement `submitAnswer` controller function (232 lines)
|
||
- [x] Validate session exists, in-progress, belongs to user/guest
|
||
- [x] Check if question belongs to session
|
||
- [x] Check if already answered (duplicate prevention)
|
||
- [x] Compare answer with correct_answer (case-insensitive, JSON parsing)
|
||
- [x] Save to quiz_answers table with transaction
|
||
- [x] Update quiz session score if correct
|
||
- [x] Increment question times_attempted and times_correct
|
||
- [x] Update session questionsAnswered, correctAnswers, timeSpent
|
||
- [x] Return immediate feedback (isCorrect, explanation, points, progress)
|
||
- [x] Add dual authentication route (user JWT or guest token)
|
||
- [x] Write comprehensive tests (14 scenarios, all passing)
|
||
|
||
#### API Endpoint:
|
||
```
|
||
POST /api/quiz/submit
|
||
Body: { quizSessionId, questionId, userAnswer, timeTaken }
|
||
Auth: User JWT token OR Guest token (X-Guest-Token)
|
||
```
|
||
|
||
#### Implementation Details:
|
||
- **Transaction-based**: All database updates atomic with rollback on errors
|
||
- **Answer Comparison**: Case-insensitive, trimmed, JSON parsing for multiple choice arrays
|
||
- **Validation Chain**: 10+ checks including UUID format, authorization, session status
|
||
- **Scoring**: Points awarded only for correct answers, session score incremented
|
||
- **Statistics**: Updates both session progress and question attempt counts
|
||
- **Feedback**: Includes explanation always, correct answer shown only if incorrect
|
||
- **Dual Auth**: Works for authenticated users and guest sessions
|
||
|
||
#### Files Created:
|
||
- ✅ `models/QuizAnswer.js` (129 lines) - Complete model with associations
|
||
- ✅ `controllers/quiz.controller.js` - Added submitAnswer (232 lines)
|
||
- ✅ `routes/quiz.routes.js` - Added POST /submit route
|
||
- ✅ `test-submit-answer.js` (450+ lines) - 14 comprehensive tests
|
||
|
||
#### Test Results:
|
||
✅ All 14/14 tests passing:
|
||
- Basic Functionality (3): proper feedback, incorrect handling, explanation included
|
||
- Validation (6): missing fields, invalid UUIDs, non-existent session
|
||
- Authorization (3): cross-user prevention, guest support, unauthenticated blocked
|
||
- Duplicate Prevention (1): cannot submit same question twice
|
||
- Question Validation (1): question must belong to session
|
||
|
||
#### Reference:
|
||
See `SEQUELIZE_QUICK_REFERENCE.md` - Submit Answer
|
||
|
||
---
|
||
|
||
### Task 28: Complete Quiz Session
|
||
**Priority**: High | **Status**: ✅ Completed | **Estimated Time**: 3 hours
|
||
|
||
#### Subtasks:
|
||
- [x] Implement `completeQuizSession` controller function (220+ lines)
|
||
- [x] Calculate final score from session (with DECIMAL field parsing)
|
||
- [x] Calculate percentage based on total points
|
||
- [x] Calculate time taken in seconds (start to completion)
|
||
- [x] Update session status to 'completed' or 'timeout'
|
||
- [x] Set endTime and completedAt timestamps
|
||
- [x] Update user stats (totalQuizzes, quizzesPassed, accuracy, streak)
|
||
- [x] Return comprehensive results with category, score, questions breakdown
|
||
- [x] Time limit validation for timed/exam quizzes
|
||
- [x] Add dual authentication route (user JWT or guest token)
|
||
- [x] Write comprehensive tests (15 scenarios, 14/15 passing)
|
||
|
||
#### API Endpoint:
|
||
```
|
||
POST /api/quiz/complete
|
||
Body: { sessionId: uuid }
|
||
Auth: User JWT token OR Guest token (X-Guest-Token)
|
||
Response: {
|
||
sessionId, status, category, quizType, difficulty,
|
||
score: { earned, total, percentage },
|
||
questions: { total, answered, correct, incorrect, unanswered },
|
||
accuracy, isPassed,
|
||
time: { started, completed, taken, limit, isTimeout }
|
||
}
|
||
```
|
||
|
||
#### Implementation Details:
|
||
- **Transaction-based**: All database updates atomic with rollback on errors
|
||
- **Score Calculation**: Final score from session.score (parses DECIMAL to number)
|
||
- **Percentage**: (earned / total) * 100, rounded to integer
|
||
- **Pass/Fail**: 70% threshold determines isPassed flag
|
||
- **Time Tracking**: Calculates seconds from startedAt to completedAt
|
||
- **Timeout Detection**: Marks as 'timeout' if exceeded timeLimit
|
||
- **User Stats Updates**: Increments totalQuizzes, quizzesPassed, totalQuestionsAnswered, correctAnswers
|
||
- **Streak Calculation**: Updates currentStreak and longestStreak based on lastActiveDate
|
||
- **Partial Completion**: Supports completing with unanswered questions
|
||
- **Dual Auth**: Works for authenticated users and guest sessions
|
||
|
||
#### Files Created/Modified:
|
||
- ✅ `controllers/quiz.controller.js` - Added completeQuizSession function (220 lines)
|
||
- ✅ `routes/quiz.routes.js` - Added POST /complete route
|
||
- ✅ `test-complete-quiz.js` (530+ lines) - 15 comprehensive tests
|
||
|
||
#### Test Results:
|
||
✅ 14/15 tests passing consistently (15th test subject to rate limiting):
|
||
- Basic Functionality (5): detailed results, guest completion, percentage, pass/fail, time tracking
|
||
- Validation (6): missing fields, invalid UUIDs, non-existent session, authorization, already completed, unauthenticated
|
||
- Partial Completion (4): unanswered questions supported, status updated, category info, counts accurate
|
||
|
||
#### Bug Fixes:
|
||
- Fixed User model field names: `correctAnswers` (not `totalCorrectAnswers`)
|
||
- Fixed DECIMAL field parsing: `parseFloat()` for score and totalPoints
|
||
- Fixed test for variable question counts per category
|
||
|
||
#### Reference:
|
||
See `SEQUELIZE_QUICK_REFERENCE.md` - Complete Quiz Session
|
||
|
||
---
|
||
|
||
### Task 29: Get Session Details
|
||
**Priority**: Medium | **Status**: ✅ Completed | **Estimated Time**: 1.5 hours
|
||
|
||
#### Subtasks:
|
||
- [x] Implement `getSessionDetails` controller function (204 lines)
|
||
- [x] Return comprehensive session info with category details
|
||
- [x] Include all questions with answers and feedback
|
||
- [x] Include progress tracking (answered, correct, incorrect, unanswered)
|
||
- [x] Check authorization (own session only - user or guest)
|
||
- [x] Add dual authentication route (user JWT or guest token)
|
||
- [x] Write comprehensive tests (14 scenarios, all passing)
|
||
|
||
#### API Endpoint:
|
||
```
|
||
GET /api/quiz/session/:sessionId
|
||
Headers: { Authorization: Bearer <token> } OR { X-Guest-Token: <token> }
|
||
Response: {
|
||
success: true,
|
||
data: {
|
||
session: {
|
||
id, status, quizType, difficulty,
|
||
category: { id, name, slug, icon, color },
|
||
score: { earned, total, percentage },
|
||
isPassed, startedAt, completedAt,
|
||
timeSpent, timeLimit, timeRemaining
|
||
},
|
||
progress: {
|
||
totalQuestions, answeredQuestions, correctAnswers,
|
||
incorrectAnswers, unansweredQuestions, progressPercentage
|
||
},
|
||
questions: [{
|
||
id, questionText, questionType, options,
|
||
difficulty, points, explanation, tags, order,
|
||
correctAnswer, userAnswer, isCorrect,
|
||
pointsEarned, timeTaken, answeredAt, isAnswered
|
||
}]
|
||
},
|
||
message: "Quiz session details retrieved successfully"
|
||
}
|
||
```
|
||
|
||
#### Implementation Details:
|
||
- **Authorization**: Verifies session belongs to authenticated user or guest
|
||
- **Smart Correct Answer Display**:
|
||
- In-progress sessions: Shows correct answer only for answered questions
|
||
- Completed/timeout sessions: Shows correct answers for all questions
|
||
- **Progress Tracking**: Real-time calculation of answered/unanswered questions
|
||
- **Time Tracking**:
|
||
- timeSpent: seconds from start to now (or completion)
|
||
- timeRemaining: null for practice, calculated for timed/exam
|
||
- **Question Details**: Includes all question data plus user's answer and feedback
|
||
- **Dual Auth**: Works for authenticated users and guest sessions
|
||
- **Field Mapping**: Uses `selectedOption` from QuizAnswer model (not `userAnswer`)
|
||
|
||
#### Files Created/Modified:
|
||
- ✅ `controllers/quiz.controller.js` - Added getSessionDetails function (204 lines)
|
||
- ✅ `routes/quiz.routes.js` - Added GET /session/:sessionId route
|
||
- ✅ `test-session-details.js` (620+ lines) - 14 comprehensive tests
|
||
|
||
#### Test Results:
|
||
✅ All 14/14 tests passing:
|
||
- Basic Functionality (3): in-progress session, completed session, guest session
|
||
- Validation (5): missing ID, invalid UUID, non-existent session, cross-user access, unauthenticated
|
||
- Structure Validation (3): session fields, progress fields, question fields
|
||
- Calculations (3): time tracking, progress percentages, answer feedback
|
||
|
||
#### Bug Fixes:
|
||
- Fixed field name: `selectedOption` in QuizAnswer model (not `userAnswer`)
|
||
- Fixed boolean logic: `isCorrect` now properly handles `false` values
|
||
|
||
#### Reference:
|
||
See `SEQUELIZE_QUICK_REFERENCE.md` - Get Quiz Session Details
|
||
|
||
---
|
||
|
||
### Task 30: Review Completed Quiz
|
||
**Priority**: Medium | **Status**: ✅ Completed | **Estimated Time**: 2 hours
|
||
|
||
#### Subtasks:
|
||
- [x] Implement `reviewQuizSession` controller function (230+ lines)
|
||
- [x] Return all questions with user answers and correct answers
|
||
- [x] Include explanations for all questions
|
||
- [x] Mark correct/incorrect with visual feedback (resultStatus)
|
||
- [x] Include time spent per question and time statistics
|
||
- [x] Add multiple choice option-level feedback (isCorrect, isSelected, feedback)
|
||
- [x] Validate session is completed or timed out (reject in-progress)
|
||
- [x] Add dual authentication route (user JWT or guest token)
|
||
- [x] Write comprehensive tests (16 scenarios, all passing)
|
||
|
||
#### API Endpoint:
|
||
```
|
||
GET /api/quiz/review/:sessionId
|
||
Headers: { Authorization: Bearer <token> } OR { X-Guest-Token: <token> }
|
||
Response: {
|
||
success: true,
|
||
data: {
|
||
session: {
|
||
id, status, quizType, difficulty,
|
||
category: { id, name, slug, icon, color },
|
||
startedAt, completedAt, timeSpent
|
||
},
|
||
summary: {
|
||
score: { earned, total, percentage },
|
||
questions: { total, answered, correct, incorrect, unanswered },
|
||
accuracy, isPassed,
|
||
timeStatistics: {
|
||
totalTime, averageTimePerQuestion,
|
||
timeLimit, wasTimedOut
|
||
}
|
||
},
|
||
questions: [{
|
||
id, questionText, questionType, options,
|
||
difficulty, points, explanation, tags, order,
|
||
correctAnswer, userAnswer, isCorrect,
|
||
resultStatus, pointsEarned, pointsPossible,
|
||
timeTaken, answeredAt, showExplanation, wasAnswered
|
||
}]
|
||
},
|
||
message: "Quiz review retrieved successfully"
|
||
}
|
||
```
|
||
|
||
#### Implementation Details:
|
||
- **Status Validation**: Only allows review of completed or timed out quizzes (rejects in-progress with 400)
|
||
- **Authorization**: Verifies session belongs to authenticated user or guest
|
||
- **Result Status**: Each question marked as 'correct', 'incorrect', or 'unanswered' for visual feedback
|
||
- **Multiple Choice Feedback**: Options include isCorrect, isSelected, and feedback fields ('correct-answer', 'user-selected-wrong', or null)
|
||
- **Explanations**: Always shown in review (showExplanation=true for all questions)
|
||
- **Points Tracking**:
|
||
- pointsEarned: actual points earned for this question
|
||
- pointsPossible: maximum points available
|
||
- Summary includes total earned vs total possible
|
||
- **Time Statistics**:
|
||
- Per-question: timeTaken and answeredAt timestamp
|
||
- Summary: totalTime, averageTimePerQuestion
|
||
- Timeout detection: wasTimedOut flag if session status is 'timeout'
|
||
- **Comprehensive Summary**: Includes score breakdown, question counts, accuracy percentage, pass/fail status
|
||
- **Dual Auth**: Works for authenticated users and guest sessions
|
||
|
||
#### Files Created/Modified:
|
||
- ✅ `controllers/quiz.controller.js` - Added reviewQuizSession function (230+ lines, total 1165+ lines)
|
||
- ✅ `routes/quiz.routes.js` - Added GET /review/:sessionId route
|
||
- ✅ `test-review-quiz.js` (700+ lines) - 16 comprehensive tests
|
||
|
||
#### Test Results:
|
||
✅ All 16/16 tests passing:
|
||
- Basic Functionality (2): completed quiz review (user), guest quiz review
|
||
- Validation (6): in-progress blocked, missing ID, invalid UUID, non-existent session, cross-user access, unauthenticated
|
||
- Structure Validation (3): session fields, summary fields, question fields
|
||
- Feedback Validation (5): result status marking, explanations shown, points tracking, time statistics, multiple choice option feedback
|
||
|
||
#### Key Features:
|
||
1. **Visual Feedback**: resultStatus ('correct'/'incorrect'/'unanswered') for UI styling
|
||
2. **Option-Level Feedback**: Multiple choice options show which is correct and which was selected
|
||
3. **Complete Explanations**: All questions include explanations for learning
|
||
4. **Time Analytics**: Per-question and aggregate time statistics
|
||
5. **Summary Statistics**: Complete breakdown of performance (score, accuracy, pass/fail)
|
||
6. **Authorization**: Own-session-only access with dual auth support
|
||
|
||
#### Reference:
|
||
See `SEQUELIZE_QUICK_REFERENCE.md` - Review Quiz Session
|
||
|
||
---
|
||
|
||
## User Dashboard & Analytics Phase
|
||
|
||
### Task 31: Get User Dashboard
|
||
**Priority**: High | **Status**: ✅ COMPLETED | **Actual Time**: 3 hours
|
||
|
||
#### Subtasks:
|
||
- [x] Create `routes/user.routes.js`
|
||
- [x] Create `controllers/user.controller.js`
|
||
- [x] Implement `getUserDashboard` function
|
||
- [x] Return user stats (total quizzes, accuracy, streak)
|
||
- [x] Return recent quiz sessions (last 10)
|
||
- [x] Return category-wise performance
|
||
- [x] Calculate overall accuracy
|
||
- [x] Include streak status (active/at-risk/inactive)
|
||
- [x] Add recent activity tracking (last 30 days)
|
||
- [x] Write tests (14/14 passing)
|
||
|
||
#### Implementation Details:
|
||
- **Files Created**:
|
||
- `controllers/user.controller.js` (252 lines)
|
||
- `routes/user.routes.js` (13 lines)
|
||
- `test-user-dashboard.js` (527 lines)
|
||
- **Files Modified**:
|
||
- `server.js` - Registered user routes at `/api/users`
|
||
- `controllers/quiz.controller.js` - Fixed streak tracking logic
|
||
|
||
#### Features Implemented:
|
||
- User information (username, email, role, profile image, member since)
|
||
- Statistics (total quizzes, pass rate, accuracy, questions answered, streaks)
|
||
- Recent 10 quiz sessions with category, scores, and time spent
|
||
- Category-wise performance (quizzes taken, pass rate, accuracy per category)
|
||
- Recent activity (daily quiz counts for last 30 days)
|
||
- Streak status calculation (active if quiz today, at-risk if last active yesterday, inactive otherwise)
|
||
|
||
#### Bug Fixes:
|
||
- Fixed field name mapping: `lastQuizDate` instead of `lastActiveDate`
|
||
- Removed non-existent `percentage` field, calculating from `score/totalPoints`
|
||
- Fixed authorization order: check user existence before authorization (proper 404 responses)
|
||
- Fixed streak logic: update `longestStreak` when resetting `currentStreak`
|
||
|
||
#### Test Results:
|
||
✅ All 14/14 tests passing:
|
||
- Dashboard data retrieval
|
||
- User info structure validation
|
||
- Stats calculations accuracy
|
||
- Recent sessions ordering
|
||
- Category performance calculations
|
||
- Authorization checks (cross-user, unauthenticated, invalid UUID, non-existent user)
|
||
- Streak status validation
|
||
|
||
#### API Endpoint:
|
||
```
|
||
GET /api/users/:userId/dashboard
|
||
Authorization: Bearer <JWT_TOKEN>
|
||
```
|
||
|
||
#### Response Structure:
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"user": {
|
||
"id": "uuid",
|
||
"username": "string",
|
||
"email": "string",
|
||
"role": "string",
|
||
"profileImage": "string|null",
|
||
"memberSince": "ISO8601"
|
||
},
|
||
"stats": {
|
||
"totalQuizzes": 10,
|
||
"quizzesPassed": 8,
|
||
"passRate": 80,
|
||
"totalQuestionsAnswered": 50,
|
||
"correctAnswers": 42,
|
||
"overallAccuracy": 84,
|
||
"currentStreak": 3,
|
||
"longestStreak": 5,
|
||
"streakStatus": "active|at-risk|inactive",
|
||
"lastActiveDate": "ISO8601|null"
|
||
},
|
||
"recentSessions": [
|
||
{
|
||
"id": "uuid",
|
||
"category": { "id": "uuid", "name": "JavaScript", "slug": "javascript", "icon": "📜", "color": "#f7df1e" },
|
||
"quizType": "practice",
|
||
"difficulty": "medium",
|
||
"status": "completed",
|
||
"score": { "earned": 45, "total": 50, "percentage": 90 },
|
||
"isPassed": true,
|
||
"questionsAnswered": 10,
|
||
"correctAnswers": 9,
|
||
"accuracy": 90,
|
||
"timeSpent": 300,
|
||
"completedAt": "ISO8601"
|
||
}
|
||
],
|
||
"categoryPerformance": [
|
||
{
|
||
"category": { "id": "uuid", "name": "JavaScript", "slug": "javascript", "icon": "📜", "color": "#f7df1e" },
|
||
"stats": {
|
||
"quizzesTaken": 5,
|
||
"quizzesPassed": 4,
|
||
"averageScore": 85,
|
||
"totalQuestions": 50,
|
||
"correctAnswers": 43,
|
||
"accuracy": 86,
|
||
"lastAttempt": "ISO8601"
|
||
}
|
||
}
|
||
],
|
||
"recentActivity": [
|
||
{ "date": "2025-11-11", "quizzesCompleted": 3 },
|
||
{ "date": "2025-11-10", "quizzesCompleted": 2 }
|
||
]
|
||
},
|
||
"message": "User dashboard retrieved successfully"
|
||
}
|
||
```
|
||
|
||
#### Reference:
|
||
See `interview_quiz_user_story.md` - User Story 4.1
|
||
|
||
#### Notes:
|
||
- Users can only access their own dashboard (authorization enforced)
|
||
- Streak calculation: active if quiz completed today, at-risk if last active yesterday, inactive otherwise
|
||
- Category performance uses SQL aggregation for efficiency
|
||
- Recent activity limited to last 30 days
|
||
|
||
---
|
||
|
||
### Task 32: Get Quiz History
|
||
**Priority**: Medium | **Status**: ✅ COMPLETED | **Estimated Time**: 2 hours | **Actual Time**: 3 hours
|
||
|
||
#### Subtasks:
|
||
- [x] Implement `getQuizHistory` function in `user.controller.js`
|
||
- [x] Pagination support (page, limit with max 50)
|
||
- [x] Filter by category UUID
|
||
- [x] Filter by status (completed/timeout/abandoned)
|
||
- [x] Filter by date range (startDate, endDate)
|
||
- [x] Sort by date or score (asc/desc)
|
||
- [x] Return formatted session summaries
|
||
- [x] Write comprehensive tests (18 test cases)
|
||
- [x] All tests passing (18/18)
|
||
|
||
#### Implementation Details:
|
||
|
||
**Controller**: `controllers/user.controller.js`
|
||
- `getQuizHistory()` function (233 lines)
|
||
- Validates userId UUID format
|
||
- Checks user existence (404 if not found)
|
||
- Authorization check (403 if accessing other user's history)
|
||
- Pagination with configurable page and limit
|
||
- Maximum 50 items per page enforced
|
||
- Category filtering with UUID validation
|
||
- Status filtering with enum validation
|
||
- Date range filtering with ISO 8601 format validation
|
||
- End date includes full day (23:59:59.999)
|
||
- Sorting by completedAt (date) or score
|
||
- Ascending or descending order
|
||
- Returns formatted sessions with:
|
||
- Category info (id, name, slug, icon, color)
|
||
- Score (earned, total, percentage)
|
||
- Questions (answered, total, correct, accuracy)
|
||
- Time (spent, allocated, percentage)
|
||
- Status and pass/fail indicator
|
||
- Timestamps (startedAt, completedAt)
|
||
- Pagination metadata (currentPage, totalPages, totalItems, itemsPerPage, hasNextPage, hasPreviousPage)
|
||
- Filter and sorting info in response
|
||
|
||
**Route**: `routes/user.routes.js`
|
||
- `GET /:userId/history` with verifyToken middleware
|
||
- Private access (users can only view own history)
|
||
|
||
**Tests**: `test-quiz-history.js` (552 lines)
|
||
- ✅ Test 1: Default pagination (page 1, limit 10)
|
||
- ✅ Test 2: Pagination structure validation
|
||
- ✅ Test 3: Session fields validation
|
||
- ✅ Test 4: Custom limit
|
||
- ✅ Test 5: Page 2 navigation
|
||
- ✅ Test 6: Category filter
|
||
- ✅ Test 7: Status filter
|
||
- ✅ Test 8: Sort by score descending
|
||
- ✅ Test 9: Sort by date ascending
|
||
- ✅ Test 10: Default sort (date descending)
|
||
- ✅ Test 11: Max limit enforcement (50)
|
||
- ✅ Test 12: Cross-user access blocked (403)
|
||
- ✅ Test 13: Unauthenticated blocked (401)
|
||
- ✅ Test 14: Invalid UUID format (400)
|
||
- ✅ Test 15: Non-existent user (404)
|
||
- ✅ Test 16: Invalid category ID (400)
|
||
- ✅ Test 17: Invalid date format (400)
|
||
- ✅ Test 18: Combined filters and sorting
|
||
|
||
**Test Results**: 18/18 tests passing ✅
|
||
|
||
#### API Endpoint:
|
||
```
|
||
GET /api/users/:userId/history
|
||
```
|
||
|
||
**Query Parameters:**
|
||
- `page` (integer, default: 1) - Page number
|
||
- `limit` (integer, default: 10, max: 50) - Items per page
|
||
- `category` (UUID) - Filter by category ID
|
||
- `status` (string) - Filter by status (completed/timeout/abandoned)
|
||
- `startDate` (ISO 8601) - Filter sessions completed on or after this date
|
||
- `endDate` (ISO 8601) - Filter sessions completed on or before this date (includes full day)
|
||
- `sortBy` (string, default: 'date') - Sort by 'date' or 'score'
|
||
- `sortOrder` (string, default: 'desc') - Sort order 'asc' or 'desc'
|
||
|
||
**Example Request:**
|
||
```
|
||
GET /api/users/b65d40c4-9971-48f3-b626-0520f9a9ac61/history?page=1&limit=5&category=d29f3a12-8e4c-4c8d-9f1a-2b3c4d5e6f7a&sortBy=score&sortOrder=desc
|
||
Authorization: Bearer <token>
|
||
```
|
||
|
||
**Example Response:**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"sessions": [
|
||
{
|
||
"id": "b6ea0e7d-73a5-4310-b480-e102804221de",
|
||
"category": {
|
||
"id": "d29f3a12-8e4c-4c8d-9f1a-2b3c4d5e6f7a",
|
||
"name": "JavaScript",
|
||
"slug": "javascript",
|
||
"icon": "fab fa-js",
|
||
"color": "#f7df1e"
|
||
},
|
||
"quizType": "practice",
|
||
"difficulty": "medium",
|
||
"status": "completed",
|
||
"score": {
|
||
"earned": 15,
|
||
"total": 20,
|
||
"percentage": 75
|
||
},
|
||
"isPassed": true,
|
||
"questions": {
|
||
"answered": 4,
|
||
"total": 4,
|
||
"correct": 3,
|
||
"accuracy": 75
|
||
},
|
||
"time": {
|
||
"spent": 45,
|
||
"allocated": 120,
|
||
"percentage": 37.5
|
||
},
|
||
"startedAt": "2025-11-12T10:15:00.000Z",
|
||
"completedAt": "2025-11-12T10:16:30.000Z"
|
||
}
|
||
],
|
||
"pagination": {
|
||
"currentPage": 1,
|
||
"totalPages": 2,
|
||
"totalItems": 8,
|
||
"itemsPerPage": 5,
|
||
"hasNextPage": true,
|
||
"hasPreviousPage": false
|
||
},
|
||
"filters": {
|
||
"category": "d29f3a12-8e4c-4c8d-9f1a-2b3c4d5e6f7a",
|
||
"status": null,
|
||
"dateRange": {
|
||
"startDate": null,
|
||
"endDate": null
|
||
}
|
||
},
|
||
"sorting": {
|
||
"sortBy": "score",
|
||
"sortOrder": "desc"
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
#### Bug Fixes During Testing:
|
||
1. Fixed field names for quiz submission:
|
||
- Changed `selectedOption` → `userAnswer`
|
||
- Changed `timeTaken` → `timeSpent`
|
||
- Used `quizSessionId` for submit endpoint
|
||
2. Fixed complete quiz endpoint field:
|
||
- Used `sessionId` (not `quizSessionId`) for complete endpoint
|
||
|
||
#### Files Modified:
|
||
- ✅ `controllers/user.controller.js` - Added getQuizHistory function (485 lines total)
|
||
- ✅ `routes/user.routes.js` - Added history route (27 lines total)
|
||
- ✅ `test-quiz-history.js` - Created comprehensive test suite (552 lines)
|
||
|
||
#### Notes:
|
||
- Users can only access their own quiz history (enforced by verifyToken and authorization check)
|
||
- Date filtering supports ISO 8601 format (e.g., "2025-11-12" or "2025-11-12T10:00:00Z")
|
||
- End date filter includes the entire day (00:00:00 to 23:59:59.999)
|
||
- Default sorting is by most recent first (date descending)
|
||
- Maximum 50 items per page to prevent performance issues
|
||
- Empty result sets return empty array with correct pagination metadata
|
||
- All validation errors return 400 with descriptive messages
|
||
- Authorization errors return 403, missing resources return 404
|
||
|
||
---
|
||
|
||
### Task 33: Update User Profile
|
||
**Priority**: Medium | **Status**: ✅ Completed | **Estimated Time**: 2 hours
|
||
|
||
#### Subtasks:
|
||
- [x] Implement `updateUserProfile` function
|
||
- [x] Allow username change (check uniqueness)
|
||
- [x] Allow profile_image upload (URL string, max 255 chars)
|
||
- [x] Password change (verify old password)
|
||
- [x] Email change (verify new email)
|
||
- [x] Write tests
|
||
|
||
#### API Endpoint:
|
||
```
|
||
PUT /api/users/:userId
|
||
```
|
||
|
||
#### Implementation Details:
|
||
- **Controller**: `controllers/user.controller.js` - Added `updateUserProfile` function (87 lines)
|
||
- **Route**: `routes/user.routes.js` - Added PUT /:userId with verifyToken middleware
|
||
- **Tests**: `test-update-profile.js` - 21 comprehensive test scenarios (all passing)
|
||
- **Authorization**: Users can only update their own profile (userId must match JWT token)
|
||
- **Features Implemented**:
|
||
- Username update: Alphanumeric only, 3-50 chars, uniqueness check
|
||
- Email update: Format validation, uniqueness check
|
||
- Password update: Requires currentPassword verification, min 6 chars, bcrypt hashing via model hook
|
||
- Profile image: URL string (max 255 chars), nullable
|
||
- **Validation**: UUID format, field requirements, format checks, uniqueness constraints
|
||
- **Response**: Updated user object (password excluded) + changedFields array
|
||
- **Error Handling**: 400 (validation), 401 (incorrect password/unauthenticated), 403 (unauthorized), 404 (not found), 409 (duplicate)
|
||
- **Bug Fix**: Removed manual password hashing from controller to prevent double-hashing (model beforeUpdate hook handles it)
|
||
|
||
#### Test Results:
|
||
```
|
||
✅ 21/21 tests passing
|
||
- Username update (with revert)
|
||
- Email update (with revert)
|
||
- Password update (with login verification and revert)
|
||
- Profile image update and removal
|
||
- Multiple fields at once
|
||
- Duplicate username/email rejection (409)
|
||
- Invalid email format (400)
|
||
- Short username/password rejection (400)
|
||
- Invalid username characters (400)
|
||
- Password change without current password (400)
|
||
- Incorrect current password (401)
|
||
- Empty update rejection (400)
|
||
- Cross-user update blocked (403)
|
||
- Unauthenticated blocked (401)
|
||
- Invalid UUID format (400)
|
||
- Non-existent user (404)
|
||
- Long profile image URL (400)
|
||
- Password excluded from response
|
||
```
|
||
|
||
#### Files Modified:
|
||
- `controllers/user.controller.js` (487 → 701 lines, +214)
|
||
- `routes/user.routes.js` (27 → 37 lines, +10)
|
||
- `test-update-profile.js` (NEW FILE, 592 lines)
|
||
|
||
---
|
||
|
||
## Bookmark Management Phase
|
||
|
||
### Task 34: Add/Remove Bookmark
|
||
**Priority**: Medium | **Status**: Not Started | **Estimated Time**: 2 hours
|
||
|
||
#### Subtasks:
|
||
- [ ] Implement `addBookmark` function
|
||
- [ ] Check if already bookmarked
|
||
- [ ] Create user_bookmarks record
|
||
- [ ] Implement `removeBookmark` function
|
||
- [ ] Delete user_bookmarks record
|
||
- [ ] Write tests
|
||
|
||
#### API Endpoints:
|
||
```
|
||
POST /api/users/:userId/bookmarks
|
||
DELETE /api/users/:userId/bookmarks/:questionId
|
||
```
|
||
|
||
#### Reference:
|
||
See `SEQUELIZE_QUICK_REFERENCE.md` - Bookmark Operations
|
||
|
||
---
|
||
|
||
### Task 35: Get User Bookmarks
|
||
**Priority**: Medium | **Status**: Not Started | **Estimated Time**: 1.5 hours
|
||
|
||
#### Subtasks:
|
||
- [ ] Implement `getUserBookmarks` function
|
||
- [ ] Include question details
|
||
- [ ] Include category info
|
||
- [ ] Sort by bookmarked_at
|
||
- [ ] Pagination
|
||
- [ ] Write tests
|
||
|
||
#### API Endpoint:
|
||
```
|
||
GET /api/users/:userId/bookmarks
|
||
```
|
||
|
||
---
|
||
|
||
## Admin Features Phase
|
||
|
||
### Task 36: Admin Statistics Dashboard
|
||
**Priority**: Medium | **Status**: Not Started | **Estimated Time**: 3 hours
|
||
|
||
#### Subtasks:
|
||
- [ ] Create `routes/admin.routes.js`
|
||
- [ ] Create `controllers/admin.controller.js`
|
||
- [ ] Implement `getSystemStatistics` function
|
||
- [ ] Count total users
|
||
- [ ] Count active users (last 7 days)
|
||
- [ ] Count total quiz sessions
|
||
- [ ] Get popular categories
|
||
- [ ] Calculate average score
|
||
- [ ] Get user growth data
|
||
- [ ] Add authorization (admin only)
|
||
- [ ] Write tests
|
||
|
||
#### API Endpoint:
|
||
```
|
||
GET /api/admin/statistics
|
||
```
|
||
|
||
#### Reference:
|
||
See `SEQUELIZE_QUICK_REFERENCE.md` - Admin Operations
|
||
|
||
---
|
||
|
||
### Task 37: Guest Settings Management
|
||
**Priority**: Medium | **Status**: Not Started | **Estimated Time**: 2 hours
|
||
|
||
#### Subtasks:
|
||
- [ ] Implement `getGuestSettings` function
|
||
- [ ] Implement `updateGuestSettings` function
|
||
- [ ] Validate settings (max quizzes, expiry hours)
|
||
- [ ] Update public categories list
|
||
- [ ] Update feature restrictions
|
||
- [ ] Write tests
|
||
|
||
#### API Endpoints:
|
||
```
|
||
GET /api/admin/guest-settings
|
||
PUT /api/admin/guest-settings
|
||
```
|
||
|
||
---
|
||
|
||
### Task 38: User Management (Admin)
|
||
**Priority**: Low | **Status**: Not Started | **Estimated Time**: 3 hours
|
||
|
||
#### Subtasks:
|
||
- [ ] Implement `getAllUsers` function (paginated)
|
||
- [ ] Implement `getUserById` function
|
||
- [ ] Implement `updateUserRole` function
|
||
- [ ] Implement `deactivateUser` function
|
||
- [ ] Write tests
|
||
|
||
#### API Endpoints:
|
||
```
|
||
GET /api/admin/users
|
||
GET /api/admin/users/:userId
|
||
PUT /api/admin/users/:userId/role
|
||
DELETE /api/admin/users/:userId
|
||
```
|
||
|
||
---
|
||
|
||
### Task 39: Guest Analytics
|
||
**Priority**: Low | **Status**: Not Started | **Estimated Time**: 2 hours
|
||
|
||
#### Subtasks:
|
||
- [ ] Implement `getGuestAnalytics` function
|
||
- [ ] Count total guest sessions
|
||
- [ ] Calculate guest-to-user conversion rate
|
||
- [ ] Average quizzes taken before conversion
|
||
- [ ] Guest bounce rate
|
||
- [ ] Write tests
|
||
|
||
#### API Endpoint:
|
||
```
|
||
GET /api/admin/guest-analytics
|
||
```
|
||
|
||
---
|
||
|
||
## Testing & Optimization Phase
|
||
|
||
### Task 40: Unit Tests
|
||
**Priority**: High | **Status**: Not Started | **Estimated Time**: 5 hours
|
||
|
||
#### Subtasks:
|
||
- [ ] Setup Jest testing framework
|
||
- [ ] Write tests for auth controllers
|
||
- [ ] Write tests for quiz controllers
|
||
- [ ] Write tests for user controllers
|
||
- [ ] Write tests for admin controllers
|
||
- [ ] Mock database calls
|
||
- [ ] Achieve 80%+ code coverage
|
||
|
||
---
|
||
|
||
### Task 41: Integration Tests
|
||
**Priority**: High | **Status**: Not Started | **Estimated Time**: 4 hours
|
||
|
||
#### Subtasks:
|
||
- [ ] Setup Supertest for API testing
|
||
- [ ] Test complete registration flow
|
||
- [ ] Test complete quiz flow (start -> answer -> complete)
|
||
- [ ] Test guest to user conversion
|
||
- [ ] Test authorization scenarios
|
||
- [ ] Test error scenarios
|
||
|
||
---
|
||
|
||
### Task 42: API Documentation
|
||
**Priority**: Medium | **Status**: Not Started | **Estimated Time**: 3 hours
|
||
|
||
#### Subtasks:
|
||
- [ ] Install Swagger/OpenAPI
|
||
- [ ] Document all endpoints
|
||
- [ ] Add request/response examples
|
||
- [ ] Add authentication details
|
||
- [ ] Generate interactive API docs
|
||
- [ ] Host at `/api-docs`
|
||
|
||
---
|
||
|
||
### Task 43: Error Handling & Logging
|
||
**Priority**: High | **Status**: Not Started | **Estimated Time**: 2 hours
|
||
|
||
#### Subtasks:
|
||
- [ ] Create centralized error handler middleware
|
||
- [ ] Handle Sequelize errors gracefully
|
||
- [ ] Setup logging with Winston/Morgan
|
||
- [ ] Log all requests (development)
|
||
- [ ] Log errors with stack traces
|
||
- [ ] Setup log rotation
|
||
|
||
---
|
||
|
||
### Task 44: Rate Limiting & Security
|
||
**Priority**: High | **Status**: Not Started | **Estimated Time**: 2 hours
|
||
|
||
#### Subtasks:
|
||
- [ ] Install express-rate-limit
|
||
- [ ] Add rate limiting to auth endpoints
|
||
- [ ] Add rate limiting to API endpoints
|
||
- [ ] Setup Helmet for security headers
|
||
- [ ] Add input sanitization
|
||
- [ ] Add CORS configuration
|
||
- [ ] Test security measures
|
||
|
||
---
|
||
|
||
### Task 45: Database Optimization
|
||
**Priority**: Medium | **Status**: Not Started | **Estimated Time**: 3 hours
|
||
|
||
#### Subtasks:
|
||
- [ ] Review and optimize all queries
|
||
- [ ] Add missing indexes
|
||
- [ ] Implement query result caching (Redis)
|
||
- [ ] Use eager loading to avoid N+1 queries
|
||
- [ ] Optimize full-text search queries
|
||
- [ ] Run EXPLAIN on complex queries
|
||
- [ ] Benchmark query performance
|
||
|
||
---
|
||
|
||
### Task 46: Performance Testing
|
||
**Priority**: Medium | **Status**: Not Started | **Estimated Time**: 2 hours
|
||
|
||
#### Subtasks:
|
||
- [ ] Setup load testing tool (Apache JMeter or Artillery)
|
||
- [ ] Test concurrent user scenarios
|
||
- [ ] Test database under load
|
||
- [ ] Monitor response times
|
||
- [ ] Identify bottlenecks
|
||
- [ ] Optimize as needed
|
||
|
||
---
|
||
|
||
## Deployment Preparation Phase
|
||
|
||
### Task 47: Docker Configuration
|
||
**Priority**: Low | **Status**: Not Started | **Estimated Time**: 2 hours
|
||
|
||
#### Subtasks:
|
||
- [ ] Create `Dockerfile` for backend
|
||
- [ ] Create `docker-compose.yml` with MySQL service
|
||
- [ ] Configure environment variables for Docker
|
||
- [ ] Test Docker build and run
|
||
- [ ] Create docker-compose for development
|
||
|
||
---
|
||
|
||
### Task 48: CI/CD Setup
|
||
**Priority**: Low | **Status**: Not Started | **Estimated Time**: 3 hours
|
||
|
||
#### Subtasks:
|
||
- [ ] Create GitHub Actions workflow
|
||
- [ ] Setup automated testing on PR
|
||
- [ ] Setup automated deployment (staging)
|
||
- [ ] Add environment secrets
|
||
- [ ] Test CI/CD pipeline
|
||
|
||
---
|
||
|
||
### Task 49: Production Configuration
|
||
**Priority**: Low | **Status**: Not Started | **Estimated Time**: 2 hours
|
||
|
||
#### Subtasks:
|
||
- [ ] Create production environment config
|
||
- [ ] Setup connection pooling for production
|
||
- [ ] Configure SSL for database connection
|
||
- [ ] Setup monitoring (New Relic/DataDog)
|
||
- [ ] Configure backup strategy
|
||
- [ ] Create deployment documentation
|
||
|
||
---
|
||
|
||
### Task 50: Final Testing & Documentation
|
||
**Priority**: High | **Status**: Not Started | **Estimated Time**: 4 hours
|
||
|
||
#### Subtasks:
|
||
- [ ] End-to-end testing of all features
|
||
- [ ] Create API usage examples
|
||
- [ ] Write README.md with setup instructions
|
||
- [ ] Document environment variables
|
||
- [ ] Create troubleshooting guide
|
||
- [ ] Code review and refactoring
|
||
- [ ] Performance optimization
|
||
- [ ] Security audit
|
||
|
||
---
|
||
|
||
## Task Summary
|
||
|
||
**Total Tasks**: 50
|
||
**Estimated Total Time**: 100-120 hours (2-3 months part-time)
|
||
|
||
### Priority Breakdown:
|
||
- **High Priority**: 25 tasks (Core functionality)
|
||
- **Medium Priority**: 18 tasks (Important features)
|
||
- **Low Priority**: 7 tasks (Nice to have)
|
||
|
||
### Phase Breakdown:
|
||
1. **Project Setup**: Tasks 1-3 (4-6 hours)
|
||
2. **Database Schema**: Tasks 4-10 (15-20 hours)
|
||
3. **Authentication**: Tasks 11-14 (8-10 hours)
|
||
4. **Guest Management**: Tasks 15-17 (6-8 hours)
|
||
5. **Category Management**: Tasks 18-20 (6-8 hours)
|
||
6. **Question Management**: Tasks 21-25 (10-12 hours)
|
||
7. **Quiz Sessions**: Tasks 26-30 (11-14 hours)
|
||
8. **User Dashboard**: Tasks 31-33 (7-9 hours)
|
||
9. **Bookmarks**: Tasks 34-35 (3-4 hours)
|
||
10. **Admin Features**: Tasks 36-39 (10-12 hours)
|
||
11. **Testing & Optimization**: Tasks 40-46 (21-25 hours)
|
||
12. **Deployment**: Tasks 47-50 (11-13 hours)
|
||
|
||
---
|
||
|
||
## Getting Started
|
||
|
||
### Recommended Order:
|
||
1. Start with **Task 1** (Project Setup)
|
||
2. Complete **Tasks 2-3** (Database & Environment)
|
||
3. Work through **Tasks 4-10** (Database Schema) sequentially
|
||
4. Then proceed with feature development in order
|
||
|
||
### Daily Workflow:
|
||
1. Pick a task based on priority
|
||
2. Read the task requirements and references
|
||
3. Implement the feature
|
||
4. Write tests
|
||
5. Update task status
|
||
6. Commit with descriptive message
|
||
7. Move to next task
|
||
|
||
---
|
||
|
||
**Good luck with the development! 🚀**
|