131 lines
3.3 KiB
TypeScript
Executable File
131 lines
3.3 KiB
TypeScript
Executable File
import { notFound, redirect } from "next/navigation";
|
|
import { db } from "@/lib/prisma";
|
|
import { requireUser } from "@/lib/auth/requireUser";
|
|
import StudentClassroomClient from "@/components/courses/StudentClassroomClient";
|
|
|
|
function getText(value: unknown): string {
|
|
if (!value) return "";
|
|
if (typeof value === "string") return value;
|
|
if (typeof value === "object") {
|
|
const record = value as Record<string, unknown>;
|
|
if (typeof record.es === "string") return record.es;
|
|
if (typeof record.en === "string") return record.en;
|
|
}
|
|
return "";
|
|
}
|
|
|
|
type PageProps = {
|
|
params: Promise<{ slug: string }>;
|
|
searchParams: Promise<{ lesson?: string }>;
|
|
};
|
|
|
|
export default async function CourseLearnPage({ params, searchParams }: PageProps) {
|
|
const { slug } = await params;
|
|
const { lesson: requestedLessonId } = await searchParams;
|
|
|
|
const user = await requireUser();
|
|
if (!user?.id) {
|
|
redirect(`/courses/${slug}`);
|
|
}
|
|
|
|
const course = await db.course.findUnique({
|
|
where: { slug },
|
|
select: {
|
|
id: true,
|
|
slug: true,
|
|
title: true,
|
|
price: true,
|
|
modules: {
|
|
orderBy: { orderIndex: "asc" },
|
|
select: {
|
|
id: true,
|
|
title: true,
|
|
lessons: {
|
|
orderBy: { orderIndex: "asc" },
|
|
select: {
|
|
id: true,
|
|
title: true,
|
|
description: true,
|
|
videoUrl: true,
|
|
estimatedDuration: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
if (!course) {
|
|
notFound();
|
|
}
|
|
|
|
let enrollment = await db.enrollment.findUnique({
|
|
where: {
|
|
userId_courseId: {
|
|
userId: user.id,
|
|
courseId: course.id,
|
|
},
|
|
},
|
|
select: { id: true },
|
|
});
|
|
|
|
if (!enrollment) {
|
|
const isFree = Number(course.price) === 0;
|
|
if (isFree) {
|
|
enrollment = await db.enrollment.create({
|
|
data: {
|
|
userId: user.id,
|
|
courseId: course.id,
|
|
amountPaid: 0,
|
|
},
|
|
select: { id: true },
|
|
});
|
|
} else {
|
|
redirect(`/courses/${slug}`);
|
|
}
|
|
}
|
|
|
|
const completedProgress = await db.userProgress.findMany({
|
|
where: {
|
|
userId: user.id,
|
|
isCompleted: true,
|
|
lesson: {
|
|
module: {
|
|
courseId: course.id,
|
|
},
|
|
},
|
|
},
|
|
select: {
|
|
lessonId: true,
|
|
},
|
|
});
|
|
|
|
const modules = course.modules.map((module) => ({
|
|
id: module.id,
|
|
title: getText(module.title) || "Untitled module",
|
|
lessons: module.lessons.map((lesson) => ({
|
|
id: lesson.id,
|
|
title: getText(lesson.title) || "Untitled lesson",
|
|
description: getText(lesson.description),
|
|
videoUrl: lesson.videoUrl,
|
|
estimatedDuration: lesson.estimatedDuration,
|
|
})),
|
|
}));
|
|
|
|
const flattenedLessonIds = modules.flatMap((module) => module.lessons.map((lesson) => lesson.id));
|
|
const initialSelectedLessonId =
|
|
requestedLessonId && flattenedLessonIds.includes(requestedLessonId)
|
|
? requestedLessonId
|
|
: flattenedLessonIds[0] ?? "";
|
|
|
|
return (
|
|
<StudentClassroomClient
|
|
courseSlug={course.slug}
|
|
courseTitle={getText(course.title) || "Untitled course"}
|
|
modules={modules}
|
|
initialSelectedLessonId={initialSelectedLessonId}
|
|
initialCompletedLessonIds={completedProgress.map((progress) => progress.lessonId)}
|
|
/>
|
|
);
|
|
}
|