Pending course, rest ready for launch

This commit is contained in:
Marcelo
2026-03-15 13:52:11 +00:00
parent be4ca2ed78
commit 62b3cfe467
77 changed files with 6450 additions and 868 deletions

View File

@@ -1,93 +1,35 @@
import CourseCard from "@/components/CourseCard";
import { db } from "@/lib/prisma";
import { requireUser } from "@/lib/auth/requireUser";
import CourseCatalogIntro from "@/components/courses/CourseCatalogIntro";
import CourseLevelTabs from "@/components/courses/CourseLevelTabs";
import ProgramSection from "@/components/courses/ProgramSection";
import { getCourseCatalogViewModel } from "@/lib/courses/publicCourses";
export const dynamic = "force-dynamic";
export default async function CoursesPage() {
const courses = await db.course.findMany({
where: {
status: "PUBLISHED",
},
include: {
author: {
select: {
fullName: true,
},
},
modules: {
select: {
_count: {
select: {
lessons: true,
},
},
},
},
_count: {
select: {
enrollments: true,
},
},
},
orderBy: {
updatedAt: "desc",
},
});
const totalLessons = courses.reduce(
(total, course) => total + course.modules.reduce((courseTotal, module) => courseTotal + module._count.lessons, 0),
0,
);
const user = await requireUser().catch(() => null);
const catalog = await getCourseCatalogViewModel(user?.id ?? null);
return (
<div className="acve-page">
<section className="acve-panel overflow-hidden p-0">
<div className="grid gap-0 lg:grid-cols-[1.45fr_0.95fr]">
<div className="acve-section-base">
<p className="acve-pill mb-4 w-fit">Course Catalog</p>
<h1 className="text-3xl font-semibold leading-tight text-[#202a39] md:text-4xl">Build your legal English learning path</h1>
<p className="mt-3 max-w-2xl text-base leading-relaxed text-slate-600 md:text-lg">
Discover our published legal English courses and start with the path that matches your level.
</p>
<div className="mt-5 grid gap-3 sm:grid-cols-3">
<div className="rounded-xl border border-slate-200 bg-white p-3">
<p className="text-xs font-semibold uppercase tracking-wide text-slate-500">Total Courses</p>
<p className="mt-1 text-2xl font-semibold text-slate-900">{courses.length}</p>
</div>
<div className="rounded-xl border border-slate-200 bg-white p-3">
<p className="text-xs font-semibold uppercase tracking-wide text-slate-500">Published Lessons</p>
<p className="mt-1 text-2xl font-semibold text-slate-900">{totalLessons}</p>
</div>
<div className="rounded-xl border border-slate-200 bg-white p-3">
<p className="text-xs font-semibold uppercase tracking-wide text-slate-500">Instructors</p>
<p className="mt-1 text-2xl font-semibold text-slate-900">
{new Set(courses.map((course) => course.author.fullName || "ACVE Team")).size}
</p>
</div>
</div>
</div>
<CourseCatalogIntro
instructorCount={catalog.totals.instructorCount}
totalCourses={catalog.totals.totalCourses}
totalLessons={catalog.totals.totalLessons}
/>
<aside className="border-t border-slate-200 bg-slate-50/80 p-5 lg:border-l lg:border-t-0">
<h2 className="text-base font-semibold text-slate-800">Open Access</h2>
<p className="mt-2 text-sm text-slate-600">
This catalog is public. Open any course card to visit the landing page and enroll.
</p>
</aside>
</div>
</section>
<CourseLevelTabs
items={catalog.sections.map((section) => ({
id: section.id,
label: section.tabLabel,
anchorId: section.anchorId,
count: section.courses.length,
}))}
/>
{courses.length === 0 ? (
<section className="acve-panel acve-section-base">
<div className="rounded-xl border border-dashed border-slate-300 bg-slate-50 px-6 py-12 text-center">
<h2 className="text-2xl font-semibold text-slate-900">Coming Soon</h2>
<p className="mt-2 text-slate-600">We are preparing new courses. Please check back shortly.</p>
</div>
</section>
) : (
<div className="grid gap-4 md:grid-cols-2 xl:grid-cols-3">
{courses.map((course) => (
<CourseCard key={course.slug} course={course} />
))}
</div>
)}
{catalog.sections.map((section) => (
<ProgramSection key={section.id} section={section} />
))}
</div>
);
}