fix: merge lessons

feat/course
Arik Chakma 1 month ago
parent 423800b22f
commit 8502b302b2
  1. 3
      src/data/courses/sql/chapters/introduction/lessons/challenge-1.md
  2. 3
      src/data/courses/sql/chapters/introduction/lessons/intro-to-sql.md
  3. 111
      src/lib/course.ts
  4. 70
      src/pages/courses/[courseId]/[chapterId]/[lessonId].astro
  5. 31
      src/pages/courses/[courseId]/index.astro

@ -1,7 +1,8 @@
---
title: Challenge 1
description: Write a SQL query to find the total number of orders in the `orders` table.
order: 100
order: 200
type: challenge
defaultValue: SELECT * FROM orders;
initSteps:
- CREATE TABLE orders (

@ -1,7 +1,8 @@
---
title: Intro to SQL
description: Learn the basics of SQL, the language for querying databases.
order: 1
order: 100
type: lesson
---
The SQL language is widely used today across web frameworks and database applications. Knowing SQL gives you the freedom to explore your data, and the power to make better decisions. By learning SQL, you will also learn concepts that apply to nearly every data storage system.

@ -10,26 +10,7 @@ export type LessonFrontmatter = {
title: string;
description: string;
order: number;
};
export type LessonFileType = MarkdownFileType<LessonFrontmatter> & {
id: string;
};
export type QuizFrontmatter = {
id: string;
order: number;
title: string;
};
export type QuizFileType = MarkdownFileType<QuizFrontmatter> & {
id: string;
};
export type ChallengeFrontmatter = {
title: string;
desctiption: string;
order: number;
type: 'lesson' | 'challenge' | 'quiz';
defaultValue?: string;
initSteps?: string[];
@ -39,7 +20,7 @@ export type ChallengeFrontmatter = {
}[];
};
export type ChallengeFileType = MarkdownFileType<ChallengeFrontmatter> & {
export type LessonFileType = MarkdownFileType<LessonFrontmatter> & {
id: string;
};
@ -52,12 +33,10 @@ export type ChapterFrontmatter = {
export type ChapterFileType = MarkdownFileType<ChapterFrontmatter> & {
id: string;
lessons: LessonFileType[];
exercises: (QuizFileType | ChallengeFileType)[];
};
export type CourseFileType = MarkdownFileType<CourseFrontmatter> & {
id: string;
chapters: ChapterFileType[];
};
function coursePathToId(filePath: string): string {
@ -70,7 +49,7 @@ function coursePathToId(filePath: string): string {
*
* @returns string[] Array of course IDs
*/
export async function getCourseIds() {
export async function getAllCourseIds() {
const courseFiles = import.meta.glob<CourseFileType>(
'/src/data/courses/*/*.md',
{
@ -81,6 +60,18 @@ export async function getCourseIds() {
return Object.keys(courseFiles).map(coursePathToId);
}
export async function getAllCourses() {
const allCourseIds = await getAllCourseIds();
const courses = await Promise.all(
allCourseIds.map(async (courseId) => {
return getCourseById(courseId);
}),
);
return courses.sort((a, b) => a.frontmatter.order - b.frontmatter.order);
}
export async function getCourseById(id: string): Promise<CourseFileType> {
const courseFilesMap: Record<string, CourseFileType> =
import.meta.glob<CourseFileType>('/src/data/courses/*/*.md', {
@ -95,12 +86,9 @@ export async function getCourseById(id: string): Promise<CourseFileType> {
throw new Error(`Course with ID ${id} not found`);
}
const chapters = await getChaptersByCourseId(id);
return {
...courseFile,
id: coursePathToId(courseFile.file),
chapters,
};
}
@ -127,13 +115,11 @@ export async function getChaptersByCourseId(courseId: string) {
for (const chapterFile of chapterFiles) {
const chapterId = chapterPathToId(chapterFile.file);
const lessons = await getLessonsByCourseId(courseId, chapterId);
const exercises = await getExercisesByCourseId(courseId, chapterId);
enrichedChapters.push({
...chapterFile,
id: chapterId,
lessons,
exercises,
});
}
@ -173,70 +159,3 @@ export async function getLessonsByCourseId(
}))
.sort((a, b) => a.frontmatter.order - b.frontmatter.order);
}
export function exercisePathToId(filePath: string): string {
const fileName = filePath.split('/').pop() || '';
return fileName.replace('.md', '');
}
export async function getExercisesByCourseId(
courseId: string,
chapterId: string,
): Promise<(QuizFileType | ChallengeFileType)[]> {
const exerciseFilesMap = import.meta.glob<QuizFileType | ChallengeFileType>(
`/src/data/courses/*/chapters/*/exercises/*.md`,
{
eager: true,
},
);
return Object.values(exerciseFilesMap)
.filter((exerciseFile) => {
const [, currentCourseId, currentChapterId] =
exerciseFile.file.match(
/\/courses\/([^/]+)\/chapters\/([^/]+)\/exercises/,
) || [];
return currentCourseId === courseId && currentChapterId === chapterId;
})
.map((exerciseFile) => ({
...exerciseFile,
id: exercisePathToId(exerciseFile.file),
}))
.sort((a, b) => a.frontmatter.order - b.frontmatter.order);
}
export async function getCourseExerciseById(
courseId: string,
chapterId: string,
exerciseId: string,
) {
const exerciseFilesMap = import.meta.glob<QuizFileType | ChallengeFileType>(
`/src/data/courses/*/chapters/*/exercises/*.md`,
{
eager: true,
},
);
const exerciseFile = Object.values(exerciseFilesMap).find((exerciseFile) => {
const [, currentCourseId, currentChapterId, currentExerciseId] =
exerciseFile.file.match(
/\/courses\/([^/]+)\/chapters\/([^/]+)\/exercises\/([^/]+)\.md/,
) || [];
return (
currentCourseId === courseId &&
currentChapterId === chapterId &&
currentExerciseId === exerciseId
);
});
if (!exerciseFile) {
throw new Error(`Exercise with ID ${exerciseId} not found`);
}
return {
...exerciseFile,
id: exercisePathToId(exerciseFile.file),
};
}

@ -0,0 +1,70 @@
---
import {
getAllCourses,
getChaptersByCourseId,
type CourseFileType,
type ChapterFileType,
type LessonFileType,
} from '../../../../lib/course';
interface Params extends Record<string, string | undefined> {
courseId: string;
chapterId: string;
lessonId: string;
}
interface Props {
course: CourseFileType;
chapter: ChapterFileType;
lesson: LessonFileType;
}
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;
const courseChapters = course.chapters;
for (const chapter of courseChapters) {
for (const lesson of chapter.lessons) {
paths.push({
params: {
courseId,
chapterId: chapter.id,
lessonId: lesson.id,
},
props: {
course,
chapter,
lesson,
},
});
}
}
}
return paths;
}
const { courseId, chapterId } = Astro.params;
const { course, chapter, lesson } = Astro.props;
---
<pre>
{JSON.stringify(lesson, null, 2)}
</pre>

@ -0,0 +1,31 @@
---
import {
getAllCourses,
getCourseById,
type CourseFileType,
} from '../../../lib/course';
export async function getStaticPaths() {
const courses = await getAllCourses();
return courses.map((course) => ({
params: { courseId: course.id },
props: { course },
}));
}
interface Params extends Record<string, string | undefined> {
courseId: string;
}
interface Props {
course: CourseFileType;
}
const { courseId } = Astro.params;
const { course } = Astro.props;
---
<pre>
{JSON.stringify(course, null, 2)}
</pre>
Loading…
Cancel
Save