# ClassMaker - Project Instructions

## Overview
ClassMaker is a quiz/test application inspired by ClassMarker. It supports multiple question types, timed quizzes, and role-based access (admin/user). It also provides a lightweight user path (`/user`) that requires only a name and email to start quizzing.

## Tech Stack
- **Framework**: Next.js 16 (App Router) with React 19
- **Language**: TypeScript (strict mode)
- **Styling**: Tailwind CSS v4 (inline `@theme` config in globals.css, no tailwind.config file)
- **Auth**: NextAuth v4 with Credentials provider and JWT strategy
- **Storage**: MongoDB (native `mongodb` driver) — connection string via `MONGO_URL_LOCAL` env var
- **Containerization**: Docker with multi-stage build, managed via Makefile

## Commands

### Development
- `npm run dev` — Start dev server
- `npm run build` — Production build (use to verify changes compile)
- `npm run lint` — Run ESLint
- No test framework is configured

### Docker (via Makefile)
- `make up` — Build Docker image and start container (port 4009)
- `make build` — Build Docker image only
- `make start` — Start container only (image must exist)
- `make stop` — Stop and remove container
- `make restart` — Stop then start (same image)
- `make rebuild` — Stop, rebuild image, then start
- `make logs` — Tail container logs

## Project Structure
```
src/
├── app/
│   ├── admin/           # Admin dashboard and template editor (dark theme)
│   ├── advanced/        # Advanced landing page (Take a Quiz / Admin Panel links)
│   ├── api/             # API routes (templates, user attempts, settings, NextAuth)
│   │   ├── admin/       #   GET /api/admin/quiz-users — all users & attempts (admin only)
│   │   ├── settings/    #   GET/PUT /api/settings — quiz theme
│   │   ├── templates/   #   CRUD /api/templates, /api/templates/[id]
│   │   └── user/        #   POST /api/user/identify, CRUD /api/user/attempts
│   ├── login/           # Credentialed user login page
│   ├── quiz/            # Credentialed quiz: setup, play, and results
│   ├── user/            # Lightweight user quiz: setup, play, and results
│   │   ├── setup/       #   Mode selection (testing/learning), creates attempt
│   │   ├── play/        #   Quiz play with progress tracking
│   │   └── results/     #   Score, per-question review, past attempts
│   ├── layout.tsx       # Root layout (Geist fonts, SessionProvider)
│   ├── page.tsx         # Home page — name + email entry form (user quiz path)
│   ├── providers.tsx    # NextAuth SessionProvider wrapper
│   └── globals.css      # Global styles + Tailwind imports
├── components/          # Shared components (QuestionRenderer, ProgressBar, Timer)
├── lib/
│   ├── auth.ts          # NextAuth configuration
│   ├── mongodb.ts       # MongoDB client singleton (getDb)
│   ├── quiz-store.ts    # Template CRUD operations (MongoDB)
│   └── user-store.ts    # Quiz user and attempt CRUD operations (MongoDB)
├── types/
│   └── quiz.ts          # All TypeScript interfaces and types
└── middleware.ts         # Route protection (/admin/* requires admin, /quiz/* requires auth)
scripts/
└── seed-mongo.ts        # Migration script: loads data/ JSON files into MongoDB
data/                    # Legacy JSON files (used only by seed script for migration)
```

## Key Conventions

### Styling
- Quiz user-facing pages use a **white background** with ClassMarker-inspired design
- Primary accent color: `#2b579a` (ClassMarker blue)
- Admin pages use a dark theme (gray-900)
- Use Tailwind utility classes — no CSS modules or styled-components
- Fade transitions between quiz questions use the `.question-fade-in` class from globals.css

### Data
- All data is stored in MongoDB (collections: `users`, `quizUsers`, `templates`, `attempts`, `settings`)
- `mongodb.ts` exports `getDb()` — a cached singleton connection (module-level cache)
- Settings use a singleton document pattern with `_key: "app"` for upsert
- Quiz state (config, answers, questions) is passed between pages via `sessionStorage`
- Migration: run `npx tsx scripts/seed-mongo.ts` to load legacy `data/` JSON files into MongoDB

