page.tsx 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. import { getServerSession } from 'next-auth'
  2. import { authOptions } from '@/lib/auth'
  3. import { redirect } from 'next/navigation'
  4. import { db } from '@/lib/db'
  5. import { teacherAssignments, sections, classes, periods, studentEnrollments, attendance, eq, and } from '@/lib/db/schema'
  6. import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
  7. import { BookOpen, Users, ClipboardCheck, Calendar } from 'lucide-react'
  8. import { DashboardLayout } from '@/components/dashboard-layout'
  9. interface TeacherStats {
  10. totalSections: number
  11. totalStudents: number
  12. attendanceRecords: number
  13. activePeriods: number
  14. }
  15. interface AssignedSection {
  16. id: string
  17. name: string
  18. className: string
  19. periodName: string
  20. studentCount: number
  21. maxStudents: number
  22. }
  23. async function getTeacherStats(teacherId: string): Promise<TeacherStats> {
  24. // Get teacher's assigned sections
  25. const assignedSections = await db
  26. .select({ sectionId: teacherAssignments.sectionId })
  27. .from(teacherAssignments)
  28. .where(eq(teacherAssignments.teacherId, teacherId))
  29. const sectionIds = assignedSections.map(a => a.sectionId)
  30. if (sectionIds.length === 0) {
  31. return {
  32. totalSections: 0,
  33. totalStudents: 0,
  34. attendanceRecords: 0,
  35. activePeriods: 0
  36. }
  37. }
  38. // Count total students enrolled in teacher's sections
  39. const studentCount = await db
  40. .select()
  41. .from(studentEnrollments)
  42. .where(eq(studentEnrollments.sectionId, sectionIds[0] as string)) // This is a simplified query
  43. // Count attendance records for teacher's sections
  44. const attendanceCount = await db
  45. .select()
  46. .from(attendance)
  47. .where(eq(attendance.sectionId, sectionIds[0] as string)) // This is a simplified query
  48. // Count active periods
  49. const activePeriods = await db
  50. .select()
  51. .from(periods)
  52. .where(eq(periods.isActive, true))
  53. return {
  54. totalSections: sectionIds.length,
  55. totalStudents: studentCount.length,
  56. attendanceRecords: attendanceCount.length,
  57. activePeriods: activePeriods.length
  58. }
  59. }
  60. async function getAssignedSections(teacherId: string): Promise<AssignedSection[]> {
  61. const result = await db
  62. .select({
  63. sectionId: sections.id,
  64. sectionName: sections.name,
  65. className: classes.name,
  66. periodName: periods.name,
  67. maxStudents: sections.maxStudents
  68. })
  69. .from(teacherAssignments)
  70. .innerJoin(sections, eq(teacherAssignments.sectionId, sections.id))
  71. .innerJoin(classes, eq(sections.classId, classes.id))
  72. .innerJoin(periods, eq(classes.periodId, periods.id))
  73. .where(eq(teacherAssignments.teacherId, teacherId))
  74. // Get student count for each section
  75. const sectionsWithStudents = await Promise.all(
  76. result.map(async (section) => {
  77. const studentCount = await db
  78. .select()
  79. .from(studentEnrollments)
  80. .where(eq(studentEnrollments.sectionId, section.sectionId))
  81. return {
  82. id: section.sectionId,
  83. name: section.sectionName,
  84. className: section.className,
  85. periodName: section.periodName,
  86. studentCount: studentCount.length,
  87. maxStudents: section.maxStudents || 0
  88. }
  89. })
  90. )
  91. return sectionsWithStudents
  92. }
  93. export default async function TeacherDashboard() {
  94. const session = await getServerSession(authOptions)
  95. if (!session || session.user.role !== 'teacher') {
  96. redirect('/auth/signin')
  97. }
  98. const stats = await getTeacherStats(session.user.id)
  99. const assignedSections = await getAssignedSections(session.user.id)
  100. const breadcrumbs = [
  101. { label: "Dashboard" }
  102. ]
  103. return (
  104. <DashboardLayout breadcrumbs={breadcrumbs}>
  105. <div className="space-y-6">
  106. <div>
  107. <h1 className="text-2xl font-bold text-gray-900">
  108. Bienvenido, {session.user.firstName} {session.user.lastName}
  109. </h1>
  110. <p className="text-gray-600">
  111. Gestiona tus clases y estudiantes desde aquí
  112. </p>
  113. </div>
  114. {/* Stats Cards */}
  115. <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
  116. <Card>
  117. <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
  118. <CardTitle className="text-sm font-medium">
  119. Secciones Asignadas
  120. </CardTitle>
  121. <BookOpen className="h-4 w-4 text-muted-foreground" />
  122. </CardHeader>
  123. <CardContent>
  124. <div className="text-2xl font-bold">{stats.totalSections}</div>
  125. </CardContent>
  126. </Card>
  127. <Card>
  128. <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
  129. <CardTitle className="text-sm font-medium">
  130. Total Estudiantes
  131. </CardTitle>
  132. <Users className="h-4 w-4 text-muted-foreground" />
  133. </CardHeader>
  134. <CardContent>
  135. <div className="text-2xl font-bold">{stats.totalStudents}</div>
  136. </CardContent>
  137. </Card>
  138. <Card>
  139. <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
  140. <CardTitle className="text-sm font-medium">
  141. Registros de Asistencia
  142. </CardTitle>
  143. <ClipboardCheck className="h-4 w-4 text-muted-foreground" />
  144. </CardHeader>
  145. <CardContent>
  146. <div className="text-2xl font-bold">{stats.attendanceRecords}</div>
  147. </CardContent>
  148. </Card>
  149. <Card>
  150. <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
  151. <CardTitle className="text-sm font-medium">
  152. Períodos Activos
  153. </CardTitle>
  154. <Calendar className="h-4 w-4 text-muted-foreground" />
  155. </CardHeader>
  156. <CardContent>
  157. <div className="text-2xl font-bold">{stats.activePeriods}</div>
  158. </CardContent>
  159. </Card>
  160. </div>
  161. {/* Assigned Sections */}
  162. <Card>
  163. <CardHeader>
  164. <CardTitle>Mis Secciones</CardTitle>
  165. </CardHeader>
  166. <CardContent>
  167. {assignedSections.length === 0 ? (
  168. <p className="text-gray-500 text-center py-8">
  169. No tienes secciones asignadas actualmente.
  170. </p>
  171. ) : (
  172. <div className="space-y-4">
  173. {assignedSections.map((section) => (
  174. <div
  175. key={section.id}
  176. className="flex items-center justify-between p-4 border rounded-lg hover:bg-gray-50"
  177. >
  178. <div>
  179. <h3 className="font-medium">
  180. {section.className} - {section.name}
  181. </h3>
  182. <p className="text-sm text-gray-600">
  183. Período: {section.periodName}
  184. </p>
  185. </div>
  186. <div className="text-right">
  187. <p className="text-sm font-medium">
  188. {section.studentCount}/{section.maxStudents} estudiantes
  189. </p>
  190. <div className="w-24 bg-gray-200 rounded-full h-2 mt-1">
  191. <div
  192. className="bg-blue-600 h-2 rounded-full"
  193. style={{
  194. width: `${Math.min(
  195. (section.studentCount / section.maxStudents) * 100,
  196. 100
  197. )}%`
  198. }}
  199. ></div>
  200. </div>
  201. </div>
  202. </div>
  203. ))}
  204. </div>
  205. )}
  206. </CardContent>
  207. </Card>
  208. </div>
  209. </DashboardLayout>
  210. )
  211. }