Kaynağa Gözat

fuck the time

Matthew Trejo 4 ay önce
ebeveyn
işleme
0544fe6a3a
3 değiştirilmiş dosya ile 362 ekleme ve 113 silme
  1. 182 0
      src/app/api/teacher/dashboard/route.ts
  2. 150 113
      src/app/teacher/page.tsx
  3. 30 0
      src/types/teacher.ts

+ 182 - 0
src/app/api/teacher/dashboard/route.ts

@@ -0,0 +1,182 @@
+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?.email) {
+      return NextResponse.json(
+        { error: 'No autorizado' },
+        { status: 401 }
+      );
+    }
+
+    // Verificar que el usuario sea un profesor
+    const user = await prisma.user.findUnique({
+      where: { email: session.user.email },
+      include: { teacher: true }
+    });
+
+    if (!user || user.role !== 'TEACHER' || !user.teacher) {
+      return NextResponse.json(
+        { error: 'Acceso denegado. Solo profesores pueden acceder a esta información.' },
+        { status: 403 }
+      );
+    }
+
+    const teacherId = user.teacher.id;
+    const today = new Date();
+    today.setHours(0, 0, 0, 0);
+
+    // Obtener asignaciones activas del profesor
+    const assignments = await prisma.teacherAssignment.findMany({
+      where: {
+        teacherId,
+        isActive: true
+      },
+      include: {
+        section: {
+          include: {
+            class: {
+              include: {
+                period: true
+              }
+            },
+            studentEnrollments: {
+              where: { isActive: true },
+              include: {
+                student: true
+              }
+            }
+          }
+        }
+      }
+    });
+
+    // Calcular estadísticas
+    const totalClasses = assignments.length;
+    const totalStudents = assignments.reduce((sum, assignment) => 
+      sum + assignment.section.studentEnrollments.length, 0
+    );
+
+    // Obtener clases de hoy (simulado - en un caso real necesitarías horarios)
+    const todayClasses = assignments.slice(0, 2).map(assignment => ({
+      id: assignment.section.id,
+      name: assignment.section.class.name,
+      section: assignment.section.name,
+      time: '08:00 - 10:00', // Placeholder - necesitarías tabla de horarios
+      room: 'Aula 101', // Placeholder - necesitarías tabla de aulas
+      students: assignment.section.studentEnrollments.length
+    }));
+
+    // Obtener asistencia reciente
+    const recentAttendance = await prisma.attendance.findMany({
+      where: {
+        section: {
+          teacherAssignments: {
+            some: {
+              teacherId,
+              isActive: true
+            }
+          }
+        },
+        date: {
+          gte: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000) // Últimos 7 días
+        }
+      },
+      include: {
+        section: {
+          include: {
+            class: true
+          }
+        }
+      },
+      orderBy: {
+        date: 'desc'
+      },
+      take: 10
+    });
+
+    // Agrupar asistencia por sección y fecha
+    const attendanceBySection = recentAttendance.reduce((acc, record) => {
+      const key = `${record.sectionId}-${record.date.toISOString().split('T')[0]}`;
+      if (!acc[key]) {
+        acc[key] = {
+          sectionId: record.sectionId,
+          className: record.section.class.name,
+          sectionName: record.section.name,
+          date: record.date,
+          present: 0,
+          total: 0
+        };
+      }
+      acc[key].total++;
+      if (record.status === 'PRESENT') {
+        acc[key].present++;
+      }
+      return acc;
+    }, {} as Record<string, any>);
+
+    const recentAttendanceStats = Object.values(attendanceBySection)
+      .slice(0, 3)
+      .map((stat: any) => ({
+        id: `${stat.sectionId}-${stat.date.toISOString()}`,
+        class: `${stat.className} - ${stat.sectionName}`,
+        date: stat.date.toISOString().split('T')[0],
+        present: stat.present,
+        total: stat.total,
+        percentage: stat.total > 0 ? Math.round((stat.present / stat.total) * 100) : 0
+      }));
+
+    // Calcular promedio de asistencia del mes actual
+    const currentMonth = new Date();
+    currentMonth.setDate(1);
+    currentMonth.setHours(0, 0, 0, 0);
+
+    const monthlyAttendance = await prisma.attendance.findMany({
+      where: {
+        section: {
+          teacherAssignments: {
+            some: {
+              teacherId,
+              isActive: true
+            }
+          }
+        },
+        date: {
+          gte: currentMonth
+        }
+      }
+    });
+
+    const totalMonthlyRecords = monthlyAttendance.length;
+    const presentMonthlyRecords = monthlyAttendance.filter(record => record.status === 'PRESENT').length;
+    const averageAttendance = totalMonthlyRecords > 0 
+      ? Math.round((presentMonthlyRecords / totalMonthlyRecords) * 100)
+      : 0;
+
+    // Preparar respuesta
+    const dashboardData = {
+      stats: {
+        totalClasses,
+        totalStudents,
+        todayClasses: todayClasses.length,
+        averageAttendance
+      },
+      todayClasses,
+      recentAttendance: recentAttendanceStats
+    };
+
+    return NextResponse.json(dashboardData);
+
+  } catch (error) {
+    console.error('Error al obtener datos del dashboard:', error);
+    return NextResponse.json(
+      { error: 'Error interno del servidor' },
+      { status: 500 }
+    );
+  }
+}

