160 lines
6.6 KiB
TypeScript
160 lines
6.6 KiB
TypeScript
import { auth } from "@/auth";
|
|
import { db } from "@/lib/db";
|
|
import { setRequestLocale } from "next-intl/server";
|
|
import Link from "next/link";
|
|
import { EnrollPathButton } from "./EnrollPathButton";
|
|
|
|
export const dynamic = "force-dynamic";
|
|
|
|
function getLocaleText(obj: any, locale: string, field: string) {
|
|
if (locale === "en") return obj[`${field}En`] || obj[`${field}Fr`];
|
|
if (locale === "es") return obj[`${field}Es`] || obj[`${field}Fr`];
|
|
return obj[`${field}Fr`];
|
|
}
|
|
|
|
const levelColors: Record<string, string> = {
|
|
BEGINNER: "#4ade80",
|
|
INTERMEDIATE: "#fbbf24",
|
|
ADVANCED: "#f87171",
|
|
};
|
|
|
|
export default async function PathsPage({
|
|
params,
|
|
}: {
|
|
params: Promise<{ locale: string }>;
|
|
}) {
|
|
const { locale } = await params;
|
|
setRequestLocale(locale);
|
|
const session = await auth();
|
|
const userId = (session?.user as any)?.id as string | undefined;
|
|
|
|
const paths = await db.learningPath.findMany({
|
|
where: { published: true },
|
|
orderBy: { order: "asc" },
|
|
include: {
|
|
courses: {
|
|
include: {
|
|
course: {
|
|
include: { modules: { include: { lessons: { select: { id: true } } } } },
|
|
},
|
|
},
|
|
orderBy: { order: "asc" },
|
|
},
|
|
_count: { select: { enrollments: true } },
|
|
},
|
|
});
|
|
|
|
// Check which paths the user is enrolled in
|
|
const enrolledPathIds = userId
|
|
? new Set(
|
|
(await db.learningPathEnrollment.findMany({ where: { userId }, select: { learningPathId: true } })).map(
|
|
(e) => e.learningPathId
|
|
)
|
|
)
|
|
: new Set<string>();
|
|
|
|
return (
|
|
<div style={{ maxWidth: 1100, margin: "0 auto", padding: "40px 24px" }}>
|
|
<div style={{ marginBottom: 40 }}>
|
|
<h1 style={{ fontSize: 28, fontWeight: 800, color: "#f1f5f9", marginBottom: 8 }}>
|
|
Parcours de formation
|
|
</h1>
|
|
<p style={{ color: "#94a3b8", fontSize: 15 }}>
|
|
Des programmes complets pour maîtriser la GRC et l'application OwlCub.
|
|
</p>
|
|
</div>
|
|
|
|
{paths.length === 0 ? (
|
|
<div style={{ textAlign: "center", padding: "60px 24px", background: "#1a1f2e", borderRadius: 12, border: "1px solid rgba(255,255,255,0.08)" }}>
|
|
<p style={{ color: "#64748b", fontSize: 16 }}>Aucun parcours disponible pour l'instant.</p>
|
|
</div>
|
|
) : (
|
|
<div style={{ display: "flex", flexDirection: "column", gap: 24 }}>
|
|
{paths.map((path) => {
|
|
const title = getLocaleText(path, locale, "title");
|
|
const desc = getLocaleText(path, locale, "desc");
|
|
const totalLessons = path.courses.reduce((sum, lpc) => sum + lpc.course.modules.reduce((s, m) => s + m.lessons.length, 0), 0);
|
|
const isEnrolled = enrolledPathIds.has(path.id);
|
|
|
|
return (
|
|
<div
|
|
key={path.id}
|
|
className="card"
|
|
style={{
|
|
display: "grid",
|
|
gridTemplateColumns: "1fr auto",
|
|
gap: 24,
|
|
background: "linear-gradient(135deg, #1a1f2e, #1e2a4a)",
|
|
border: "1px solid rgba(29,78,216,0.25)",
|
|
}}
|
|
>
|
|
<div>
|
|
<div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 10 }}>
|
|
<h2 style={{ fontSize: 20, fontWeight: 700, color: "#f1f5f9" }}>{title}</h2>
|
|
{isEnrolled && (
|
|
<span style={{ fontSize: 11, fontWeight: 600, background: "rgba(34,197,94,0.15)", color: "#4ade80", border: "1px solid rgba(34,197,94,0.3)", borderRadius: 4, padding: "2px 8px" }}>
|
|
Inscrit
|
|
</span>
|
|
)}
|
|
</div>
|
|
<p style={{ color: "#94a3b8", fontSize: 14, marginBottom: 16, lineHeight: 1.6 }}>{desc}</p>
|
|
|
|
{/* Course list */}
|
|
<div style={{ display: "flex", flexDirection: "column", gap: 6, marginBottom: 16 }}>
|
|
{path.courses.map((lpc, i) => {
|
|
const courseTitle = getLocaleText(lpc.course, locale, "title");
|
|
const lessonCount = lpc.course.modules.reduce((s, m) => s + m.lessons.length, 0);
|
|
return (
|
|
<div key={lpc.id} style={{ display: "flex", alignItems: "center", gap: 10 }}>
|
|
<span style={{ width: 22, height: 22, background: "rgba(29,78,216,0.3)", color: "#60a5fa", borderRadius: 4, display: "flex", alignItems: "center", justifyContent: "center", fontSize: 11, fontWeight: 700, flexShrink: 0 }}>
|
|
{i + 1}
|
|
</span>
|
|
<span style={{ fontSize: 13, color: "#cbd5e1" }}>{courseTitle}</span>
|
|
<span style={{ fontSize: 11, color: "#475569" }}>· {lessonCount} leçon{lessonCount !== 1 ? "s" : ""}</span>
|
|
<span style={{ fontSize: 11, fontWeight: 600, color: levelColors[lpc.course.level] ?? "#94a3b8" }}>
|
|
{lpc.course.level}
|
|
</span>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
|
|
<div style={{ display: "flex", gap: 16, fontSize: 13, color: "#64748b" }}>
|
|
<span>📚 {path.courses.length} formation{path.courses.length !== 1 ? "s" : ""}</span>
|
|
<span>🎬 {totalLessons} leçons</span>
|
|
<span>👥 {path._count.enrollments} inscrits</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div style={{ display: "flex", flexDirection: "column", gap: 10, alignItems: "flex-end", justifyContent: "center", minWidth: 160 }}>
|
|
{isEnrolled ? (
|
|
<Link
|
|
href={`/${locale}/courses/${path.courses[0]?.course.slug ?? ""}/learn`}
|
|
className="btn btn-primary"
|
|
style={{ width: "100%", justifyContent: "center", fontSize: 14 }}
|
|
>
|
|
Continuer →
|
|
</Link>
|
|
) : (
|
|
<EnrollPathButton
|
|
pathId={path.id}
|
|
locale={locale}
|
|
isLoggedIn={!!session}
|
|
/>
|
|
)}
|
|
<Link
|
|
href={`/${locale}/paths/${path.slug}`}
|
|
style={{ fontSize: 13, color: "#60a5fa", textAlign: "center", width: "100%" }}
|
|
>
|
|
Voir le détail
|
|
</Link>
|
|
</div>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|