78 lines
2.3 KiB
TypeScript
78 lines
2.3 KiB
TypeScript
import { NextRequest, NextResponse } from "next/server"
|
|
import { prisma } from "@/lib/db"
|
|
import { initCurriculum, getLecturaStages, getNumerosStages } from "@/curriculum/index"
|
|
import { getCurrentStage, getNextExercise } from "@/lib/curriculum/engine"
|
|
|
|
export async function GET(request: NextRequest) {
|
|
const childId = request.nextUrl.searchParams.get("childId")
|
|
const topic = (request.nextUrl.searchParams.get("topic") || "lectura") as "lectura" | "numeros"
|
|
if (!childId) {
|
|
return NextResponse.json({ error: "childId required" }, { status: 400 })
|
|
}
|
|
|
|
await initCurriculum()
|
|
|
|
const stages = topic === "lectura" ? getLecturaStages() : getNumerosStages()
|
|
const stageKeys = Object.keys(stages).map(Number).sort((a, b) => a - b)
|
|
|
|
const progress = prisma.curriculumProgress.findMany({
|
|
where: { childId, topic },
|
|
orderBy: { stage: "asc" },
|
|
})
|
|
|
|
const allAttempts = prisma.skillAttempt.findMany({
|
|
where: {
|
|
exerciseId: { not: null },
|
|
stage: { not: null },
|
|
sessionLog: { childId },
|
|
},
|
|
select: { exerciseId: true, correct: true },
|
|
})
|
|
|
|
const [progressRows, attempts] = await Promise.all([progress, allAttempts])
|
|
|
|
const mastered = new Set(
|
|
attempts.filter((a) => a.correct).map((a) => a.exerciseId),
|
|
)
|
|
|
|
const stagesInfo = stageKeys.map((sk) => {
|
|
const s = stages[sk]
|
|
const total = s?.exercises?.length || 0
|
|
const completed = s?.exercises?.filter((e) => mastered.has(e.id)).length || 0
|
|
const prog = progressRows.find((p) => p.stage === sk)
|
|
return {
|
|
stage: sk,
|
|
name: s?.name || `Etapa ${sk}`,
|
|
total,
|
|
completed,
|
|
unlocked: !!prog,
|
|
completedAt: prog?.completedAt || null,
|
|
percent: total > 0 ? Math.round((completed / total) * 100) : 0,
|
|
}
|
|
})
|
|
|
|
const errorTypes = await prisma.skillAttempt.groupBy({
|
|
by: ["errorType"],
|
|
where: {
|
|
errorType: { not: null },
|
|
sessionLog: { childId },
|
|
},
|
|
_count: { errorType: true },
|
|
})
|
|
|
|
const errorPatterns = errorTypes.map((e) => ({
|
|
errorType: e.errorType,
|
|
count: e._count.errorType,
|
|
}))
|
|
|
|
const activeProgress = progressRows.find((p) => !p.completedAt)
|
|
const currentStage = activeProgress?.stage || 1
|
|
|
|
return NextResponse.json({
|
|
currentStage,
|
|
stages: stagesInfo,
|
|
errorPatterns,
|
|
hasActiveSession: true,
|
|
})
|
|
}
|