Matthew Trejo 4 månader sedan
förälder
incheckning
f11d3f2f07
4 ändrade filer med 574 tillägg och 218 borttagningar
  1. 2 2
      src/app/admin/page.tsx
  2. 232 0
      src/app/api/student/dashboard/route.ts
  3. 123 37
      src/app/student/attendance/page.tsx
  4. 217 179
      src/app/student/page.tsx

+ 2 - 2
src/app/admin/page.tsx

@@ -128,7 +128,7 @@ export default function AdminDashboard() {
           </Card>
 
           {/* Resumen del Periodo */}
-          <Card>
+          {/* <Card>
             <CardHeader>
               <CardTitle className="flex items-center gap-2">
                 <Calendar className="h-5 w-5" />
@@ -160,7 +160,7 @@ export default function AdminDashboard() {
                 </div>
               </div>
             </CardContent>
-          </Card>
+          </Card> */}
         </div>
       </div>
     </MainLayout>

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

@@ -0,0 +1,232 @@
+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?.user?.id) {
+      return NextResponse.json(
+        { message: 'No autorizado' },
+        { status: 401 }
+      );
+    }
+
+    if (session.user.role !== 'STUDENT') {
+      return NextResponse.json(
+        { message: 'Acceso denegado. Solo estudiantes pueden acceder a esta información.' },
+        { status: 403 }
+      );
+    }
+
+    // Buscar el estudiante por userId
+    const student = await prisma.student.findUnique({
+      where: {
+        userId: session.user.id,
+        isActive: true,
+      },
+    });
+
+    if (!student) {
+      return NextResponse.json(
+        { message: 'Estudiante no encontrado' },
+        { status: 404 }
+      );
+    }
+
+    // Obtener fecha actual
+    const today = new Date();
+    today.setHours(0, 0, 0, 0);
+    const tomorrow = new Date(today);
+    tomorrow.setDate(tomorrow.getDate() + 1);
+
+    // Obtener primer día del mes actual
+    const firstDayOfMonth = new Date(today.getFullYear(), today.getMonth(), 1);
+
+    // 1. Obtener clases matriculadas activas
+    const enrollments = await prisma.studentEnrollment.findMany({
+      where: {
+        studentId: student.id,
+        isActive: true,
+        section: {
+          isActive: true,
+          class: {
+            isActive: true,
+            period: {
+              isActive: true
+            }
+          }
+        }
+      },
+      include: {
+        section: {
+          include: {
+            class: {
+              include: {
+                period: true
+              }
+            },
+            teacherAssignments: {
+              where: { isActive: true },
+              include: {
+                teacher: {
+                  select: {
+                    firstName: true,
+                    lastName: true
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+    });
+
+    // 2. Obtener estadísticas generales de asistencia
+    const allAttendances = await prisma.attendance.findMany({
+      where: {
+        studentId: student.id,
+        section: {
+          class: {
+            period: {
+              isActive: true
+            }
+          }
+        }
+      }
+    });
+
+    const totalRecords = allAttendances.length;
+    const presentRecords = allAttendances.filter(a => a.status === 'PRESENT').length;
+    const justifiedRecords = allAttendances.filter(a => a.status === 'JUSTIFIED').length;
+    const absentRecords = allAttendances.filter(a => a.status === 'ABSENT').length;
+    const attendanceRate = totalRecords > 0 ? Math.round(((presentRecords + justifiedRecords) / totalRecords) * 100) : 0;
+
+    // 3. Obtener asistencias de hoy (si las hay)
+    const todayAttendances = await prisma.attendance.findMany({
+      where: {
+        studentId: student.id,
+        date: {
+          gte: today,
+          lt: tomorrow
+        }
+      },
+      include: {
+        section: {
+          include: {
+            class: true
+          }
+        }
+      }
+    });
+
+    // 4. Obtener faltas del mes actual
+    const monthlyAbsences = await prisma.attendance.count({
+      where: {
+        studentId: student.id,
+        status: 'ABSENT',
+        date: {
+          gte: firstDayOfMonth,
+          lt: tomorrow
+        }
+      }
+    });
+
+    // 5. Obtener horario de hoy (secciones matriculadas - simulado)
+    const todaySchedule = enrollments.map(enrollment => ({
+      id: enrollment.section.id,
+      name: enrollment.section.class.name,
+      section: enrollment.section.name,
+      time: '08:00 - 10:00', // Esto sería ideal tenerlo en la BD
+      room: 'Aula 101', // Esto sería ideal tenerlo en la BD
+      professor: enrollment.section.teacherAssignments[0] 
+        ? `${enrollment.section.teacherAssignments[0].teacher.firstName} ${enrollment.section.teacherAssignments[0].teacher.lastName}`
+        : 'Sin asignar'
+    }));
+
+    // 6. Obtener historial reciente de asistencia (últimos 10 registros)
+    const recentAttendances = await prisma.attendance.findMany({
+      where: {
+        studentId: student.id
+      },
+      include: {
+        section: {
+          include: {
+            class: true
+          }
+        }
+      },
+      orderBy: {
+        date: 'desc'
+      },
+      take: 10
+    });
+
+    // 7. Obtener resumen por clase
+    const classesOverview = await Promise.all(
+      enrollments.map(async (enrollment) => {
+        const classAttendances = await prisma.attendance.findMany({
+          where: {
+            studentId: student.id,
+            sectionId: enrollment.sectionId
+          }
+        });
+
+        const totalClasses = classAttendances.length;
+        const attendedClasses = classAttendances.filter(a => a.status === 'PRESENT' || a.status === 'JUSTIFIED').length;
+        const attendancePercentage = totalClasses > 0 ? Math.round((attendedClasses / totalClasses) * 100) : 0;
+
+        return {
+          id: enrollment.section.id,
+          name: enrollment.section.class.name,
+          section: enrollment.section.name,
+          professor: enrollment.section.teacherAssignments[0] 
+            ? `${enrollment.section.teacherAssignments[0].teacher.firstName} ${enrollment.section.teacherAssignments[0].teacher.lastName}`
+            : 'Sin asignar',
+          attendance: attendancePercentage,
+          totalClasses,
+          attendedClasses
+        };
+      })
+    );
+
+    // Formatear datos de respuesta
+    const dashboardData = {
+      student: {
+        id: student.id,
+        firstName: student.firstName,
+        lastName: student.lastName,
+        admissionNumber: student.admissionNumber
+      },
+      stats: {
+        totalClasses: enrollments.length,
+        attendanceRate,
+        todayClasses: todaySchedule.length,
+        monthlyAbsences
+      },
+      todaySchedule,
+      recentAttendances: recentAttendances.map(attendance => ({
+        id: attendance.id,
+        class: attendance.section.class.name,
+        date: attendance.date.toISOString().split('T')[0],
+        status: attendance.status === 'PRESENT' ? 'Presente' : 
+                attendance.status === 'JUSTIFIED' ? 'Justificado' : 'Ausente',
+        time: '08:00', // Esto sería ideal tenerlo en la BD
+        statusColor: attendance.status === 'PRESENT' ? 'text-green-600' : 
+                    attendance.status === 'JUSTIFIED' ? 'text-yellow-600' : 'text-red-600'
+      })),
+      classesOverview
+    };
+
+    return NextResponse.json(dashboardData);
+
+  } catch (error) {
+    console.error('Error fetching student dashboard data:', error);
+    return NextResponse.json(
+      { message: 'Error interno del servidor' },
+      { status: 500 }
+    );
+  }
+}

+ 123 - 37
src/app/student/attendance/page.tsx

@@ -10,6 +10,7 @@ import { Label } from '@/components/ui/label';
 import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
 import { Progress } from '@/components/ui/progress';
 import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
+import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
 import {
   Calendar,
   Clock,
@@ -24,7 +25,8 @@ import {
   BarChart3,
   CalendarDays,
   FileText,
-  Download
+  Download,
+  ArrowUpDown
 } from 'lucide-react';
 import { toast } from 'sonner';
 import { AttendanceStatus } from '@prisma/client';
@@ -140,6 +142,8 @@ export default function StudentAttendancePage() {
   const [selectedStatus, setSelectedStatus] = useState<string>('all');
   const [startDate, setStartDate] = useState('');
   const [endDate, setEndDate] = useState('');
+  const [sortField, setSortField] = useState<'date' | 'class' | 'status'>('date');
+  const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('desc');
 
   useEffect(() => {
     fetchAttendance();
@@ -178,6 +182,32 @@ export default function StudentAttendancePage() {
       (record.reason && record.reason.toLowerCase().includes(searchTerm.toLowerCase()));
     
     return matchesSearch;
+  }).sort((a, b) => {
+    let aValue: string | number;
+    let bValue: string | number;
+    
+    switch (sortField) {
+      case 'date':
+        aValue = new Date(a.date).getTime();
+        bValue = new Date(b.date).getTime();
+        break;
+      case 'class':
+        aValue = a.section.class.name;
+        bValue = b.section.class.name;
+        break;
+      case 'status':
+        aValue = a.status;
+        bValue = b.status;
+        break;
+      default:
+        return 0;
+    }
+    
+    if (sortDirection === 'asc') {
+      return aValue < bValue ? -1 : aValue > bValue ? 1 : 0;
+    } else {
+      return aValue > bValue ? -1 : aValue < bValue ? 1 : 0;
+    }
   }) || [];
 
   const getStatusColor = (status: AttendanceStatus) => {
@@ -248,6 +278,15 @@ export default function StudentAttendancePage() {
     setEndDate('');
   };
 
+  const handleSort = (field: 'date' | 'class' | 'status') => {
+    if (sortField === field) {
+      setSortDirection(sortDirection === 'asc' ? 'desc' : 'asc');
+    } else {
+      setSortField(field);
+      setSortDirection('desc');
+    }
+  };
+
   if (loading) {
     return (
       <MainLayout requiredRole="STUDENT" title="Historial de Asistencia">
@@ -460,48 +499,95 @@ export default function StudentAttendancePage() {
           {/* Tab de Registros */}
           <TabsContent value="records" className="space-y-4">
             {filteredRecords.length > 0 ? (
-              <div className="grid grid-cols-1 gap-4">
-                {filteredRecords.map((record) => (
-                  <Card key={record.id} className="hover:shadow-md transition-shadow">
-                    <CardContent className="p-4">
-                      <div className="flex justify-between items-start">
-                        <div className="space-y-2 flex-1">
-                          <div className="flex items-center gap-3">
+              <Card>
+                <CardContent className="p-0">
+                  <Table>
+                    <TableHeader>
+                      <TableRow>
+                        <TableHead 
+                          className="cursor-pointer hover:bg-muted/50 select-none"
+                          onClick={() => handleSort('date')}
+                        >
+                          <div className="flex items-center gap-2">
+                            Fecha
+                            <ArrowUpDown className="h-4 w-4" />
+                          </div>
+                        </TableHead>
+                        <TableHead 
+                          className="cursor-pointer hover:bg-muted/50 select-none"
+                          onClick={() => handleSort('class')}
+                        >
+                          <div className="flex items-center gap-2">
+                            Clase
+                            <ArrowUpDown className="h-4 w-4" />
+                          </div>
+                        </TableHead>
+                        <TableHead>Sección</TableHead>
+                        <TableHead>Profesor</TableHead>
+                        <TableHead 
+                          className="cursor-pointer hover:bg-muted/50 select-none"
+                          onClick={() => handleSort('status')}
+                        >
+                          <div className="flex items-center gap-2">
+                            Estado
+                            <ArrowUpDown className="h-4 w-4" />
+                          </div>
+                        </TableHead>
+                        <TableHead>Observación</TableHead>
+                      </TableRow>
+                    </TableHeader>
+                    <TableBody>
+                      {filteredRecords.map((record) => (
+                        <TableRow key={record.id} className="hover:bg-muted/50">
+                          <TableCell className="font-medium">
+                            <div className="space-y-1">
+                              <div>{formatShortDate(record.date)}</div>
+                              <div className="text-xs text-muted-foreground">
+                                {new Date(record.date).toLocaleDateString('es-ES', { weekday: 'short' })}
+                              </div>
+                            </div>
+                          </TableCell>
+                          <TableCell>
+                            <div className="space-y-1">
+                              <div className="font-medium">{record.section.class.name}</div>
+                              <div className="text-sm text-muted-foreground">
+                                {record.section.class.code} • {record.section.class.period.name}
+                              </div>
+                            </div>
+                          </TableCell>
+                          <TableCell>
+                            <Badge variant="outline">{record.section.name}</Badge>
+                          </TableCell>
+                          <TableCell>
+                            <div className="text-sm">
+                              {getTeacherNames(record.section.teachers) || 'Sin asignar'}
+                            </div>
+                          </TableCell>
+                          <TableCell>
                             <div className={`flex items-center gap-2 ${getStatusColor(record.status)}`}>
                               {getStatusIcon(record.status)}
                               <Badge variant={getStatusBadgeVariant(record.status)}>
                                 {getStatusText(record.status)}
                               </Badge>
                             </div>
-                            <div className="text-sm text-muted-foreground">
-                              {formatDate(record.date)}
-                            </div>
-                          </div>
-                          
-                          <div className="space-y-1">
-                            <div className="font-medium">
-                              {record.section.class.name} - {record.section.name}
-                            </div>
-                            <div className="text-sm text-muted-foreground">
-                              {record.section.class.code} • {record.section.class.period.name}
-                            </div>
-                            <div className="text-sm text-muted-foreground">
-                              <User className="h-3 w-3 inline mr-1" />
-                              {getTeacherNames(record.section.teachers) || 'Sin profesor asignado'}
-                            </div>
-                          </div>
-                          
-                          {record.reason && (
-                            <div className="text-sm bg-muted p-2 rounded">
-                              <strong>Observación:</strong> {record.reason}
-                            </div>
-                          )}
-                        </div>
-                      </div>
-                    </CardContent>
-                  </Card>
-                ))}
-              </div>
+                          </TableCell>
+                          <TableCell>
+                            {record.reason ? (
+                              <div className="max-w-xs">
+                                <div className="text-sm truncate" title={record.reason}>
+                                  {record.reason}
+                                </div>
+                              </div>
+                            ) : (
+                              <span className="text-muted-foreground text-sm">-</span>
+                            )}
+                          </TableCell>
+                        </TableRow>
+                      ))}
+                    </TableBody>
+                  </Table>
+                </CardContent>
+              </Card>
             ) : (
               <Card>
                 <CardContent className="flex flex-col items-center justify-center py-12">

+ 217 - 179
src/app/student/page.tsx

@@ -1,133 +1,153 @@
 'use client'
 
+import { useEffect, useState } from 'react'
 import { MainLayout } from '@/components/layout/main-layout'
 import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
-import { BookOpen, Calendar, ClipboardList, TrendingUp, Clock, CheckCircle, XCircle } from 'lucide-react'
+import { BookOpen, Calendar, ClipboardList, TrendingUp, Clock, CheckCircle, XCircle, Loader2 } from 'lucide-react'
+import { toast } from 'sonner'
 
-const stats = [
-  {
-    title: 'Mis Clases',
-    value: '3',
-    description: 'Clases matriculadas',
-    icon: BookOpen,
-    color: 'text-blue-600',
-    bgColor: 'bg-blue-100'
-  },
-  {
-    title: 'Asistencia General',
-    value: '88%',
-    description: 'Promedio del semestre',
-    icon: TrendingUp,
-    color: 'text-green-600',
-    bgColor: 'bg-green-100'
-  },
-  {
-    title: 'Clases Hoy',
-    value: '2',
-    description: 'Clases programadas',
-    icon: Calendar,
-    color: 'text-purple-600',
-    bgColor: 'bg-purple-100'
-  },
-  {
-    title: 'Faltas Este Mes',
-    value: '3',
-    description: 'Inasistencias registradas',
-    icon: XCircle,
-    color: 'text-red-600',
-    bgColor: 'bg-red-100'
-  }
-]
+interface Student {
+  id: string;
+  firstName: string;
+  lastName: string;
+  admissionNumber: string;
+}
+
+interface Stats {
+  totalClasses: number;
+  attendanceRate: number;
+  todayClasses: number;
+  monthlyAbsences: number;
+}
+
+interface TodayScheduleItem {
+  id: string;
+  name: string;
+  section: string;
+  time: string;
+  room: string;
+  professor: string;
+}
+
+interface AttendanceHistoryItem {
+  id: string;
+  class: string;
+  date: string;
+  status: string;
+  time: string;
+  statusColor: string;
+}
+
+interface ClassOverviewItem {
+  id: string;
+  name: string;
+  section: string;
+  professor: string;
+  attendance: number;
+  totalClasses: number;
+  attendedClasses: number;
+}
+
+interface DashboardData {
+  student: Student;
+  stats: Stats;
+  todaySchedule: TodayScheduleItem[];
+  recentAttendances: AttendanceHistoryItem[];
+  classesOverview: ClassOverviewItem[];
+}
 
-const todaySchedule = [
-  {
-    id: 1,
-    name: 'Matemáticas I',
-    section: 'Sección A',
-    time: '08:00 - 10:00',
-    room: 'Aula 101',
-    professor: 'Juan Pérez'
-  },
-  {
-    id: 2,
-    name: 'Programación I',
-    section: 'Sección B',
-    time: '14:00 - 16:00',
-    room: 'Lab 201',
-    professor: 'Juan Pérez'
+export default function StudentDashboard() {
+  const [data, setData] = useState<DashboardData | null>(null)
+  const [loading, setLoading] = useState(true)
+
+  useEffect(() => {
+    fetchDashboardData()
+  }, [])
+
+  const fetchDashboardData = async () => {
+    try {
+      setLoading(true)
+      const response = await fetch('/api/student/dashboard')
+      if (!response.ok) {
+        throw new Error('Error al cargar los datos del dashboard')
+      }
+      const dashboardData: DashboardData = await response.json()
+      setData(dashboardData)
+    } catch (error) {
+      console.error('Error:', error)
+      toast.error('Error al cargar los datos del dashboard')
+    } finally {
+      setLoading(false)
+    }
   }
-]
 
-const attendanceHistory = [
-  {
-    id: 1,
-    class: 'Matemáticas I',
-    date: '2024-01-20',
-    status: 'Presente',
-    time: '08:00',
-    statusColor: 'text-green-600'
-  },
-  {
-    id: 2,
-    class: 'Programación I',
-    date: '2024-01-19',
-    status: 'Presente',
-    time: '14:00',
-    statusColor: 'text-green-600'
-  },
-  {
-    id: 3,
-    class: 'Física I',
-    date: '2024-01-18',
-    status: 'Ausente',
-    time: '10:00',
-    statusColor: 'text-red-600'
-  },
-  {
-    id: 4,
-    class: 'Matemáticas I',
-    date: '2024-01-17',
-    status: 'Presente',
-    time: '08:00',
-    statusColor: 'text-green-600'
+  if (loading) {
+    return (
+      <MainLayout 
+        title="Dashboard Estudiante" 
+        subtitle="Mi progreso académico y asistencia"
+        requiredRole="STUDENT"
+      >
+        <div className="flex items-center justify-center h-64">
+          <Loader2 className="h-8 w-8 animate-spin" />
+        </div>
+      </MainLayout>
+    )
   }
-]
 
-const classesOverview = [
-  {
-    id: 1,
-    name: 'Matemáticas I',
-    section: 'Sección A',
-    professor: 'Juan Pérez',
-    attendance: 92,
-    totalClasses: 25,
-    attendedClasses: 23
-  },
-  {
-    id: 2,
-    name: 'Programación I',
-    section: 'Sección B',
-    professor: 'Juan Pérez',
-    attendance: 85,
-    totalClasses: 20,
-    attendedClasses: 17
-  },
-  {
-    id: 3,
-    name: 'Física I',
-    section: 'Sección A',
-    professor: 'Juan Pérez',
-    attendance: 87,
-    totalClasses: 23,
-    attendedClasses: 20
+  if (!data) {
+    return (
+      <MainLayout 
+        title="Dashboard Estudiante" 
+        subtitle="Mi progreso académico y asistencia"
+        requiredRole="STUDENT"
+      >
+        <div className="text-center py-8">
+          <p className="text-muted-foreground">No se pudieron cargar los datos del dashboard.</p>
+        </div>
+      </MainLayout>
+    )
   }
-]
 
-export default function StudentDashboard() {
+  const stats = [
+    {
+      title: 'Mis Clases',
+      value: data.stats.totalClasses.toString(),
+      description: 'Clases matriculadas',
+      icon: BookOpen,
+      color: 'text-blue-600',
+      bgColor: 'bg-blue-100'
+    },
+    {
+      title: 'Asistencia General',
+      value: `${data.stats.attendanceRate}%`,
+      description: 'Promedio del semestre',
+      icon: TrendingUp,
+      color: 'text-green-600',
+      bgColor: 'bg-green-100'
+    },
+    {
+      title: 'Clases Hoy',
+      value: data.stats.todayClasses.toString(),
+      description: 'Clases programadas',
+      icon: Calendar,
+      color: 'text-purple-600',
+      bgColor: 'bg-purple-100'
+    },
+    {
+      title: 'Faltas Este Mes',
+      value: data.stats.monthlyAbsences.toString(),
+      description: 'Inasistencias registradas',
+      icon: XCircle,
+      color: 'text-red-600',
+      bgColor: 'bg-red-100'
+    }
+  ]
+
   return (
     <MainLayout 
       title="Dashboard Estudiante" 
-      subtitle="Mi progreso académico y asistencia"
+      subtitle={`Bienvenido/a ${data.student.firstName} ${data.student.lastName}`}
       requiredRole="STUDENT"
     >
       <div className="space-y-6">
@@ -170,19 +190,25 @@ export default function StudentDashboard() {
             </CardHeader>
             <CardContent>
               <div className="space-y-4">
-                {todaySchedule.map((classItem) => (
-                  <div key={classItem.id} className="p-3 border rounded-lg">
-                    <div className="space-y-2">
-                      <p className="text-sm font-medium">{classItem.name}</p>
-                      <p className="text-xs text-muted-foreground">
-                        {classItem.section} • {classItem.room}
-                      </p>
-                      <p className="text-xs text-muted-foreground">
-                        {classItem.time} • Prof. {classItem.professor}
-                      </p>
+                {data.todaySchedule.length > 0 ? (
+                  data.todaySchedule.map((classItem) => (
+                    <div key={classItem.id} className="p-3 border rounded-lg">
+                      <div className="space-y-2">
+                        <p className="text-sm font-medium">{classItem.name}</p>
+                        <p className="text-xs text-muted-foreground">
+                          {classItem.section} • {classItem.room}
+                        </p>
+                        <p className="text-xs text-muted-foreground">
+                          {classItem.time} • Prof. {classItem.professor}
+                        </p>
+                      </div>
                     </div>
-                  </div>
-                ))}
+                  ))
+                ) : (
+                  <p className="text-sm text-muted-foreground text-center py-4">
+                    No hay clases programadas para hoy
+                  </p>
+                )}
               </div>
             </CardContent>
           </Card>
@@ -200,26 +226,32 @@ export default function StudentDashboard() {
             </CardHeader>
             <CardContent>
               <div className="space-y-3">
-                {attendanceHistory.map((record) => (
-                  <div key={record.id} className="flex items-center justify-between">
-                    <div className="space-y-1">
-                      <p className="text-sm font-medium">{record.class}</p>
-                      <p className="text-xs text-muted-foreground">
-                        {record.date} • {record.time}
-                      </p>
-                    </div>
-                    <div className="flex items-center gap-2">
-                      {record.status === 'Presente' ? (
-                        <CheckCircle className="h-4 w-4 text-green-600" />
-                      ) : (
-                        <XCircle className="h-4 w-4 text-red-600" />
-                      )}
-                      <span className={`text-xs font-medium ${record.statusColor}`}>
-                        {record.status}
-                      </span>
+                {data.recentAttendances.length > 0 ? (
+                  data.recentAttendances.map((record) => (
+                    <div key={record.id} className="flex items-center justify-between">
+                      <div className="space-y-1">
+                        <p className="text-sm font-medium">{record.class}</p>
+                        <p className="text-xs text-muted-foreground">
+                          {record.date} • {record.time}
+                        </p>
+                      </div>
+                      <div className="flex items-center gap-2">
+                        {record.status === 'Presente' || record.status === 'Justificado' ? (
+                          <CheckCircle className="h-4 w-4 text-green-600" />
+                        ) : (
+                          <XCircle className="h-4 w-4 text-red-600" />
+                        )}
+                        <span className={`text-xs font-medium ${record.statusColor}`}>
+                          {record.status}
+                        </span>
+                      </div>
                     </div>
-                  </div>
-                ))}
+                  ))
+                ) : (
+                  <p className="text-sm text-muted-foreground text-center py-4">
+                    No hay registros de asistencia
+                  </p>
+                )}
               </div>
             </CardContent>
           </Card>
@@ -237,42 +269,48 @@ export default function StudentDashboard() {
             </CardHeader>
             <CardContent>
               <div className="space-y-4">
-                {classesOverview.map((classItem) => (
-                  <div key={classItem.id} className="space-y-2">
-                    <div className="flex justify-between items-start">
-                      <div>
-                        <p className="text-sm font-medium">{classItem.name}</p>
-                        <p className="text-xs text-muted-foreground">
-                          {classItem.section} • Prof. {classItem.professor}
-                        </p>
-                      </div>
-                      <span className={`text-xs font-medium ${
-                        classItem.attendance >= 90 
-                          ? 'text-green-600' 
-                          : classItem.attendance >= 80 
-                          ? 'text-yellow-600' 
-                          : 'text-red-600'
-                      }`}>
-                        {classItem.attendance}%
-                      </span>
-                    </div>
-                    <div className="w-full bg-gray-200 rounded-full h-2">
-                      <div 
-                        className={`h-2 rounded-full ${
+                {data.classesOverview.length > 0 ? (
+                  data.classesOverview.map((classItem) => (
+                    <div key={classItem.id} className="space-y-2">
+                      <div className="flex justify-between items-start">
+                        <div>
+                          <p className="text-sm font-medium">{classItem.name}</p>
+                          <p className="text-xs text-muted-foreground">
+                            {classItem.section} • Prof. {classItem.professor}
+                          </p>
+                        </div>
+                        <span className={`text-xs font-medium ${
                           classItem.attendance >= 90 
-                            ? 'bg-green-600' 
+                            ? 'text-green-600' 
                             : classItem.attendance >= 80 
-                            ? 'bg-yellow-600' 
-                            : 'bg-red-600'
-                        }`}
-                        style={{ width: `${classItem.attendance}%` }}
-                      ></div>
+                            ? 'text-yellow-600' 
+                            : 'text-red-600'
+                        }`}>
+                          {classItem.attendance}%
+                        </span>
+                      </div>
+                      <div className="w-full bg-gray-200 rounded-full h-2">
+                        <div 
+                          className={`h-2 rounded-full ${
+                            classItem.attendance >= 90 
+                              ? 'bg-green-600' 
+                              : classItem.attendance >= 80 
+                              ? 'bg-yellow-600' 
+                              : 'bg-red-600'
+                          }`}
+                          style={{ width: `${classItem.attendance}%` }}
+                        ></div>
+                      </div>
+                      <p className="text-xs text-muted-foreground">
+                        {classItem.attendedClasses}/{classItem.totalClasses} clases asistidas
+                      </p>
                     </div>
-                    <p className="text-xs text-muted-foreground">
-                      {classItem.attendedClasses}/{classItem.totalClasses} clases asistidas
-                    </p>
-                  </div>
-                ))}
+                  ))
+                ) : (
+                  <p className="text-sm text-muted-foreground text-center py-4">
+                    No hay clases matriculadas
+                  </p>
+                )}
               </div>
             </CardContent>
           </Card>