import { Check, Loader2, Play } from 'lucide-react'; import { cn } from '../../lib/classname'; import type { ChapterFileType, LessonFileType } from '../../lib/course'; import { useMemo, type CSSProperties } from 'react'; import { useCourseProgress } from '../../hooks/use-course'; import { CheckIcon } from '../ReactIcons/CheckIcon'; import { getPercentage } from '../../helper/number'; import { useIsMounted } from '../../hooks/use-is-mounted'; function LeftBorder({ hasCompleted }: { hasCompleted?: boolean }) { return ( ); } type ChapterProps = ChapterFileType & { index: number; isActive?: boolean; isCompleted?: boolean; activeCourseId: string; activeChapterId?: string; activeLessonId?: string; onChapterClick?: () => void; }; export function Chapter(props: ChapterProps) { const { id: chapterId, index, frontmatter, lessons, isActive = false, onChapterClick, activeCourseId, activeChapterId, activeLessonId, } = props; const { title } = frontmatter; const { data: courseProgress } = useCourseProgress(activeCourseId); const completeLessonSet = useMemo( () => new Set( (courseProgress?.completed || []) .filter((l) => l.chapterId === chapterId) .map((l) => `${l.chapterId}/${l.lessonId}`), ), [courseProgress], ); const isChapterCompleted = lessons.every((lesson) => completeLessonSet.has(`${chapterId}/${lesson.id}`), ); const completedPercentage = useMemo(() => { const completedCount = lessons.filter((lesson) => completeLessonSet.has(`${chapterId}/${lesson.id}`), ).length; return getPercentage(completedCount, lessons.length); }, [lessons, completeLessonSet]); const [filteredLessons, exercises] = useMemo(() => { const sortedLessons = lessons.sort( (a, b) => a.frontmatter.order - b.frontmatter.order, ); return [ sortedLessons.filter( (lesson) => !['quiz', 'challenge'].includes(lesson.frontmatter.type), ), sortedLessons.filter((lesson) => ['quiz', 'challenge'].includes(lesson.frontmatter.type), ), ]; }, [lessons]); return (
{isActive && (
{lessons.length > 0 && ( <>
)} {lessons.length === 0 && (
Coming Soon
)}
)}
); } type LessonListProps = { activeCourseId: string; activeChapterId?: string; activeLessonId?: string; chapterId: string; lessons: LessonFileType[]; completedLessonSet: Set; }; function LessonList(props: LessonListProps) { const { activeCourseId, activeChapterId, activeLessonId, chapterId, lessons, completedLessonSet, } = props; return (
{lessons.map((lesson, counter) => { const isActive = activeLessonId === lesson.id && chapterId === activeChapterId; const isCompleted = completedLessonSet.has(`${chapterId}/${lesson.id}`); return ( ); })}
); } type LessonProps = LessonFileType & { isActive?: boolean; isCompleted?: boolean; courseId: string; counter: number; chapterId: string; }; export function Lesson(props: LessonProps) { const { frontmatter, isActive, courseId, chapterId, id: lessonId, counter, isCompleted, } = props; const { title } = frontmatter; const isMounted = useIsMounted(); const { isLoading } = useCourseProgress(courseId); const href = `/learn/${courseId}/${chapterId}/${lessonId}`; return (
{!isCompleted && counter} {isCompleted && }
{title}
); }