| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551 |
- 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;
- }
- }
|