170 lines
5.1 KiB
TypeScript
Executable File
170 lines
5.1 KiB
TypeScript
Executable File
import { notFound } from "next/navigation";
|
|
import { requireUser } from "@/lib/auth/requireUser";
|
|
import CourseDetailHeader from "@/components/courses/CourseDetailHeader";
|
|
import CourseProgressCard from "@/components/courses/CourseProgressCard";
|
|
import ProgramContentList from "@/components/courses/ProgramContentList";
|
|
import { getCourseDetailViewModel } from "@/lib/courses/publicCourses";
|
|
|
|
export const dynamic = "force-dynamic";
|
|
|
|
type PageProps = {
|
|
params: Promise<{ slug: string }>;
|
|
};
|
|
|
|
type DetailActionState = {
|
|
primaryAction: {
|
|
label: string;
|
|
href?: string;
|
|
disabled?: boolean;
|
|
};
|
|
secondaryAction?: {
|
|
label: string;
|
|
href?: string;
|
|
disabled?: boolean;
|
|
};
|
|
helperText?: string;
|
|
};
|
|
|
|
function buildActionState(args: {
|
|
slug: string;
|
|
isAuthenticated: boolean;
|
|
isEnrolled: boolean;
|
|
availabilityState: "published" | "upcoming" | "draft";
|
|
progressPercent: number;
|
|
firstPreviewLessonId: string | null;
|
|
price: number;
|
|
}): DetailActionState {
|
|
const learnUrl = `/courses/${args.slug}/learn`;
|
|
const loginUrl = `/auth/login?redirectTo=${encodeURIComponent(`/courses/${args.slug}`)}`;
|
|
const previewUrl = args.firstPreviewLessonId ? `${learnUrl}?lesson=${args.firstPreviewLessonId}` : undefined;
|
|
|
|
if (args.availabilityState !== "published") {
|
|
return {
|
|
primaryAction: {
|
|
label: "Próximamente",
|
|
disabled: true,
|
|
},
|
|
helperText: "Este programa se encuentra en preparación editorial y estará habilitado en próximas publicaciones.",
|
|
};
|
|
}
|
|
|
|
if (args.isAuthenticated && args.isEnrolled) {
|
|
const label = args.progressPercent >= 100 ? "Revisar programa" : args.progressPercent > 0 ? "Continuar" : "Comenzar";
|
|
return {
|
|
primaryAction: {
|
|
label,
|
|
href: learnUrl,
|
|
},
|
|
helperText:
|
|
args.progressPercent > 0
|
|
? "Tu avance se conserva automáticamente. Puedes continuar desde la lección más reciente."
|
|
: "Inicia el recorrido académico desde el primer módulo.",
|
|
};
|
|
}
|
|
|
|
if (args.isAuthenticated) {
|
|
if (args.price <= 0) {
|
|
return {
|
|
primaryAction: {
|
|
label: "Comenzar",
|
|
href: learnUrl,
|
|
},
|
|
helperText: "Programa con acceso abierto para iniciar de inmediato.",
|
|
};
|
|
}
|
|
|
|
if (previewUrl) {
|
|
return {
|
|
primaryAction: {
|
|
label: "Ver clase de muestra",
|
|
href: previewUrl,
|
|
},
|
|
helperText: "El contenido completo está disponible para estudiantes inscritos en el programa.",
|
|
};
|
|
}
|
|
|
|
return {
|
|
primaryAction: {
|
|
label: "Inscripción próximamente",
|
|
disabled: true,
|
|
},
|
|
helperText: "La inscripción completa para este programa estará disponible en breve.",
|
|
};
|
|
}
|
|
|
|
if (previewUrl) {
|
|
return {
|
|
primaryAction: {
|
|
label: "Ver clase de muestra",
|
|
href: previewUrl,
|
|
},
|
|
secondaryAction: {
|
|
label: "Iniciar sesión",
|
|
href: loginUrl,
|
|
},
|
|
helperText: "Puedes revisar una vista previa o iniciar sesión para gestionar tu itinerario académico.",
|
|
};
|
|
}
|
|
|
|
return {
|
|
primaryAction: {
|
|
label: "Iniciar sesión para comenzar",
|
|
href: loginUrl,
|
|
},
|
|
helperText: "Accede con tu cuenta para comenzar este programa y registrar tu progreso.",
|
|
};
|
|
}
|
|
|
|
export default async function CourseDetailPage({ params }: PageProps) {
|
|
const { slug } = await params;
|
|
const user = await requireUser().catch(() => null);
|
|
|
|
const course = await getCourseDetailViewModel(slug, user?.id ?? null);
|
|
if (!course) notFound();
|
|
|
|
const actions = buildActionState({
|
|
slug: course.slug,
|
|
isAuthenticated: Boolean(user?.id),
|
|
isEnrolled: course.isEnrolled,
|
|
availabilityState: course.availabilityState,
|
|
progressPercent: course.progressPercent,
|
|
firstPreviewLessonId: course.firstPreviewLessonId,
|
|
price: course.price,
|
|
});
|
|
|
|
return (
|
|
<div className="acve-page">
|
|
<section className="grid items-start gap-5 xl:grid-cols-[1.55fr_0.95fr]">
|
|
<CourseDetailHeader
|
|
availabilityLabel={course.availabilityLabel}
|
|
availabilityState={course.availabilityState}
|
|
description={course.longDescription}
|
|
durationLabel={course.durationLabel}
|
|
instructor={course.instructor}
|
|
lessonCount={course.lessonCount}
|
|
proficiencyLabel={course.proficiencyLabel}
|
|
stageLabel={course.stage.levelLabel}
|
|
studentsCount={course.studentsCount}
|
|
thumbnailUrl={course.thumbnailUrl}
|
|
title={course.title}
|
|
/>
|
|
|
|
<CourseProgressCard
|
|
availabilityLabel={course.availabilityLabel}
|
|
completedLessons={course.completedLessons}
|
|
durationLabel={course.durationLabel}
|
|
helperText={actions.helperText}
|
|
instructor={course.instructor}
|
|
primaryAction={actions.primaryAction}
|
|
progressPercent={course.progressPercent}
|
|
secondaryAction={actions.secondaryAction}
|
|
stageLabel={course.stage.levelLabel}
|
|
totalLessons={course.totalLessons}
|
|
/>
|
|
</section>
|
|
|
|
<ProgramContentList modules={course.modules} />
|
|
</div>
|
|
);
|
|
}
|