Files
lms-sb/src/app/(student)/courses/[slug]/layout.tsx
T
admins 47840901c5 feat: collapsible mobile sidebar for student course view
Hamburger button (top-left, lg:hidden), dark overlay, slide-in animation.
Sidebar closes on lesson link click. Spacer added to prevent content overlap.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 05:07:29 +00:00

71 lines
2.0 KiB
TypeScript

import { headers } from "next/headers";
import { auth } from "@/lib/auth";
import { redirect } from "next/navigation";
import { prisma } from "@/lib/prisma";
import { notFound } from "next/navigation";
import { CourseSidebar } from "@/components/student/course-sidebar";
interface Props {
children: React.ReactNode;
params: Promise<{ slug: string }>;
}
export default async function CourseLayout({ children, params }: Props) {
const { slug } = await params;
const session = await auth.api.getSession({ headers: await headers() });
if (!session) redirect("/login");
const isAdmin = session.user.role === "admin";
const course = await prisma.course.findUnique({
where: { slug, ...(isAdmin ? {} : { published: true }) },
include: {
modules: {
orderBy: { order: "asc" },
include: {
lessons: {
where: { published: true },
orderBy: { order: "asc" },
select: { id: true, title: true },
},
},
},
},
});
if (!course) notFound();
if (!isAdmin) {
const enrollment = await prisma.courseEnrollment.findUnique({
where: { userId_courseId: { userId: session.user.id, courseId: course.id } },
});
if (!enrollment) redirect("/dashboard");
if (enrollment.expiresAt && enrollment.expiresAt < new Date()) {
redirect("/dashboard?expired=1");
}
}
// Fetch completed lesson IDs for this user
const allLessonIds = course.modules.flatMap((m) => m.lessons.map((l) => l.id));
const progressRecords = isAdmin
? []
: await prisma.lessonProgress.findMany({
where: { userId: session.user.id, lessonId: { in: allLessonIds } },
select: { lessonId: true },
});
const completedLessonIds = new Set(progressRecords.map((p) => p.lessonId));
return (
<div className="flex flex-1">
<CourseSidebar course={course} completedLessonIds={completedLessonIds} />
<main className="flex-1 min-w-0 overflow-y-auto">
<div className="h-12 lg:hidden" />
{children}
</main>
</div>
);
}