Ver código fonte

actually show data holy shit

Matthew Trejo 4 meses atrás
pai
commit
cea175da01

+ 102 - 0
src/app/api/student/attendance-summary/route.ts

@@ -0,0 +1,102 @@
+import { NextRequest, NextResponse } from 'next/server'
+import { getServerSession } from 'next-auth/next'
+import { authOptions } from '@/lib/auth'
+import { db } from '@/lib/db'
+import { 
+  attendance, 
+  eq, 
+  and,
+  gte,
+  lte
+} from '@/lib/db/schema'
+
+export async function GET(request: NextRequest) {
+  try {
+    const session = await getServerSession(authOptions)
+
+    if (!session || session.user.role !== 'student') {
+      return NextResponse.json(
+        { error: 'No autorizado' },
+        { status: 401 }
+      )
+    }
+
+    const { searchParams } = new URL(request.url)
+    const startDate = searchParams.get('startDate')
+    const endDate = searchParams.get('endDate')
+    const sectionId = searchParams.get('sectionId')
+
+    const studentId = session.user.id
+
+    // Build filter conditions
+    const conditions = [eq(attendance.studentId, studentId)]
+    
+    if (startDate) {
+      conditions.push(gte(attendance.date, startDate))
+    }
+    
+    if (endDate) {
+      conditions.push(lte(attendance.date, endDate))
+    }
+
+    if (sectionId) {
+      conditions.push(eq(attendance.sectionId, sectionId))
+    }
+
+    // Get attendance records
+    const attendanceRecords = await db
+      .select({
+        status: attendance.status,
+        date: attendance.date,
+        sectionId: attendance.sectionId,
+        classId: attendance.classId
+      })
+      .from(attendance)
+      .where(and(...conditions))
+      .orderBy(attendance.date)
+
+    // Calculate summary statistics
+    const totalClasses = attendanceRecords.length
+    const present = attendanceRecords.filter(record => record.status === 'present').length
+    const absent = attendanceRecords.filter(record => record.status === 'absent').length
+    const tardy = attendanceRecords.filter(record => record.status === 'late').length
+    const justified = attendanceRecords.filter(record => record.status === 'justified').length
+    
+    const attendanceRate = totalClasses > 0 ? Math.round((present / totalClasses) * 100 * 10) / 10 : 0
+
+    // Group by month for trend analysis
+    const monthlyData = attendanceRecords.reduce((acc, record) => {
+      const month = record.date.substring(0, 7) // YYYY-MM format
+      if (!acc[month]) {
+        acc[month] = { present: 0, absent: 0, tardy: 0, total: 0 }
+      }
+      acc[month].total++
+      if (record.status === 'present') acc[month].present++
+      else if (record.status === 'absent') acc[month].absent++
+      else if (record.status === 'late') acc[month].tardy++
+      return acc
+    }, {} as Record<string, { present: number; absent: number; tardy: number; total: number }>)
+
+    const attendanceSummary = {
+      totalClasses,
+      present,
+      absent,
+      tardy,
+      justified,
+      attendanceRate,
+      monthlyData: Object.entries(monthlyData).map(([month, data]) => ({
+        month,
+        ...data,
+        rate: data.total > 0 ? Math.round((data.present / data.total) * 100 * 10) / 10 : 0
+      }))
+    }
+
+    return NextResponse.json(attendanceSummary)
+  } catch (error) {
+    console.error('Error fetching attendance summary:', error)
+    return NextResponse.json(
+      { error: 'Error interno del servidor' },
+      { status: 500 }
+    )
+  }
+}

+ 128 - 0
src/app/api/student/dashboard/route.ts

@@ -0,0 +1,128 @@
+import { NextRequest, NextResponse } from 'next/server'
+import { getServerSession } from 'next-auth/next'
+import { authOptions } from '@/lib/auth'
+import { db } from '@/lib/db'
+import {
+  users,
+  studentEnrollments,
+  sections,
+  classes,
+  periods,
+  teacherAssignments,
+  attendance,
+  eq,
+  and
+} from '@/lib/db/schema'
+import { count } from 'drizzle-orm'
+
+export async function GET(request: NextRequest) {
+  try {
+    const session = await getServerSession(authOptions)
+
+    if (!session || session.user.role !== 'student') {
+      return NextResponse.json(
+        { error: 'No autorizado' },
+        { status: 401 }
+      )
+    }
+
+    const studentId = session.user.id
+
+    // Get enrolled sections with class and period information
+    const enrolledSections = await db
+      .select({
+        sectionId: sections.id,
+        sectionName: sections.name,
+        className: classes.name,
+        classId: classes.id,
+        classCode: classes.code,
+        credits: classes.credits,
+        periodName: periods.name,
+        periodId: periods.id,
+        maxStudents: sections.maxStudents,
+        isActive: sections.isActive,
+        enrollmentDate: studentEnrollments.createdAt
+      })
+      .from(studentEnrollments)
+      .innerJoin(sections, eq(studentEnrollments.sectionId, sections.id))
+      .innerJoin(classes, eq(studentEnrollments.classId, classes.id))
+      .innerJoin(periods, eq(classes.periodId, periods.id))
+      .where(
+        and(
+          eq(studentEnrollments.studentId, studentId),
+          eq(studentEnrollments.isActive, true)
+        )
+      )
+      .orderBy(classes.code, sections.name)
+
+    // Get teacher information for each section
+    const sectionsWithTeachers = await Promise.all(
+      enrolledSections.map(async (section) => {
+        // Get teacher assigned to this section
+        const teacherQuery = await db
+          .select({
+            teacherId: users.id,
+            teacherFirstName: users.firstName,
+            teacherLastName: users.lastName
+          })
+          .from(teacherAssignments)
+          .innerJoin(users, eq(teacherAssignments.teacherId, users.id))
+          .where(
+            and(
+              eq(teacherAssignments.sectionId, section.sectionId),
+              eq(teacherAssignments.isActive, true)
+            )
+          )
+          .limit(1)
+
+        const teacher = teacherQuery[0]
+        const teacherName = teacher 
+          ? `${teacher.teacherFirstName} ${teacher.teacherLastName}`
+          : 'Sin asignar'
+
+        return {
+          id: section.sectionId,
+          name: section.sectionName,
+          className: section.className,
+          classCode: section.classCode,
+          credits: section.credits,
+          teacherName,
+          isActive: section.isActive
+        }
+      })
+    )
+
+    // Calculate attendance summary
+    const attendanceRecords = await db
+      .select({
+        status: attendance.status
+      })
+      .from(attendance)
+      .where(eq(attendance.studentId, studentId))
+
+    const totalClasses = attendanceRecords.length
+    const present = attendanceRecords.filter(record => record.status === 'present').length
+    const absent = attendanceRecords.filter(record => record.status === 'absent').length
+    const tardy = attendanceRecords.filter(record => record.status === 'late').length
+    const attendanceRate = totalClasses > 0 ? Math.round((present / totalClasses) * 100 * 10) / 10 : 0
+
+    const attendanceSummary = {
+      totalClasses,
+      present,
+      absent,
+      tardy,
+      attendanceRate
+    }
+
+    return NextResponse.json({
+      sections: sectionsWithTeachers,
+      attendanceSummary
+    })
+  } catch (error) {
+    console.error('Error fetching student dashboard data:', error)
+    return NextResponse.json(
+      { error: 'Error interno del servidor' },
+      { status: 500 }
+    )
+  }
+}

