wip: course footer

feat/course
Arik Chakma 1 month ago
parent a831810487
commit f13d98eff3
  1. 96
      src/components/Course/Chapter.tsx
  2. 56
      src/components/Course/CourseLayout.tsx
  3. 2
      src/components/Course/LessonView.tsx
  4. 5
      src/data/courses/sql/chapters/ddl/ddl.md
  5. 9
      src/data/courses/sql/chapters/introduction/lessons/challenge-1.md
  6. 10
      src/pages/learn/[courseId]/[chapterId]/[lessonId].astro
  7. 0
      src/pages/learn/[courseId]/index.astro
  8. 23
      src/styles/global.css

@ -60,52 +60,60 @@ export function Chapter(props: ChapterProps) {
<div className="flex size-5 items-center justify-center rounded-full bg-zinc-700 text-xs text-white">
{index}
</div>
<span>{title}</span>
<span className="truncate text-left">{title}</span>
</button>
{isActive && (
<div className="flex flex-col border-b border-zinc-800">
<div>
{filteredLessons?.map((lesson) => {
const isActive = lessonId === lesson.id;
return (
<Lesson
key={lesson.id}
{...lesson}
courseId={courseId}
chapterId={chapterId}
isActive={isActive}
isCompleted={false}
/>
);
})}
</div>
<div className="relative">
<label className="relative z-10 my-2 ml-2 block max-w-max rounded-md bg-zinc-800 p-1 px-2 text-xs">
Exercises
</label>
<span className="absolute left-[17px] top-0 h-full w-0.5 bg-zinc-700"></span>
</div>
<div>
{exercises?.map((exercise) => {
const isActive = lessonId === exercise.id;
return (
<Lesson
key={exercise.id}
{...exercise}
courseId={courseId}
chapterId={chapterId}
isActive={isActive}
isCompleted={false}
/>
);
})}
</div>
{lessons.length > 0 && (
<>
<div>
{filteredLessons?.map((lesson) => {
const isActive = lessonId === lesson.id;
return (
<Lesson
key={lesson.id}
{...lesson}
courseId={courseId}
chapterId={chapterId}
isActive={isActive}
isCompleted={false}
/>
);
})}
</div>
<div className="relative">
<label className="relative z-10 my-2 ml-2 block max-w-max rounded-md bg-zinc-800 p-1 px-2 text-xs">
Exercises
</label>
<span className="absolute left-[17px] top-0 h-full w-0.5 bg-zinc-700"></span>
</div>
<div>
{exercises?.map((exercise) => {
const isActive = lessonId === exercise.id;
return (
<Lesson
key={exercise.id}
{...exercise}
courseId={courseId}
chapterId={chapterId}
isActive={isActive}
isCompleted={false}
/>
);
})}
</div>
</>
)}
{lessons.length === 0 && (
<div className="p-2 text-sm text-zinc-500">Coming Soon</div>
)}
</div>
)}
</div>
@ -131,7 +139,7 @@ export function Lesson(props: LessonProps) {
} = props;
const { title } = frontmatter;
const href = `/courses/${courseId}/${chapterId}/${lessonId}`;
const href = `/learn/${courseId}/${chapterId}/${lessonId}`;
return (
<a
@ -144,7 +152,7 @@ export function Lesson(props: LessonProps) {
<div className="relative z-10 flex size-5 items-center justify-center rounded-full bg-zinc-700 text-xs text-white">
{isCompleted && <Check className="h-4 w-4" />}
</div>
<span>{title}</span>
<span className="truncate text-left">{title}</span>
<span className="absolute left-[17px] top-0 h-full w-0.5 bg-zinc-700"></span>
</a>

@ -1,4 +1,6 @@
import { ChevronLeft, ChevronRight } from 'lucide-react';
import { CourseSidebar, type CourseSidebarProps } from './CourseSidebar';
import { useMemo } from 'react';
type CourseLayoutProps = {
children: React.ReactNode;
@ -6,12 +8,58 @@ type CourseLayoutProps = {
export function CourseLayout(props: CourseLayoutProps) {
const { children, ...sidebarProps } = props;
const { chapters, courseId, chapterId, lessonId } = sidebarProps;
const allLessonLinks = useMemo(() => {
const lessons: string[] = [];
for (const chapter of chapters) {
for (const lesson of chapter.lessons) {
lessons.push(`/learn/${courseId}/${chapter.id}/${lesson.id}`);
}
}
return lessons;
}, [chapters]);
const currentLessonIndex = allLessonLinks.indexOf(
`/learn/${courseId}/${chapterId}/${lessonId}`,
);
const prevLessonLink = allLessonLinks[currentLessonIndex - 1] || '';
const nextLessonLink = allLessonLinks[currentLessonIndex + 1] || '';
return (
<section className="grid h-screen grid-cols-[240px_1fr] overflow-hidden bg-zinc-900 text-zinc-50">
<CourseSidebar {...sidebarProps} />
{children}
<section className="grid h-screen grid-rows-[1fr_60px] overflow-hidden bg-zinc-900 text-zinc-50">
<div className="grid grid-cols-[240px_1fr] overflow-hidden">
<CourseSidebar {...sidebarProps} />
{children}
</div>
<footer className="flex items-center justify-end border-t border-zinc-800 px-4">
<div className="flex items-center gap-2">
<button
className="flex items-center gap-1 rounded-lg border border-zinc-800 px-2 py-1.5 text-sm leading-none disabled:opacity-60"
onClick={() => {
window.location.href = prevLessonLink;
}}
disabled={!prevLessonLink}
>
<ChevronLeft className="size-4 stroke-[3]" />
Prev
</button>
<button
className="flex items-center gap-1 rounded-lg border border-zinc-800 px-2 py-1.5 text-sm leading-none disabled:opacity-60"
onClick={() => {
window.location.href = nextLessonLink;
}}
disabled={!nextLessonLink}
>
Next
<ChevronRight className="size-4 stroke-[3]" />
</button>
</div>
</footer>
</section>
);
}

@ -38,7 +38,7 @@ export function LessonView(props: LessonViewProps) {
>
<div className="relative h-full">
<div className="absolute inset-0 overflow-y-auto [scrollbar-color:#3f3f46_#27272a;]">
<div className="mx-auto max-w-xl p-4 py-10">{children}</div>
<div className="mx-auto max-w-xl p-4">{children}</div>
</div>
</div>
</CourseLayout>

@ -0,0 +1,5 @@
---
title: Data Definition Language (DDL)
description: Learn how to create, modify, and delete database objects using SQL Data Definition Language (DDL) statements.
order: 200
---

@ -3,7 +3,10 @@ title: Challenge 1
description: Write a SQL query to find the total number of orders in the `orders` table.
order: 300
type: challenge
defaultValue: SELECT * FROM orders;
defaultValue: |
SELECT * FROM orders;
SELECT COUNT(*) FROM orders;
initSteps:
- CREATE TABLE orders (
id INTEGER PRIMARY KEY,
@ -31,3 +34,7 @@ Write a SQL query to find the total number of orders in the `orders` table.
## Result
Your query should return a single column with the total number of orders in the `orders` table.
- The column name should be `total_orders`.
- The value should be the total number of orders in the `orders` table.
- The query should return a single row with the total number of orders in the `orders` table.

@ -81,7 +81,9 @@ const { course, chapter, lesson } = Astro.props;
lesson={lesson}
client:load
>
<lesson.Content />
<div class='course-content prose prose-lg prose-invert mt-8 text-zinc-300 prose-headings:mb-3 prose-headings:mt-8 prose-code:text-zinc-100 prose-li:my-1 prose-thead:border-zinc-800 prose-tr:border-zinc-800'>
<lesson.Content />
</div>
</ChallengeView>
)
}
@ -97,7 +99,9 @@ const { course, chapter, lesson } = Astro.props;
lesson={lesson}
client:load
>
<lesson.Content />
<div class='course-content prose prose-lg prose-invert mt-8 text-zinc-300 prose-headings:mb-3 prose-headings:mt-8 prose-code:text-zinc-100 prose-li:my-1 prose-thead:border-zinc-800 prose-tr:border-zinc-800'>
<lesson.Content />
</div>
</LessonView>
)
}
@ -112,7 +116,7 @@ const { course, chapter, lesson } = Astro.props;
course={course}
lesson={lesson}
client:load
></QuizView>
/>
)
}
</SkeletonLayout>

@ -62,6 +62,29 @@ a > code:before {
content: '' !important;
}
.course-content.prose ul li > code,
.course-content.prose ol li > code,
.course-content p code,
.course-content a > code,
.course-content strong > code,
.course-content em > code,
.course-content h1 > code,
.course-content h2 > code,
.course-content h3 > code {
background: transparent !important;
color: #f4f4f5 !important;
font-size: inherit !important;
}
.course-content.prose ul li > code:before,
.course-content p > code:before,
.course-content.prose ul li > code:after,
.course-content p > code:after,
.course-content a > code:after,
.course-content a > code:before {
content: '`' !important;
}
.sponsor-footer {
text-align: center;
font-weight: 600;

Loading…
Cancel
Save