Files
lms-sb/src/app/(student)/dashboard/page.tsx
T

107 lines
4.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { headers } from "next/headers";
import { auth } from "@/lib/auth";
import { redirect } from "next/navigation";
import { prisma } from "@/lib/prisma";
import Link from "next/link";
export default async function StudentDashboard() {
const session = await auth.api.getSession({ headers: await headers() });
if (!session) redirect("/login");
const enrollments = await prisma.courseEnrollment.findMany({
where: { userId: session.user.id },
include: {
course: {
include: {
modules: {
include: { _count: { select: { lessons: true } } },
},
_count: { select: { modules: true } },
},
},
},
orderBy: { enrolledAt: "desc" },
});
const now = new Date();
const active = enrollments.filter((e) => !e.expiresAt || e.expiresAt > now);
const expired = enrollments.filter((e) => e.expiresAt && e.expiresAt <= now);
return (
<main className="max-w-4xl mx-auto px-6 py-10 w-full">
<h1 className="text-2xl font-bold mb-1">Мои курсы</h1>
<p className="text-sm mb-8" style={{ color: "var(--muted-foreground)" }}>
{active.length} активных курсов
</p>
{active.length === 0 ? (
<div className="card-aubade p-12 text-center">
<p className="text-4xl mb-3">📚</p>
<p className="font-medium">Доступных курсов пока нет</p>
<p className="text-sm mt-1" style={{ color: "var(--muted-foreground)" }}>
Обратитесь к администратору за доступом
</p>
</div>
) : (
<div className="grid gap-4 sm:grid-cols-2">
{active.map(({ course, expiresAt }) => {
const totalLessons = course.modules.reduce((s, m) => s + m._count.lessons, 0);
return (
<Link
key={course.id}
href={`/courses/${course.slug}`}
className="card-aubade p-0 overflow-hidden flex flex-col"
>
{course.coverImage ? (
// eslint-disable-next-line @next/next/no-img-element
<img src={course.coverImage} alt={course.title} className="w-full aspect-video object-cover" />
) : (
<div className="w-full aspect-video flex items-center justify-center text-4xl" style={{ backgroundColor: "var(--color-surface)" }}>
📚
</div>
)}
<div className="p-4 flex-1">
<h2 className="font-bold text-base leading-tight">{course.title}</h2>
{course.description && (
<p className="text-xs mt-1 line-clamp-2" style={{ color: "var(--muted-foreground)" }}>
{course.description}
</p>
)}
<div className="flex items-center justify-between mt-3">
<span className="text-xs" style={{ color: "var(--muted-foreground)" }}>
{course._count.modules} модулей · {totalLessons} уроков
</span>
{expiresAt && (
<span className="text-xs" style={{ color: "var(--muted-foreground)" }}>
до {new Date(expiresAt).toLocaleDateString("ru-RU")}
</span>
)}
</div>
</div>
</Link>
);
})}
</div>
)}
{expired.length > 0 && (
<div className="mt-10">
<p className="text-xs font-bold uppercase tracking-widest mb-3" style={{ color: "var(--muted-foreground)" }}>
Доступ истёк
</p>
<div className="space-y-2">
{expired.map(({ course, expiresAt }) => (
<div key={course.id} className="flex items-center justify-between px-4 py-3 opacity-50" style={{ border: "2px solid var(--border)" }}>
<span className="text-sm font-medium">{course.title}</span>
<span className="text-xs" style={{ color: "oklch(0.577 0.245 27.325)" }}>
Истёк {new Date(expiresAt!).toLocaleDateString("ru-RU")}
</span>
</div>
))}
</div>
</div>
)}
</main>
);
}