+ 33 - 43
src/app/student/dashboard/page.tsx

@@ -17,6 +17,8 @@ interface Section {
   credits: number
   teacherName: string
   isActive: boolean
+  periodName: string
+  enrollmentDate: string
 }
 
 interface AttendanceSummary {
@@ -24,9 +26,15 @@ interface AttendanceSummary {
   present: number
   absent: number
   tardy: number
+  justified: number
   attendanceRate: number
 }
 
+interface DashboardData {
+  sections: Section[]
+  attendanceSummary: AttendanceSummary
+}
+
 export default function StudentDashboard() {
   const [sections, setSections] = useState<Section[]>([])
   const [attendanceSummary, setAttendanceSummary] = useState<AttendanceSummary | null>(null)
@@ -38,50 +46,22 @@ export default function StudentDashboard() {
 
   const fetchStudentData = async () => {
     try {
-      // Simular datos por ahora - en el futuro se conectará a APIs reales
-      const mockSections: Section[] = [
-        {
-          id: '1',
-          name: 'Sección A',
-          className: 'Matemáticas Avanzadas',
-          classCode: 'MAT-401',
-          credits: 4,
-          teacherName: 'Prof. García',
-          isActive: true
-        },
-        {
-          id: '2',
-          name: 'Sección B',
-          className: 'Programación Web',
-          classCode: 'INF-301',
-          credits: 3,
-          teacherName: 'Prof. Rodríguez',
-          isActive: true
-        },
-        {
-          id: '3',
-          name: 'Sección C',
-          className: 'Base de Datos',
-          classCode: 'INF-302',
-          credits: 3,
-          teacherName: 'Prof. Martínez',
-          isActive: true
-        }
-      ]
-
-      const mockAttendance: AttendanceSummary = {
-        totalClasses: 45,
-        present: 38,
-        absent: 4,
-        tardy: 3,
-        attendanceRate: 84.4
+      setLoading(true)
+      
+      // Obtener datos del dashboard del estudiante
+      const response = await fetch('/api/student/dashboard')
+      
+      if (!response.ok) {
+        throw new Error('Error al obtener los datos del dashboard')
       }
-
-      setSections(mockSections)
-      setAttendanceSummary(mockAttendance)
+      
+      const data: DashboardData = await response.json()
+      
+      setSections(data.sections)
+      setAttendanceSummary(data.attendanceSummary)
     } catch (error) {
       console.error('Error:', error)
-      toast.error('Error al cargar los datos')
+      toast.error('Error al cargar los datos del dashboard')
     } finally {
       setLoading(false)
     }
@@ -138,7 +118,7 @@ export default function StudentDashboard() {
 
         {/* Resumen de Asistencia */}
         {attendanceSummary && (
-          <div className="grid grid-cols-1 md:grid-cols-4 gap-4">
+          <div className="grid grid-cols-1 md:grid-cols-5 gap-4">
             <Card>
               <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
                 <CardTitle className="text-sm font-medium">Total Clases</CardTitle>
@@ -178,6 +158,16 @@ export default function StudentDashboard() {
                 <div className="text-2xl font-bold text-yellow-600">{attendanceSummary.tardy}</div>
               </CardContent>
             </Card>
+
+            {/* <Card>
+              <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
+                <CardTitle className="text-sm font-medium">Justificado</CardTitle>
+                <CheckCircle className="h-4 w-4 text-blue-600" />
+              </CardHeader>
+              <CardContent>
+                <div className="text-2xl font-bold text-blue-600">{attendanceSummary.justified}</div>
+              </CardContent>
+            </Card> */}
           </div>
         )}
 
@@ -234,7 +224,7 @@ export default function StudentDashboard() {
                         {section.classCode} - {section.name}
                       </p>
                       <p className="text-sm text-muted-foreground">
-                        {section.teacherName} • {section.credits} créditos
+                        {section.teacherName} • {section.periodName} • {section.credits} créditos
                       </p>
                     </div>
                   </div>