feat(classroom): add active sessions API endpoint
Provides API for useActiveSessionsInClassroom hook to fetch which students in a classroom are currently practicing. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
import { and, eq, inArray, isNull } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { db, schema } from '@/db'
|
||||
import { getClassroomPresence, getTeacherClassroom } from '@/lib/classroom'
|
||||
import { getViewerId } from '@/lib/viewer'
|
||||
|
||||
/**
|
||||
* Get or create user record for a viewerId (guestId)
|
||||
*/
|
||||
async function getOrCreateUser(viewerId: string) {
|
||||
let user = await db.query.users.findFirst({
|
||||
where: eq(schema.users.guestId, viewerId),
|
||||
})
|
||||
|
||||
if (!user) {
|
||||
const [newUser] = await db.insert(schema.users).values({ guestId: viewerId }).returning()
|
||||
user = newUser
|
||||
}
|
||||
|
||||
return user
|
||||
}
|
||||
|
||||
interface RouteParams {
|
||||
params: Promise<{ classroomId: string }>
|
||||
}
|
||||
|
||||
/**
|
||||
* Active session information returned by this endpoint
|
||||
*/
|
||||
interface ActiveSessionInfo {
|
||||
/** Session plan ID (for observation) */
|
||||
sessionId: string
|
||||
/** Player ID */
|
||||
playerId: string
|
||||
/** When the session started */
|
||||
startedAt: Date
|
||||
/** Current part index */
|
||||
currentPartIndex: number
|
||||
/** Current slot index within the part */
|
||||
currentSlotIndex: number
|
||||
/** Total parts in session */
|
||||
totalParts: number
|
||||
/** Total problems in session (sum of all slots) */
|
||||
totalProblems: number
|
||||
/** Number of completed problems */
|
||||
completedProblems: number
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/classrooms/[classroomId]/presence/active-sessions
|
||||
* Get active practice sessions for students currently present in the classroom
|
||||
*
|
||||
* Returns: { sessions: ActiveSessionInfo[] }
|
||||
*
|
||||
* This endpoint allows teachers to see which students are actively practicing
|
||||
* so they can observe their sessions in real-time.
|
||||
*/
|
||||
export async function GET(req: NextRequest, { params }: RouteParams) {
|
||||
try {
|
||||
const { classroomId } = await params
|
||||
const viewerId = await getViewerId()
|
||||
const user = await getOrCreateUser(viewerId)
|
||||
|
||||
// Verify user is the teacher of this classroom
|
||||
const classroom = await getTeacherClassroom(user.id)
|
||||
if (!classroom || classroom.id !== classroomId) {
|
||||
return NextResponse.json({ error: 'Not authorized' }, { status: 403 })
|
||||
}
|
||||
|
||||
// Get all students currently present in the classroom
|
||||
const presences = await getClassroomPresence(classroomId)
|
||||
// Filter out presences where player was deleted (undefined)
|
||||
const playerIds = presences.filter((p) => p.player !== undefined).map((p) => p.player!.id)
|
||||
|
||||
if (playerIds.length === 0) {
|
||||
return NextResponse.json({ sessions: [] })
|
||||
}
|
||||
|
||||
// Find active sessions for these players
|
||||
// Active = status is 'in_progress', startedAt is set, completedAt is null
|
||||
const activeSessions = await db.query.sessionPlans.findMany({
|
||||
where: and(
|
||||
inArray(schema.sessionPlans.playerId, playerIds),
|
||||
eq(schema.sessionPlans.status, 'in_progress'),
|
||||
isNull(schema.sessionPlans.completedAt)
|
||||
),
|
||||
})
|
||||
|
||||
// Map to ActiveSessionInfo
|
||||
const sessions: ActiveSessionInfo[] = activeSessions
|
||||
.filter((session) => session.startedAt)
|
||||
.map((session) => {
|
||||
// Calculate total and completed problems
|
||||
const parts = session.parts
|
||||
const totalProblems = parts.reduce((sum, part) => sum + part.slots.length, 0)
|
||||
let completedProblems = 0
|
||||
for (let i = 0; i < session.currentPartIndex; i++) {
|
||||
completedProblems += parts[i].slots.length
|
||||
}
|
||||
completedProblems += session.currentSlotIndex
|
||||
|
||||
return {
|
||||
sessionId: session.id,
|
||||
playerId: session.playerId,
|
||||
startedAt: session.startedAt as Date,
|
||||
currentPartIndex: session.currentPartIndex,
|
||||
currentSlotIndex: session.currentSlotIndex,
|
||||
totalParts: parts.length,
|
||||
totalProblems,
|
||||
completedProblems,
|
||||
}
|
||||
})
|
||||
|
||||
return NextResponse.json({ sessions })
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch active sessions:', error)
|
||||
return NextResponse.json({ error: 'Failed to fetch active sessions' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user