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 (
-
-
-
-
-
-
-
-
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}
-
- );
- })}
-
-
-
-
-
-
-
+
+
+
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}; })}
+
+
);
}
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 })) } };
};