+ 150 - 113
src/app/teacher/page.tsx

@@ -1,91 +1,116 @@
 '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, Users, Calendar, ClipboardList, Clock, CheckCircle } from 'lucide-react'
+import { BookOpen, Users, Calendar, ClipboardList, Clock, CheckCircle, Loader2 } from 'lucide-react'
+import { TeacherDashboardData } from '@/types/teacher'
 
-const stats = [
-  {
-    title: 'Mis Clases',
-    value: '3',
-    description: 'Clases asignadas',
-    icon: BookOpen,
-    color: 'text-blue-600',
-    bgColor: 'bg-blue-100'
-  },
-  {
-    title: 'Estudiantes',
-    value: '15',
-    description: 'Total de estudiantes',
-    icon: Users,
-    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: 'Asistencia Promedio',
-    value: '85%',
-    description: 'Este mes',
-    icon: CheckCircle,
-    color: 'text-orange-600',
-    bgColor: 'bg-orange-100'
+export default function TeacherDashboard() {
+  const [dashboardData, setDashboardData] = useState<TeacherDashboardData | null>(null)
+  const [loading, setLoading] = useState(true)
+  const [error, setError] = useState<string | null>(null)
+
+  useEffect(() => {
+    const fetchDashboardData = async () => {
+      try {
+        const response = await fetch('/api/teacher/dashboard')
+        if (!response.ok) {
+          throw new Error('Error al cargar los datos del dashboard')
+        }
+        const data = await response.json()
+        setDashboardData(data)
+      } catch (err) {
+        setError(err instanceof Error ? err.message : 'Error desconocido')
+      } finally {
+        setLoading(false)
+      }
+    }
+
+    fetchDashboardData()
+  }, [])
+
+  const stats = dashboardData ? [
+    {
+      title: 'Mis Clases',
+      value: dashboardData.stats.totalClasses.toString(),
+      description: 'Clases asignadas',
+      icon: BookOpen,
+      color: 'text-blue-600',
+      bgColor: 'bg-blue-100'
+    },
+    {
+      title: 'Estudiantes',
+      value: dashboardData.stats.totalStudents.toString(),
+      description: 'Total de estudiantes',
+      icon: Users,
+      color: 'text-green-600',
+      bgColor: 'bg-green-100'
+    },
+    {
+      title: 'Clases Hoy',
+      value: dashboardData.stats.todayClasses.toString(),
+      description: 'Clases programadas',
+      icon: Calendar,
+      color: 'text-purple-600',
+      bgColor: 'bg-purple-100'
+    },
+    {
+      title: 'Asistencia Promedio',
+      value: `${dashboardData.stats.averageAttendance}%`,
+      description: 'Este mes',
+      icon: CheckCircle,
+      color: 'text-orange-600',
+      bgColor: 'bg-orange-100'
+    }
+  ] : []
+
+  if (loading) {
+    return (
+      <MainLayout 
+        title="Dashboard Profesor" 
+        subtitle="Gestión de clases y asistencia"
+        requiredRole="TEACHER"
+      >
+        <div className="flex items-center justify-center h-64">
+          <Loader2 className="h-8 w-8 animate-spin" />
+          <span className="ml-2">Cargando datos del dashboard...</span>
+        </div>
+      </MainLayout>
+    )
   }
-]
 
-const todayClasses = [
-  {
-    id: 1,
-    name: 'Matemáticas I',
-    section: 'Sección A',
-    time: '08:00 - 10:00',
-    room: 'Aula 101',
-    students: 25
-  },
-  {
-    id: 2,
-    name: 'Programación I',
-    section: 'Sección B',
-    time: '14:00 - 16:00',
-    room: 'Lab 201',
-    students: 20
+  if (error) {
+    return (
+      <MainLayout 
+        title="Dashboard Profesor" 
+        subtitle="Gestión de clases y asistencia"
+        requiredRole="TEACHER"
+      >
+        <div className="flex items-center justify-center h-64">
+          <div className="text-center">
+            <p className="text-red-600 mb-2">Error al cargar los datos</p>
+            <p className="text-sm text-muted-foreground">{error}</p>
+          </div>
+        </div>
+      </MainLayout>
+    )
   }
-]
 
-const recentAttendance = [
-  {
-    id: 1,
-    class: 'Matemáticas I - Sección A',
-    date: '2024-01-20',
-    present: 23,
-    total: 25,
-    percentage: 92
-  },
-  {
-    id: 2,
-    class: 'Programación I - Sección B',
-    date: '2024-01-19',
-    present: 18,
-    total: 20,
-    percentage: 90
-  },
-  {
-    id: 3,
-    class: 'Física I - Sección A',
-    date: '2024-01-18',
-    present: 20,
-    total: 22,
-    percentage: 91
+  if (!dashboardData) {
+    return (
+      <MainLayout 
+        title="Dashboard Profesor" 
+        subtitle="Gestión de clases y asistencia"
+        requiredRole="TEACHER"
+      >
+        <div className="flex items-center justify-center h-64">
+          <p className="text-muted-foreground">No hay datos disponibles</p>
+        </div>
+      </MainLayout>
+    )
   }
-]
 
-export default function TeacherDashboard() {
   return (
     <MainLayout 
       title="Dashboard Profesor" 
@@ -132,22 +157,28 @@ export default function TeacherDashboard() {
             </CardHeader>
             <CardContent>
               <div className="space-y-4">
-                {todayClasses.map((classItem) => (
-                  <div key={classItem.id} className="flex items-center justify-between p-3 border rounded-lg">
-                    <div className="space-y-1">
-                      <p className="text-sm font-medium">{classItem.name}</p>
-                      <p className="text-xs text-muted-foreground">
-                        {classItem.section} • {classItem.room}
-                      </p>
-                    </div>
-                    <div className="text-right">
-                      <p className="text-sm font-medium">{classItem.time}</p>
-                      <p className="text-xs text-muted-foreground">
-                        {classItem.students} estudiantes
-                      </p>
+                {dashboardData.todayClasses.length > 0 ? (
+                  dashboardData.todayClasses.map((classItem) => (
+                    <div key={classItem.id} className="flex items-center justify-between p-3 border rounded-lg">
+                      <div className="space-y-1">
+                        <p className="text-sm font-medium">{classItem.name}</p>
+                        <p className="text-xs text-muted-foreground">
+                          {classItem.section} • {classItem.room}
+                        </p>
+                      </div>
+                      <div className="text-right">
+                        {/*<p className="text-sm font-medium">{classItem.time}</p>*/}
+                        <p className="text-xs text-muted-foreground">
+                          {classItem.students} estudiantes
+                        </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>
@@ -165,28 +196,34 @@ export default function TeacherDashboard() {
             </CardHeader>
             <CardContent>
               <div className="space-y-4">
-                {recentAttendance.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}</p>
+                {dashboardData.recentAttendance.length > 0 ? (
+                  dashboardData.recentAttendance.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}</p>
+                      </div>
+                      <div className="text-right">
+                        <p className="text-sm font-medium">
+                          {record.present}/{record.total}
+                        </p>
+                        <p className={`text-xs font-medium ${
+                          record.percentage >= 90 
+                            ? 'text-green-600' 
+                            : record.percentage >= 80 
+                            ? 'text-yellow-600' 
+                            : 'text-red-600'
+                        }`}>
+                          {record.percentage}%
+                        </p>
+                      </div>
                     </div>
-                    <div className="text-right">
-                      <p className="text-sm font-medium">
-                        {record.present}/{record.total}
-                      </p>
-                      <p className={`text-xs font-medium ${
-                        record.percentage >= 90 
-                          ? 'text-green-600' 
-                          : record.percentage >= 80 
-                          ? 'text-yellow-600' 
-                          : 'text-red-600'
-                      }`}>
-                        {record.percentage}%
-                      </p>
-                    </div>
-                  </div>
-                ))}
+                  ))
+                ) : (
+                  <p className="text-sm text-muted-foreground text-center py-4">
+                    No hay registros de asistencia recientes
+                  </p>
+                )}
               </div>
             </CardContent>
           </Card>

+ 30 - 0
src/types/teacher.ts

@@ -0,0 +1,30 @@
+export interface TeacherDashboardStats {
+  totalClasses: number;
+  totalStudents: number;
+  todayClasses: number;
+  averageAttendance: number;
+}
+
+export interface TodayClass {
+  id: string;
+  name: string;
+  section: string;
+  time: string;
+  room: string;
+  students: number;
+}
+
+export interface RecentAttendanceRecord {
+  id: string;
+  class: string;
+  date: string;
+  present: number;
+  total: number;
+  percentage: number;
+}
+
+export interface TeacherDashboardData {
+  stats: TeacherDashboardStats;
+  todayClasses: TodayClass[];
+  recentAttendance: RecentAttendanceRecord[];
+}