67 lines
2.1 KiB
TypeScript
67 lines
2.1 KiB
TypeScript
import Link from "next/link";
|
|
import type { Category, Course, CourseCategory } from "@prisma/client";
|
|
import { formatMoney, billingLabel } from "@/lib/format";
|
|
import { firstLessonPath } from "@/lib/course-queries";
|
|
|
|
type CourseWithCats = Course & {
|
|
categories: (CourseCategory & { category: Category })[];
|
|
modules: { lessons: { slug: string }[] }[];
|
|
};
|
|
|
|
export function CourseCard({
|
|
course,
|
|
enrolled,
|
|
}: {
|
|
course: CourseWithCats;
|
|
enrolled: boolean;
|
|
}) {
|
|
const cats = course.categories.map((c) => c.category.name).join(", ");
|
|
const first = firstLessonPath(course);
|
|
const isFree = course.priceCents === 0;
|
|
const priceSuffix = billingLabel(course.billingInterval);
|
|
|
|
return (
|
|
<article className="course-card">
|
|
<div className="course-card-body">
|
|
<h3>{course.title}</h3>
|
|
<p className="course-meta">
|
|
Von <strong>{course.authorName}</strong>
|
|
{cats ? <> · {cats}</> : null}
|
|
</p>
|
|
<div className="course-rating" aria-label="Bewertung">
|
|
{course.ratingCount > 0 ? (
|
|
<>
|
|
<span className="stars">★</span> {course.ratingAverage.toFixed(2)} ({course.ratingCount})
|
|
</>
|
|
) : (
|
|
<span className="muted">Noch keine Bewertungen</span>
|
|
)}
|
|
</div>
|
|
{!isFree && (
|
|
<p className="course-price">
|
|
{formatMoney(course.priceCents, course.currency)}
|
|
{priceSuffix ? ` ${priceSuffix}` : ""}
|
|
</p>
|
|
)}
|
|
</div>
|
|
<div className="course-card-actions">
|
|
{enrolled && first ? (
|
|
<Link href={first} className="btn btn-primary">
|
|
Mit dem Lernen beginnen
|
|
</Link>
|
|
) : enrolled ? (
|
|
<span className="muted">Keine Lektionen</span>
|
|
) : isFree ? (
|
|
<Link href={`/kurse/${course.slug}`} className="btn btn-primary">
|
|
In diesen Kurs einschreiben
|
|
</Link>
|
|
) : (
|
|
<Link href={`/kurse/${course.slug}`} className="btn btn-primary">
|
|
Details & Kauf
|
|
</Link>
|
|
)}
|
|
</div>
|
|
</article>
|
|
);
|
|
}
|