### Question Types
Five supported types defined in `QuestionType`: `true-false`, `single-answer`, `multiple-answer`, `dropdown`, `multiple-dropdown`

#### Multiple Dropdown
- A single question contains multiple sub-questions (`subQuestions[]`), each with its own set of options and a `correctOptionId`
- Each `SubQuestion` has: `id`, `text`, `options: QuizOption[]`, `correctOptionId`
- Answers are encoded as `subQuestionId:optionId` strings in the `selectedAnswers` array
- Scoring: all sub-questions must be correct for the question to count as correct

### Two Quiz Paths

#### 1. Credentialed Path (`/advanced` → `/login` → `/quiz/*`)
- Requires NextAuth login (username/password)
- User selects template, question count, timer, and mode
- Available themes: ClassMarker (default) and Modern

#### 2. Lightweight User Path (`/` → `/user/*`)
- No credentials — just name + email
- Always 10 random questions (pooled from all templates), no timer
- User chooses between testing and learning mode
- Progress is tracked even for incomplete quizzes (debounced saves + `sendBeacon` on unload)
- Attempts are stored server-side with question snapshots and partial answers
- Admin can view all users and their attempt details in the admin dashboard

### Authentication
- **Provider**: NextAuth v4 with a single `CredentialsProvider` (username/password) and JWT session strategy
- **Two roles**: `admin` and `user` — stored in MongoDB `users` collection with bcrypt-hashed passwords
- **Default credentials**: `admin`/`admin` (admin role), `user`/`user` (user role)

#### Login Pages
- `/login` — User login (light theme). On success redirects to `/quiz`
- `/admin/login` — Admin login (dark theme). On success redirects to `/admin`
- Both use `signIn("credentials", { redirect: false })` and handle errors client-side

#### Route Protection (middleware.ts)
- Matcher: `/admin/:path*` and `/quiz/:path*`
- `/admin/login` is always accessible (bypasses both auth and role checks)
- All other `/admin/*` routes require an authenticated user with `role: "admin"` — non-admins are redirected to `/admin/login`
- All `/quiz/*` routes require any authenticated user — unauthenticated users are redirected to `/login`
- `/user/*` routes are **not** protected by middleware — they use their own identification via `/api/user/identify`

#### JWT & Session
- The `jwt` callback in `lib/auth.ts` copies `user.role` into the JWT token on sign-in
- The `session` callback exposes `role` and `id` on `session.user`
- Access role in components via `useSession()`: `(session?.user as { role?: string })?.role`

#### Sign Out
- User pages: `signOut({ callbackUrl: "/login" })`
- Admin pages: `signOut({ callbackUrl: "/admin/login" })`

### Admin Dashboard
- Template management (CRUD)
- Quiz theme switcher (ClassMarker / Modern)
- Quiz Users & Progress section: expandable list of all lightweight quiz users with their attempts, including per-question detail view with answers and correctness

### Path Aliases
- `@/*` maps to `./src/*` (configured in tsconfig.json)

## Docker
- **Dockerfile**: Multi-stage build (deps → build → runner) using `node:20-alpine`
- **Output mode**: `standalone` (configured in `next.config.ts`)
- **Port**: 4009
- **Runs as**: Non-root `nextjs` user (uid 1001)
- **Environment variables**: `MONGO_URL_LOCAL`, `NEXTAUTH_SECRET`, and `NEXTAUTH_URL` are set in the Makefile

## Important Notes
- Always use `"use client"` directive for components with hooks or interactivity
- NextAuth secret should be set via `NEXTAUTH_SECRET` env var in production
- `MONGO_URL_LOCAL` env var must be set with the MongoDB connection string
- Template IDs are generated from the title slug; non-Latin characters fall back to `template-{timestamp}`
