diff --git a/src/components/Course/Chapter.tsx b/src/components/Course/Chapter.tsx index 13f1e43ce..cc5ffc349 100644 --- a/src/components/Course/Chapter.tsx +++ b/src/components/Course/Chapter.tsx @@ -12,9 +12,9 @@ type ChapterProps = ChapterFileType & { isActive?: boolean; isCompleted?: boolean; - courseId: string; - chapterId: string; - lessonId?: string; + currentCourseId: string; + currentChapterId?: string; + currentLessonId?: string; onChapterClick?: () => void; }; @@ -26,30 +26,30 @@ export function Chapter(props: ChapterProps) { isActive = false, onChapterClick, - courseId, - chapterId, - lessonId, + currentCourseId, + currentChapterId, + currentLessonId, } = props; const { title } = frontmatter; - const { data: courseProgress } = useCourseProgress(courseId); + const { data: courseProgress } = useCourseProgress(currentCourseId); const completeLessonSet = useMemo( () => new Set( (courseProgress?.completed || []) - .filter((l) => l.chapterId === chapterId) + .filter((l) => l.chapterId === currentChapterId) .map((l) => `${l.chapterId}/${l.lessonId}`), ), [courseProgress], ); const isChapterCompleted = lessons.every((lesson) => - completeLessonSet.has(`${chapterId}/${lesson.id}`), + completeLessonSet.has(`${currentChapterId}/${lesson.id}`), ); const completedPercentage = useMemo(() => { const completedCount = lessons.filter((lesson) => - completeLessonSet.has(`${chapterId}/${lesson.id}`), + completeLessonSet.has(`${currentChapterId}/${lesson.id}`), ).length; return getPercentage(completedCount, lessons.length); @@ -103,17 +103,17 @@ export function Chapter(props: ChapterProps) { <>
{filteredLessons?.map((lesson) => { - const isActive = lessonId === lesson.id; + const isActive = currentLessonId === lesson.id; const isCompleted = completeLessonSet.has( - `${chapterId}/${lesson.id}`, + `${currentChapterId}/${lesson.id}`, ); return ( @@ -131,17 +131,17 @@ export function Chapter(props: ChapterProps) {
{exercises?.map((exercise) => { - const isActive = lessonId === exercise.id; + const isActive = currentLessonId === exercise.id; const isCompleted = completeLessonSet.has( - `${chapterId}/${exercise.id}`, + `${currentChapterId}/${exercise.id}`, ); return ( @@ -161,8 +161,8 @@ export function Chapter(props: ChapterProps) { } type LessonProps = LessonFileType & { - courseId: string; - chapterId: string; + currentCourseId: string; + currentChapterId?: string; isActive?: boolean; isCompleted?: boolean; @@ -172,16 +172,16 @@ export function Lesson(props: LessonProps) { const { frontmatter, isActive, - courseId, - chapterId, + currentCourseId, + currentChapterId, id: lessonId, isCompleted, } = props; const { title } = frontmatter; const isMounted = useIsMounted(); - const { isLoading } = useCourseProgress(courseId); - const href = `/learn/${courseId}/${chapterId}/${lessonId}`; + const { isLoading } = useCourseProgress(currentCourseId); + const href = `/learn/${currentCourseId}/${currentChapterId}/${lessonId}`; return ( ; export function CourseLayout(props: CourseLayoutProps) { const { children, ...sidebarProps } = props; - const { chapters, courseId, chapterId, lessonId, lesson } = sidebarProps; + const { + chapters, + currentCourseId, + currentChapterId, + currentLessonId, + lesson, + } = sidebarProps; const $currentLesson = useStore(currentLesson); const [showNextWarning, setShowNextWarning] = useState(false); - const { data: courseProgress } = useCourseProgress(courseId); - const completeLesson = useCompleteLessonMutation(courseId); + const { data: courseProgress } = useCourseProgress(currentCourseId); + const completeLesson = useCompleteLessonMutation(currentCourseId); const completeLessonSet = useMemo( () => new Set( (courseProgress?.completed || []).map( - (l) => `/learn/${courseId}/${l.chapterId}/${l.lessonId}`, + (l) => `/learn/${currentCourseId}/${l.chapterId}/${l.lessonId}`, ), ), [courseProgress], @@ -38,7 +45,7 @@ export function CourseLayout(props: CourseLayoutProps) { const lessons: string[] = []; for (const chapter of chapters) { for (const lesson of chapter.lessons) { - lessons.push(`/learn/${courseId}/${chapter.id}/${lesson.id}`); + lessons.push(`/learn/${currentCourseId}/${chapter.id}/${lesson.id}`); } } @@ -53,7 +60,7 @@ export function CourseLayout(props: CourseLayoutProps) { return getPercentage(completedCount, allLessonLinks.length); }, [allLessonLinks, completeLessonSet]); - const currentLessonUrl = `/learn/${courseId}/${chapterId}/${lessonId}`; + const currentLessonUrl = `/learn/${currentCourseId}/${currentChapterId}/${currentLessonId}`; const isCurrentLessonCompleted = completeLessonSet.has(currentLessonUrl); const currentLessonIndex = allLessonLinks.indexOf(currentLessonUrl); @@ -68,10 +75,14 @@ export function CourseLayout(props: CourseLayoutProps) { return; } + if (!currentChapterId || !currentLessonId) { + return; + } + completeLesson.mutate( { - chapterId, - lessonId, + chapterId: currentChapterId, + lessonId: currentLessonId, }, { onSuccess: () => { @@ -91,10 +102,10 @@ export function CourseLayout(props: CourseLayoutProps) { } currentLesson.set({ - courseId, - chapterId, - lessonId, - lessonType: lesson.frontmatter.type, + courseId: currentCourseId, + chapterId: currentChapterId, + lessonId: currentLessonId, + lessonType: lesson?.frontmatter?.type, challengeStatus: 'pending', quizStatus: 'pending', }); @@ -112,7 +123,14 @@ export function CourseLayout(props: CourseLayoutProps) { /> )} -
+
-
-
- - - -
-
+ {currentChapterId && currentLessonId && ( +
+
+ + + +
+
+ )}
); diff --git a/src/components/Course/CourseSidebar.tsx b/src/components/Course/CourseSidebar.tsx index 45e9247fc..4137d55f4 100644 --- a/src/components/Course/CourseSidebar.tsx +++ b/src/components/Course/CourseSidebar.tsx @@ -1,15 +1,16 @@ import { useState } from 'react'; import type { ChapterFileType, LessonFileType } from '../../lib/course'; import { Chapter } from './Chapter'; +import { StickyNote } from 'lucide-react'; export type CourseSidebarProps = { - courseId: string; - chapterId: string; - lessonId: string; + currentCourseId: string; + currentChapterId?: string; + currentLessonId?: string; title: string; chapters: ChapterFileType[]; - lesson: LessonFileType; + lesson?: LessonFileType; completedPercentage: number; }; @@ -19,12 +20,14 @@ export function CourseSidebar(props: CourseSidebarProps) { title, chapters, completedPercentage, - chapterId, - lessonId, - courseId, + currentCourseId, + currentChapterId, + currentLessonId, } = props; - const [activeChapterId, setActiveChapterId] = useState(chapterId); + const [activeChapterId, setActiveChapterId] = useState(currentChapterId); + + const ceritificateUrl = `/learn/${currentCourseId}/certificate`; return (
diff --git a/src/pages/learn/[courseId]/certificate.astro b/src/pages/learn/[courseId]/certificate.astro new file mode 100644 index 000000000..fcbc2f674 --- /dev/null +++ b/src/pages/learn/[courseId]/certificate.astro @@ -0,0 +1,65 @@ +--- +import { CourseLayout } from '../../../components/Course/CourseLayout'; +import SkeletonLayout from '../../../layouts/SkeletonLayout.astro'; +import { + getAllCourses, + getChaptersByCourseId, + type CourseFileType, + type ChapterFileType, +} from '../../../lib/course'; + +interface Params extends Record { + courseId: string; +} + +interface Props { + course: CourseFileType & { chapters: ChapterFileType[] }; +} + +export async function getStaticPaths() { + const courses = await getAllCourses(); + const coursesWithChapters = await Promise.all( + courses.map(async (course) => { + const chapters = await getChaptersByCourseId(course.id); + return { + ...course, + chapters, + }; + }), + ); + + const paths: { + params: Params; + props: Props; + }[] = []; + + for (const course of coursesWithChapters) { + const courseId = course.id; + + paths.push({ + params: { + courseId, + }, + props: { + course, + }, + }); + } + + return paths; +} + +const { courseId } = Astro.params; +const { course } = Astro.props; +--- + + + + Hello + + diff --git a/src/stores/course.ts b/src/stores/course.ts index 05b7ed2a5..37f8c7a05 100644 --- a/src/stores/course.ts +++ b/src/stores/course.ts @@ -3,9 +3,9 @@ import type { AllowedLessonType } from '../lib/course'; export type CurrentLessonType = { courseId: string; - chapterId: string; - lessonId: string; - lessonType: AllowedLessonType; + chapterId?: string; + lessonId?: string; + lessonType?: AllowedLessonType; challengeStatus?: 'pending' | 'wrong' | 'correct'; quizStatus?: 'pending' | 'wrong' | 'correct'; };