diff --git a/pages/class-analysis/index.tsx b/pages/class-analysis/index.tsx new file mode 100644 index 0000000..07871b6 --- /dev/null +++ b/pages/class-analysis/index.tsx @@ -0,0 +1,96 @@ +import { GetServerSideProps } from "next"; +import { getServerSession } from "next-auth/next"; +import { authOptions } from "../../lib/authOptions"; +import { prisma } from "../../lib/prisma"; +import Link from "next/link"; +import { ArrowLeft, ArrowRight, BarChart3, BookOpen, CalendarDays, LayoutDashboard, LogOut, UserRoundPlus } from "lucide-react"; +import { signOut } from "next-auth/react"; + +type ClassOption = { id: string; label: string }; +type Props = { classes: ClassOption[]; teacherName: string }; + +function formatClassLabel(department: string, semester: number, classNumber: string) { + return `${department}-${classNumber} Sem ${semester}`; +} + +export default function ClassAnalysisIndex({ classes, teacherName }: Props) { + const initials = teacherName.split(" ").filter(Boolean).slice(0, 2).map((part) => part[0]?.toUpperCase()).join("") || "T"; + + return ( +
+ + +
+
+
+
+ Back to Dashboard +
+

Class Analysis

+

Choose a class

+

Select a class assigned to you to open its analysis.

+
+ +
+
+ {classes.map((classOption) => ( + + + {classOption.label}Open attendance, academic and student analysis + + + ))} + {!classes.length &&
No classes are assigned to you.
} +
+
+
+
+
+
+ ); +} + +export const getServerSideProps: GetServerSideProps = async (ctx) => { + const session = await getServerSession(ctx.req, ctx.res, authOptions); + if (!session?.user) return { redirect: { destination: "/login", permanent: false } }; + + const userId = (session.user as any).id; + const role = (session.user as any).role; + const sections = await prisma.section.findMany({ + where: role === "ADMIN" ? undefined : { + OR: [ + { class: { proctorId: userId } }, + { subjects: { some: { assignments: { some: { teacherId: userId } } } } }, + ], + }, + include: { class: { include: { department: true } } }, + }); + + const classes = new Map(); + for (const section of sections) { + classes.set(section.class.id, { id: section.class.id, label: formatClassLabel(section.class.department.name, section.class.semester, section.name) }); + } + + return { props: { classes: Array.from(classes.values()), teacherName: session.user.name || session.user.email } }; +}; diff --git a/pages/dashboard.tsx b/pages/dashboard.tsx index 2229489..a42adf2 100644 --- a/pages/dashboard.tsx +++ b/pages/dashboard.tsx @@ -28,34 +28,10 @@ interface Props { } const quickActions = [ - { - title: "Attendance Agent", - description: "Take attendance for your current class", - href: "/attendance-agent", - icon: UserRoundPlus, - tone: "bg-[#eef1ff] text-[#4b6bff]", - }, - { - title: "Timetable", - description: "View your full class schedule", - href: "/timetable", - icon: CalendarDays, - tone: "bg-[#eef7ff] text-[#4388d8]", - }, - { - title: "Class Analysis", - description: "Analyze performance of a class/section", - href: "/section-analysis", - icon: BarChart3, - tone: "bg-[#eaf9f1] text-[#159b62]", - }, - { - title: "Subject Analysis", - description: "Deep dive into a subject in a section", - href: "/subject-analysis", - icon: BookOpen, - tone: "bg-[#fff5e7] text-[#ee9412]", - }, + { title: "Attendance Agent", description: "Take attendance for your current class", href: "/attendance-agent", icon: UserRoundPlus, tone: "bg-[#eef1ff] text-[#4b6bff]" }, + { title: "Timetable", description: "View your full class schedule", href: "/timetable", icon: CalendarDays, tone: "bg-[#eef7ff] text-[#4388d8]" }, + { title: "Class Analysis", description: "Analyze performance of a class", href: "/class-analysis", icon: BarChart3, tone: "bg-[#eaf9f1] text-[#159b62]" }, + { title: "Subject Analysis", description: "Deep dive into a subject", href: "/subject-analysis", icon: BookOpen, tone: "bg-[#fff5e7] text-[#ee9412]" }, ]; const todaySchedule = [ @@ -73,231 +49,43 @@ const recentActivity = [ { icon: CalendarDays, tone: "text-[#4388d8] bg-[#eef7ff]", text: "Timetable updated", time: "Aug 27, 3:45 PM" }, ]; -export default function Dashboard({ teacherName, sections, subjects }: Props) { - const initials = teacherName - .split(" ") - .filter(Boolean) - .slice(0, 2) - .map((part) => part[0]?.toUpperCase()) - .join(""); +export default function Dashboard({ teacherName }: Props) { + const initials = teacherName.split(" ").filter(Boolean).slice(0, 2).map((part) => part[0]?.toUpperCase()).join(""); return (
- -
-
-
-
-
- - ClassPulse -
-

Welcome back, {teacherName}

-

Here's an overview of your day.

-
-
- -
- - {new Date().toLocaleDateString("en-IN", { day: "numeric", month: "short", year: "numeric" })} -
- -
-
- -
-
-

Quick Actions

-

Jump straight into the tools you use most.

-
-
- {quickActions.map((action) => { - const Icon = action.icon; - const href = action.title === "Class Analysis" - ? sections[0]?.id ? `/section-analysis/${sections[0].id}` : "/section-analysis" - : action.title === "Subject Analysis" - ? "/subject-analysis" - : action.href; - - return ( - - - - {action.title} - {action.description} - - - - ); - })} -
-
- -
-
-
-
-

Today's Timetable

-

Your scheduled classes for today.

-
- View full timetable -
-
- {todaySchedule.map((item, index) => ( -
-
- {item.time} - {item.end} -
-
- - {index < todaySchedule.length - 1 && } -

{item.subject}

-

{item.section} {item.room}

- {item.active && Next class} -
-
- ))} -
-
- -
-
-
-

Recent Activity

-

Your latest updates and actions.

-
-
-
- {recentActivity.map((activity, index) => { - const Icon = activity.icon; - return ( -
- -

{activity.text}

- {activity.time} -
- ); - })} -
-
-
- -
© 2026 ClassPulse. All rights reserved.
-
-
+
+
ClassPulse

Welcome back, {teacherName}

Here's an overview of your day.

{new Date().toLocaleDateString("en-IN", { day: "numeric", month: "short", year: "numeric" })}
+

Quick Actions

Jump straight into the tools you use most.

{quickActions.map((action) => { const Icon = action.icon; return {action.title}{action.description}; })}
+

Today's Timetable

Your scheduled classes for today.

View full timetable
{todaySchedule.map((item, index) =>
{item.time}{item.end}
{index < todaySchedule.length - 1 && }

{item.subject}

{item.section} {item.room}

{item.active && Next class}
)}

Recent Activity

Your latest activity.

{recentActivity.map((activity, index) => { const Icon = activity.icon; return

{activity.text}

{activity.time}
; })}
+
© 2026 ClassPulse. All rights reserved.
+
); } export const getServerSideProps: GetServerSideProps = async (ctx) => { const session = await getServerSession(ctx.req, ctx.res, authOptions); - if (!session?.user) { - return { redirect: { destination: "/login", permanent: false } }; - } - + if (!session?.user) return { redirect: { destination: "/login", permanent: false } }; const userId = (session.user as any).id; const role = (session.user as any).role; - - const sectionsRaw = - role === "ADMIN" - ? await prisma.section.findMany({ include: { class: { include: { department: true } } } }) - : await prisma.section.findMany({ - where: { - OR: [ - { class: { proctorId: userId } }, - { subjects: { some: { assignments: { some: { teacherId: userId } } } } }, - ], - }, - include: { class: { include: { department: true } } }, - }); - - const subjectsRaw = await prisma.subject.findMany({ - where: role === "ADMIN" ? {} : { assignments: { some: { teacherId: userId } } }, - include: { section: { include: { class: true } } }, - }); - - return { - props: { - teacherName: session.user.name || session.user.email, - sections: sectionsRaw.map((s) => ({ - id: s.id, - label: `${s.class.department.name} — ${s.class.program}, Sem ${s.class.semester}, Section ${s.name}`, - })), - subjects: subjectsRaw.map((s) => ({ - id: s.id, - label: `${s.name} (${s.code}) — Section ${s.section.name}`, - })), - }, - }; + const sectionsRaw = await prisma.section.findMany({ where: role === "ADMIN" ? undefined : { OR: [{ class: { proctorId: userId } }, { subjects: { some: { assignments: { some: { teacherId: userId } } } } }] }, include: { class: { include: { department: true } } } }); + const subjectsRaw = await prisma.subject.findMany({ where: role === "ADMIN" ? {} : { assignments: { some: { teacherId: userId } } }, include: { section: { include: { class: true } } } }); + const classMap = new Map(); + for (const section of sectionsRaw) classMap.set(section.class.id, { id: section.class.id, label: `${section.class.department.name}-${section.name} Sem ${section.class.semester}` }); + return { props: { teacherName: session.user.name || session.user.email, sections: Array.from(classMap.values()), subjects: subjectsRaw.map((subject) => ({ id: subject.id, label: `${subject.name} (${subject.code})` })) } }; }; diff --git a/pages/section-analysis/[sectionId].tsx b/pages/section-analysis/[sectionId].tsx index 93616a0..685a03f 100644 --- a/pages/section-analysis/[sectionId].tsx +++ b/pages/section-analysis/[sectionId].tsx @@ -1,18 +1,10 @@ import { GetServerSideProps } from "next"; -// The bare /section-analysis/[sectionId] URL isn't a page on its own — -// analysis lives under /attendance, /academic, /overall, /students. -// This just sends anyone landing here (dashboard links, bookmarks, etc.) -// straight to the default tab. -export const getServerSideProps: GetServerSideProps = async (ctx) => { - const { sectionId } = ctx.params as { sectionId: string }; - return { - redirect: { - destination: `/section-analysis/${sectionId}/attendance`, - permanent: false, - }, - }; -}; +// Keep legacy dashboard/bookmark URLs working, but always require +// class selection before opening Class Analysis. +export const getServerSideProps: GetServerSideProps = async () => ({ + redirect: { destination: "/class-analysis", permanent: false }, +}); export default function SectionAnalysisRedirect() { return null; diff --git a/pages/section-analysis/index.tsx b/pages/section-analysis/index.tsx new file mode 100644 index 0000000..6ef2ca4 --- /dev/null +++ b/pages/section-analysis/index.tsx @@ -0,0 +1,9 @@ +import { GetServerSideProps } from "next"; + +export const getServerSideProps: GetServerSideProps = async () => ({ + redirect: { destination: "/class-analysis", permanent: false }, +}); + +export default function SectionAnalysisRedirect() { + return null; +} diff --git a/pages/subject-analysis/index.tsx b/pages/subject-analysis/index.tsx index e25b1dc..13dfe92 100644 --- a/pages/subject-analysis/index.tsx +++ b/pages/subject-analysis/index.tsx @@ -6,153 +6,40 @@ import Link from "next/link"; import { useState } from "react"; import { BookOpen, ArrowLeft, ArrowRight, BarChart3 } from "lucide-react"; -interface SubjectOption { - id: string; - sectionId: string; - name: string; - code: string; -} - -interface ClassOption { - id: string; - label: string; -} +interface SubjectOption { id: string; sectionId: string; name: string; code: string; } +interface ClassOption { id: string; label: string; } +interface Props { teacherName: string; classes: ClassOption[]; subjects: SubjectOption[]; } -interface Props { - teacherName: string; - classes: ClassOption[]; - subjects: SubjectOption[]; +function formatClassLabel(department: string, classNumber: string, semester: number) { + return `${department}-${classNumber} Sem ${semester}`; } export default function SubjectAnalysisChooser({ teacherName, classes, subjects }: Props) { const [selectedClass, setSelectedClass] = useState(classes[0]?.id || ""); const selectedSubjects = subjects.filter((subject) => subject.sectionId === selectedClass); - return (
- -
-
- - Back to Dashboard - - -
-

Subject Analysis

-

Choose a class and subject

-

- Select a class you are assigned to, then choose one of your assigned subjects for that class. -

-
- -
-
- - -
- -
-
- - {selectedSubjects.length} available -
- - {selectedSubjects.length === 0 ? ( -
- No subjects are assigned to you for this class. -
- ) : ( -
- {selectedSubjects.map((subject) => ( - - - - - - {subject.name} - {subject.code} - - - - ))} -
- )} -
-
- -

Signed in as {teacherName}. Only your assigned classes and subjects are shown.

-
-
+
+ Back to Dashboard +

Subject Analysis

Choose a class and subject

Select a class you are assigned to, then choose one of your assigned subjects for that class.

+
+
{selectedSubjects.length} available
{selectedSubjects.length === 0 ?
No subjects are assigned to you for this class.
:
{selectedSubjects.map((subject) => {subject.name}{subject.code})}
}
+

Signed in as {teacherName}. Only your assigned classes and subjects are shown.

+
); } export const getServerSideProps: GetServerSideProps = async (ctx) => { const session = await getServerSession(ctx.req, ctx.res, authOptions); - if (!session?.user) { - return { redirect: { destination: "/login", permanent: false } }; - } - - const userId = (session.user as any).id; - const role = (session.user as any).role; - - const subjectsRaw = await prisma.subject.findMany({ - where: role === "ADMIN" ? {} : { assignments: { some: { teacherId: userId } } }, - include: { - section: { include: { class: { include: { department: true } } } }, - }, - orderBy: [{ section: { class: { semester: "asc" } } }, { name: "asc" }], - }); - + if (!session?.user) return { redirect: { destination: "/login", permanent: false } }; + const userId = (session.user as any).id; const role = (session.user as any).role; + const subjectsRaw = await prisma.subject.findMany({ where: role === "ADMIN" ? {} : { assignments: { some: { teacherId: userId } } }, include: { section: { include: { class: { include: { department: true } } } } }, orderBy: [{ section: { class: { semester: "asc" } } }, { name: "asc" }] }); const classMap = new Map(); - for (const subject of subjectsRaw) { - const section = subject.section; - const sectionClass = section.class; - classMap.set(section.id, { - id: section.id, - label: `${sectionClass.department.name} — ${sectionClass.program}, Sem ${sectionClass.semester}, Section ${section.name}`, - }); - } - - return { - props: { - teacherName: session.user.name || session.user.email, - classes: Array.from(classMap.values()), - subjects: subjectsRaw.map((subject) => ({ - id: subject.id, - sectionId: subject.section.id, - name: subject.name, - code: subject.code, - })), - }, - }; + for (const subject of subjectsRaw) { const c = subject.section.class; classMap.set(c.id, { id: c.id, label: formatClassLabel(c.department.name, subject.section.name, c.semester) }); } + return { props: { teacherName: session.user.name || session.user.email, classes: Array.from(classMap.values()), subjects: subjectsRaw.map((subject) => ({ id: subject.id, sectionId: subject.section.class.id, name: subject.name, code: subject.code })) } }; };