// ═══════════════════════════════════════════════════════════════════════════ // POLL API - Create, vote, and manage polls // ═══════════════════════════════════════════════════════════════════════════ // POST /api/poll - Create poll, vote, or end poll // GET /api/poll?sessionId=xxx - Get all polls for a session // ═══════════════════════════════════════════════════════════════════════════ import { NextRequest, NextResponse } from 'next/server'; import { sessionManager } from '@/lib/session-manager'; export { OPTIONS } from '@/lib/cors'; export const runtime = 'nodejs'; export const dynamic = 'force-dynamic'; // ═══════════════════════════════════════════════════════════════════════════ // GET POLLS // ═══════════════════════════════════════════════════════════════════════════ // GET /api/poll?sessionId=xxx // Returns all polls for a session (active and ended) // ═══════════════════════════════════════════════════════════════════════════ export async function GET(request: NextRequest) { const { searchParams } = new URL(request.url); const sessionId = searchParams.get('sessionId'); if (!sessionId) { return NextResponse.json({ success: false, error: 'Missing required parameter: sessionId' }, { status: 400 }); } const session = sessionManager.getSession(sessionId); if (!session) { return NextResponse.json({ success: false, error: 'Session not found' }, { status: 404 }); } const polls = sessionManager.getPolls(sessionId); const activePoll = sessionManager.getActivePoll(sessionId); return NextResponse.json({ success: true, data: { polls, activePoll, totalPolls: polls.length } }); } // ═══════════════════════════════════════════════════════════════════════════ // POLL ACTIONS // ═══════════════════════════════════════════════════════════════════════════ export async function POST(request: NextRequest) { try { const body = await request.json(); const { action, sessionId } = body; // Validate sessionId if (!sessionId) { return NextResponse.json({ success: false, error: 'Missing required field: sessionId' }, { status: 400 }); } // Check session exists const session = sessionManager.getSession(sessionId); if (!session) { return NextResponse.json({ success: false, error: 'Session not found' }, { status: 404 }); } switch (action) { case 'create': return handleCreate(sessionId, body); case 'vote': return handleVote(sessionId, body); case 'end': return handleEnd(sessionId, body); case 'setCorrectAnswers': return handleSetCorrectAnswers(sessionId, body); default: return NextResponse.json({ success: false, error: `Invalid action: ${action}. Valid actions: create, vote, end, setCorrectAnswers` }, { status: 400 }); } } catch (error) { return NextResponse.json({ success: false, error: 'Invalid JSON body' }, { status: 400 }); } } // ═══════════════════════════════════════════════════════════════════════════ // CREATE POLL // ═══════════════════════════════════════════════════════════════════════════ // POST /api/poll // Body: { // action: 'create', // sessionId: string, // Required: Session ID // question: string, // Required: Poll question // options: string[], // Required: Array of option texts // type?: 'single' | 'multiple', // Optional: Vote type (default: 'single') // correctAnswer?: string, // Optional: Correct answer (for quizzes) // duration?: number // Optional: Duration in seconds (default: 60) // } // ═══════════════════════════════════════════════════════════════════════════ async function handleCreate(sessionId: string, body: any) { const { question, options, type, correctAnswer, duration } = body; // Validate required fields if (!question || typeof question !== 'string') { return NextResponse.json({ success: false, error: 'Missing required field: question (string)' }, { status: 400 }); } if (!options || !Array.isArray(options) || options.length < 2) { return NextResponse.json({ success: false, error: 'Missing required field: options (array with at least 2 items)' }, { status: 400 }); } // Auto-end any existing active poll before creating a new one const activePoll = sessionManager.getActivePoll(sessionId); if (activePoll) { await sessionManager.endPoll(sessionId, activePoll.id); } // Create poll const poll = await sessionManager.createPoll( sessionId, question, options, type || 'single', correctAnswer, duration || 0 // 0 = no auto-end timer ); return NextResponse.json({ success: true, data: poll, message: 'Poll created successfully' }); } // ═══════════════════════════════════════════════════════════════════════════ // VOTE ON POLL // ═══════════════════════════════════════════════════════════════════════════ // POST /api/poll // Body: { // action: 'vote', // sessionId: string, // Required: Session ID // pollId: string, // Required: Poll ID // option: string, // Required: Selected option text // studentId: string // Required: Student's unique ID // } // ═══════════════════════════════════════════════════════════════════════════ async function handleVote(sessionId: string, body: any) { const { pollId, option, studentId } = body; // Validate required fields if (!pollId) { return NextResponse.json({ success: false, error: 'Missing required field: pollId' }, { status: 400 }); } if (!option) { return NextResponse.json({ success: false, error: 'Missing required field: option' }, { status: 400 }); } if (!studentId) { return NextResponse.json({ success: false, error: 'Missing required field: studentId' }, { status: 400 }); } // Vote const poll = await sessionManager.votePoll(sessionId, pollId, option, studentId); if (!poll) { return NextResponse.json({ success: false, error: 'Poll not found or inactive' }, { status: 404 }); } // Check if student already voted if (!poll.votedStudents.includes(studentId)) { // Vote was recorded } return NextResponse.json({ success: true, data: { pollId: poll.id, results: poll.results, totalVotes: poll.totalVotes, voted: poll.votedStudents.includes(studentId) }, message: 'Vote recorded successfully' }); } // ═══════════════════════════════════════════════════════════════════════════ // END POLL // ═══════════════════════════════════════════════════════════════════════════ // POST /api/poll // Body: { // action: 'end', // sessionId: string, // Required: Session ID // pollId: string // Required: Poll ID to end // } // ═══════════════════════════════════════════════════════════════════════════ async function handleEnd(sessionId: string, body: any) { const { pollId } = body; if (!pollId) { return NextResponse.json({ success: false, error: 'Missing required field: pollId' }, { status: 400 }); } await sessionManager.endPoll(sessionId, pollId); return NextResponse.json({ success: true, message: 'Poll ended successfully' }); } // ═══════════════════════════════════════════════════════════════════════════ // SET CORRECT ANSWERS (Teacher marks correct after poll ends) // ═══════════════════════════════════════════════════════════════════════════ // POST /api/poll // Body: { action: 'setCorrectAnswers', sessionId, pollId, correctOptionIds: string[], correctAnswer?: string } // ═══════════════════════════════════════════════════════════════════════════ async function handleSetCorrectAnswers(sessionId: string, body: any) { const { pollId, correctOptionIds, correctAnswer } = body; if (!pollId) { return NextResponse.json({ success: false, error: 'Missing pollId' }, { status: 400 }); } const ids: string[] = correctOptionIds || (correctAnswer ? [correctAnswer] : []); // Broadcast to all students so they see the correct answer highlighted await sessionManager.broadcast(sessionId, 'correct_answer', { pollId, correctOptionIds: ids, correctAnswer: correctAnswer || ids[0] || null, timestamp: new Date().toISOString(), }); return NextResponse.json({ success: true, message: 'Correct answers broadcast to students' }); }