import { NextRequest, NextResponse } from 'next/server'; import { getServerSession } from 'next-auth'; import { authOptions } from '@/lib/auth'; import { prisma } from '@/lib/prisma'; export async function GET(request: NextRequest) { try { const session = await getServerSession(authOptions); if (!session || session.user.role !== 'ADMIN') { return NextResponse.json( { message: 'No tienes permisos para acceder a los reportes' }, { status: 403 } ); } const { searchParams } = new URL(request.url); const reportType = searchParams.get('type'); const periodId = searchParams.get('periodId'); const startDate = searchParams.get('startDate'); const endDate = searchParams.get('endDate'); switch (reportType) { case 'overview': return await getOverviewReport(); case 'students': return await getStudentsReport(periodId); case 'teachers': return await getTeachersReport(periodId); case 'attendance': return await getAttendanceReport(periodId, startDate, endDate); case 'enrollments': return await getEnrollmentsReport(periodId); case 'classes': return await getClassesReport(periodId); default: return await getOverviewReport(); } } catch (error) { console.error('Error generating report:', error); return NextResponse.json( { message: 'Error interno del servidor' }, { status: 500 } ); } } // Reporte general del sistema async function getOverviewReport() { try { const [totalUsers, totalStudents, totalTeachers, totalClasses, totalSections, activePeriods] = await Promise.all([ prisma.user.count(), prisma.student.count({ where: { isActive: true } }), prisma.teacher.count({ where: { isActive: true } }), prisma.class.count({ where: { isActive: true } }), prisma.section.count({ where: { isActive: true } }), prisma.period.count({ where: { isActive: true } }) ]); const usersByRole = await prisma.user.groupBy({ by: ['role'], _count: { id: true } }); const recentEnrollments = await prisma.studentEnrollment.count({ where: { createdAt: { gte: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000) // Últimos 30 días } } }); const recentAttendance = await prisma.attendance.count({ where: { createdAt: { gte: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000) // Últimos 7 días } } }); return NextResponse.json({ overview: { totalUsers, totalStudents, totalTeachers, totalClasses, totalSections, activePeriods, recentEnrollments, recentAttendance }, usersByRole: usersByRole.map(item => ({ role: item.role, count: item._count.id })) }); } catch (error) { console.error('Error in overview report:', error); throw error; } } // Reporte de estudiantes async function getStudentsReport(periodId: string | null) { try { const whereClause = periodId ? { enrollments: { some: { section: { class: { periodId: periodId } } } } } : {}; const students = await prisma.student.findMany({ where: { isActive: true, ...whereClause }, include: { enrollments: { where: { isActive: true, ...(periodId ? { section: { class: { periodId: periodId } } } : {}) }, include: { section: { include: { class: { include: { period: true } } } } } }, attendances: { where: { ...(periodId ? { section: { class: { periodId: periodId } } } : {}) } } }, orderBy: { lastName: 'asc' } }); const studentsWithStats = students.map(student => { const totalAttendances = student.attendances.length; const presentAttendances = student.attendances.filter(a => a.status === 'PRESENT').length; const attendanceRate = totalAttendances > 0 ? (presentAttendances / totalAttendances) * 100 : 0; return { id: student.id, firstName: student.firstName, lastName: student.lastName, cedula: student.cedula, email: student.email, admissionNumber: student.admissionNumber, enrollmentsCount: student.enrollments.length, attendanceRate: Math.round(attendanceRate * 100) / 100, totalAttendances, presentAttendances, enrollments: student.enrollments.map(enrollment => ({ sectionName: enrollment.section.name, className: enrollment.section.class.name, classCode: enrollment.section.class.code, periodName: enrollment.section.class.period.name })) }; }); return NextResponse.json({ students: studentsWithStats, summary: { totalStudents: students.length, averageEnrollments: students.length > 0 ? students.reduce((sum, s) => sum + s.enrollments.length, 0) / students.length : 0, averageAttendanceRate: studentsWithStats.length > 0 ? studentsWithStats.reduce((sum, s) => sum + s.attendanceRate, 0) / studentsWithStats.length : 0 } }); } catch (error) { console.error('Error in students report:', error); throw error; } } // Reporte de profesores async function getTeachersReport(periodId: string | null) { try { const whereClause = periodId ? { assignments: { some: { section: { class: { periodId: periodId } } } } } : {}; const teachers = await prisma.teacher.findMany({ where: { isActive: true, ...whereClause }, include: { assignments: { where: { isActive: true, ...(periodId ? { section: { class: { periodId: periodId } } } : {}) }, include: { section: { include: { class: { include: { period: true } }, studentEnrollments: { where: { isActive: true } } } } } } }, orderBy: { lastName: 'asc' } }); const teachersWithStats = teachers.map(teacher => { const totalStudents = teacher.assignments.reduce((sum, assignment) => sum + assignment.section.studentEnrollments.length, 0 ); return { id: teacher.id, firstName: teacher.firstName, lastName: teacher.lastName, cedula: teacher.cedula, email: teacher.email, phone: teacher.phone, assignmentsCount: teacher.assignments.length, totalStudents, assignments: teacher.assignments.map(assignment => ({ sectionName: assignment.section.name, className: assignment.section.class.name, classCode: assignment.section.class.code, periodName: assignment.section.class.period.name, studentsCount: assignment.section.studentEnrollments.length })) }; }); return NextResponse.json({ teachers: teachersWithStats, summary: { totalTeachers: teachers.length, averageAssignments: teachers.length > 0 ? teachers.reduce((sum, t) => sum + t.assignments.length, 0) / teachers.length : 0, totalStudentsManaged: teachersWithStats.reduce((sum, t) => sum + t.totalStudents, 0) } }); } catch (error) { console.error('Error in teachers report:', error); throw error; } } /* eslint-disable @typescript-eslint/no-explicit-any */ // Reporte de asistencia async function getAttendanceReport(periodId: string | null, startDate: string | null, endDate: string | null) { try { const dateFilter: any = {}; if (startDate) dateFilter.gte = new Date(startDate); if (endDate) dateFilter.lte = new Date(endDate); const whereClause: any = { ...(Object.keys(dateFilter).length > 0 && { date: dateFilter }), ...(periodId && { section: { class: { periodId: periodId } } }) }; const attendances = await prisma.attendance.findMany({ where: whereClause, include: { student: true, section: { include: { class: { include: { period: true } } } } }, orderBy: { date: 'desc' } }); const attendanceByStatus = await prisma.attendance.groupBy({ by: ['status'], where: whereClause, _count: { id: true } }); const attendanceByDate = await prisma.attendance.groupBy({ by: ['date'], where: whereClause, _count: { id: true }, orderBy: { date: 'asc' } }); return NextResponse.json({ attendances: attendances.map(attendance => ({ id: attendance.id, date: attendance.date, status: attendance.status, reason: attendance.reason, student: { firstName: attendance.student.firstName, lastName: attendance.student.lastName, cedula: attendance.student.cedula, admissionNumber: attendance.student.admissionNumber }, section: { name: attendance.section.name, className: attendance.section.class.name, classCode: attendance.section.class.code, periodName: attendance.section.class.period.name } })), summary: { totalRecords: attendances.length, byStatus: attendanceByStatus.map(item => ({ status: item.status, count: item._count.id })), byDate: attendanceByDate.map(item => ({ date: item.date, count: item._count.id })) } }); } catch (error) { console.error('Error in attendance report:', error); throw error; } } // Reporte de inscripciones async function getEnrollmentsReport(periodId: string | null) { try { const whereClause = periodId ? { section: { class: { periodId: periodId } } } : {}; const enrollments = await prisma.studentEnrollment.findMany({ where: whereClause, include: { student: true, section: { include: { class: { include: { period: true } } } } }, orderBy: { createdAt: 'desc' } }); const enrollmentsByStatus = await prisma.studentEnrollment.groupBy({ by: ['isActive'], where: whereClause, _count: { id: true } }); const enrollmentsBySection = await prisma.studentEnrollment.groupBy({ by: ['sectionId'], where: whereClause, _count: { id: true } }); return NextResponse.json({ enrollments: enrollments.map(enrollment => ({ id: enrollment.id, isActive: enrollment.isActive, createdAt: enrollment.createdAt, student: { firstName: enrollment.student.firstName, lastName: enrollment.student.lastName, cedula: enrollment.student.cedula, admissionNumber: enrollment.student.admissionNumber }, section: { name: enrollment.section.name, className: enrollment.section.class.name, classCode: enrollment.section.class.code, periodName: enrollment.section.class.period.name } })), summary: { totalEnrollments: enrollments.length, byStatus: enrollmentsByStatus.map(item => ({ isActive: item.isActive, count: item._count.id })), bySectionCount: enrollmentsBySection.length } }); } catch (error) { console.error('Error in enrollments report:', error); throw error; } } // Reporte de clases async function getClassesReport(periodId: string | null) { try { const whereClause = periodId ? { periodId } : { isActive: true }; const classes = await prisma.class.findMany({ where: whereClause, include: { period: true, sections: { include: { studentEnrollments: { where: { isActive: true } }, teacherAssignments: { where: { isActive: true }, include: { teacher: true } } } } }, orderBy: { name: 'asc' } }); const classesWithStats = classes.map(classItem => { const totalSections = classItem.sections.length; const totalStudents = classItem.sections.reduce((sum, section) => sum + section.studentEnrollments.length, 0 ); const totalTeachers = classItem.sections.reduce((sum, section) => sum + section.teacherAssignments.length, 0 ); return { id: classItem.id, name: classItem.name, code: classItem.code, description: classItem.description, isActive: classItem.isActive, period: { name: classItem.period.name, isActive: classItem.period.isActive }, totalSections, totalStudents, totalTeachers, sections: classItem.sections.map(section => ({ name: section.name, studentsCount: section.studentEnrollments.length, teachersCount: section.teacherAssignments.length, teachers: section.teacherAssignments.map(assignment => ({ firstName: assignment.teacher.firstName, lastName: assignment.teacher.lastName })) })) }; }); return NextResponse.json({ classes: classesWithStats, summary: { totalClasses: classes.length, totalSections: classesWithStats.reduce((sum, c) => sum + c.totalSections, 0), totalStudents: classesWithStats.reduce((sum, c) => sum + c.totalStudents, 0), averageStudentsPerClass: classes.length > 0 ? classesWithStats.reduce((sum, c) => sum + c.totalStudents, 0) / classes.length : 0 } }); } catch (error) { console.error('Error in classes report:', error); throw error; } }