113 lines
6.0 KiB
TypeScript
113 lines
6.0 KiB
TypeScript
import { LockKeyhole, PlayCircle } from "lucide-react";
|
|
import { cn } from "@/lib/utils";
|
|
import type { CourseProgramModuleView } from "@/lib/courses/publicCourses";
|
|
|
|
type ProgramContentListProps = {
|
|
modules: CourseProgramModuleView[];
|
|
};
|
|
|
|
const badgeClass: Record<string, string> = {
|
|
Video: "border-sky-300/70 bg-sky-50 text-sky-800 dark:border-sky-700/50 dark:bg-sky-900/30 dark:text-sky-200",
|
|
Lectura: "border-indigo-300/70 bg-indigo-50 text-indigo-800 dark:border-indigo-700/50 dark:bg-indigo-900/30 dark:text-indigo-200",
|
|
Actividad: "border-rose-300/70 bg-rose-50 text-rose-800 dark:border-rose-700/50 dark:bg-rose-900/30 dark:text-rose-200",
|
|
"Evaluación":
|
|
"border-amber-300/70 bg-amber-50 text-amber-800 dark:border-amber-700/50 dark:bg-amber-900/30 dark:text-amber-200",
|
|
};
|
|
|
|
export default function ProgramContentList({ modules }: ProgramContentListProps) {
|
|
return (
|
|
<section className="acve-panel acve-section-base">
|
|
<div className="mb-5">
|
|
<p className="text-xs font-semibold uppercase tracking-[0.14em] text-muted-foreground">Plan de estudios</p>
|
|
<h2 className="acve-heading mt-2 text-2xl md:text-3xl">Contenido del Programa</h2>
|
|
</div>
|
|
|
|
{modules.length === 0 ? (
|
|
<div className="rounded-2xl border border-dashed border-border bg-muted/35 px-6 py-9 text-center">
|
|
<p className="text-lg font-semibold text-foreground">Contenido en preparación</p>
|
|
<p className="mt-2 text-sm text-muted-foreground">
|
|
El equipo académico está publicando módulos y lecciones para este programa.
|
|
</p>
|
|
</div>
|
|
) : (
|
|
<div className="space-y-5">
|
|
{modules.map((module) => (
|
|
<article key={module.id} className="overflow-hidden rounded-2xl border border-border/80 bg-card/65">
|
|
<header className="flex flex-wrap items-center justify-between gap-3 border-b border-border/70 bg-muted/30 px-4 py-3">
|
|
<h3 className="text-lg font-semibold text-foreground">
|
|
Módulo {module.order}. {module.title}
|
|
</h3>
|
|
<p className="text-xs font-semibold uppercase tracking-wide text-muted-foreground">{module.items.length} lecciones</p>
|
|
</header>
|
|
|
|
<ol className="divide-y divide-border/60">
|
|
{module.items.map((item) => (
|
|
<li key={item.id} className="px-4 py-3">
|
|
<div className="flex flex-wrap items-start justify-between gap-3">
|
|
<div className="min-w-0 flex-1">
|
|
<div className="flex flex-wrap items-center gap-2">
|
|
<span className="inline-flex min-w-8 items-center justify-center rounded-full border border-border bg-card px-2 py-0.5 text-xs font-semibold text-muted-foreground">
|
|
{String(item.order).padStart(2, "0")}
|
|
</span>
|
|
|
|
{item.badges.map((badge) => (
|
|
<span
|
|
key={`${item.id}-${badge}`}
|
|
className={cn("rounded-full border px-2 py-0.5 text-[11px] font-semibold", badgeClass[badge])}
|
|
>
|
|
{badge}
|
|
</span>
|
|
))}
|
|
|
|
{item.isPreview ? (
|
|
<span className="rounded-full border border-primary/35 bg-primary/10 px-2 py-0.5 text-[11px] font-semibold text-primary">
|
|
Vista previa
|
|
</span>
|
|
) : null}
|
|
{item.isFinalExam ? (
|
|
<span className="rounded-full border border-amber-300/70 bg-amber-50 px-2 py-0.5 text-[11px] font-semibold text-amber-800 dark:border-amber-700/50 dark:bg-amber-900/30 dark:text-amber-200">
|
|
Evaluación final obligatoria
|
|
</span>
|
|
) : null}
|
|
|
|
{item.isCompleted ? (
|
|
<span className="rounded-full border border-emerald-300/70 bg-emerald-50 px-2 py-0.5 text-[11px] font-semibold text-emerald-800 dark:border-emerald-700/50 dark:bg-emerald-900/30 dark:text-emerald-200">
|
|
Completada
|
|
</span>
|
|
) : null}
|
|
</div>
|
|
|
|
<p className="mt-2 text-sm font-semibold text-foreground md:text-base">{item.title}</p>
|
|
{item.subtitle ? <p className="mt-1 text-sm text-muted-foreground">{item.subtitle}</p> : null}
|
|
</div>
|
|
|
|
<div className="flex shrink-0 items-center gap-2 text-xs font-medium text-muted-foreground">
|
|
{item.durationLabel ? <span>{item.durationLabel}</span> : null}
|
|
{item.isUpcoming ? (
|
|
<span className="rounded-full border border-amber-300/70 bg-amber-50 px-2 py-0.5 text-amber-800 dark:border-amber-700/50 dark:bg-amber-900/30 dark:text-amber-200">
|
|
Próximamente
|
|
</span>
|
|
) : item.isLocked ? (
|
|
<span className="inline-flex items-center gap-1 rounded-full border border-border bg-muted/35 px-2 py-0.5">
|
|
<LockKeyhole className="h-3 w-3" />
|
|
Bloqueada
|
|
</span>
|
|
) : (
|
|
<span className="inline-flex items-center gap-1 rounded-full border border-primary/30 bg-primary/10 px-2 py-0.5 text-primary">
|
|
<PlayCircle className="h-3 w-3" />
|
|
Disponible
|
|
</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</li>
|
|
))}
|
|
</ol>
|
|
</article>
|
|
))}
|
|
</div>
|
|
)}
|
|
</section>
|
|
);
|
|
}
|