fix: refactor layout

feat/course
Arik Chakma 1 month ago
parent 088d4e2e2d
commit 0abddab414
  1. 1
      package.json
  2. 18
      pnpm-lock.yaml
  3. 29
      src/components/Course/ChallengeView.tsx
  4. 33
      src/components/Course/LessonView.tsx
  5. 32
      src/components/Course/QuizView.tsx
  6. 9
      src/components/SqlCodeEditor/sql-code-editor-theme.ts
  7. 10
      src/data/courses/sql/chapters/introduction/lessons/challenge-1.md
  8. 34
      src/pages/learn/[courseId]/[chapterId]/[lessonId].astro
  9. 11
      src/stores/query-client.ts

@ -43,6 +43,7 @@
"@nanostores/react": "^0.7.2", "@nanostores/react": "^0.7.2",
"@napi-rs/image": "^1.9.2", "@napi-rs/image": "^1.9.2",
"@resvg/resvg-js": "^2.6.2", "@resvg/resvg-js": "^2.6.2",
"@tanstack/react-query": "^5.59.15",
"@types/react": "^18.3.3", "@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0", "@types/react-dom": "^18.3.0",
"astro": "^4.15.4", "astro": "^4.15.4",

@ -50,6 +50,9 @@ importers:
'@resvg/resvg-js': '@resvg/resvg-js':
specifier: ^2.6.2 specifier: ^2.6.2
version: 2.6.2 version: 2.6.2
'@tanstack/react-query':
specifier: ^5.59.15
version: 5.59.15(react@18.3.1)
'@types/react': '@types/react':
specifier: ^18.3.3 specifier: ^18.3.3
version: 18.3.8 version: 18.3.8
@ -1194,6 +1197,14 @@ packages:
peerDependencies: peerDependencies:
tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20' tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20'
'@tanstack/query-core@5.59.13':
resolution: {integrity: sha512-Oou0bBu/P8+oYjXsJQ11j+gcpLAMpqW42UlokQYEz4dE7+hOtVO9rVuolJKgEccqzvyFzqX4/zZWY+R/v1wVsQ==}
'@tanstack/react-query@5.59.15':
resolution: {integrity: sha512-QbVlAkTI78wB4Mqgf2RDmgC0AOiJqer2c5k9STOOSXGv1S6ZkY37r/6UpE8DbQ2Du0ohsdoXgFNEyv+4eDoPEw==}
peerDependencies:
react: ^18 || ^19
'@tybys/wasm-util@0.9.0': '@tybys/wasm-util@0.9.0':
resolution: {integrity: sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==} resolution: {integrity: sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==}
@ -4288,6 +4299,13 @@ snapshots:
postcss-selector-parser: 6.0.10 postcss-selector-parser: 6.0.10
tailwindcss: 3.4.13 tailwindcss: 3.4.13
'@tanstack/query-core@5.59.13': {}
'@tanstack/react-query@5.59.15(react@18.3.1)':
dependencies:
'@tanstack/query-core': 5.59.13
react: 18.3.1
'@tybys/wasm-util@0.9.0': '@tybys/wasm-util@0.9.0':
dependencies: dependencies:
tslib: 2.7.0 tslib: 2.7.0

@ -3,46 +3,22 @@ import {
ResizablePanel, ResizablePanel,
ResizablePanelGroup, ResizablePanelGroup,
} from '../Resizable'; } from '../Resizable';
import { CourseSidebar } from './CourseSidebar';
import { CourseLayout } from './CourseLayout';
import { SqlCodeEditor } from '../SqlCodeEditor/SqlCodeEditor'; import { SqlCodeEditor } from '../SqlCodeEditor/SqlCodeEditor';
import type { ReactNode } from 'react'; import type { ReactNode } from 'react';
import type { import type { LessonFileType } from '../../lib/course';
ChapterFileType,
CourseFileType,
LessonFileType,
} from '../../lib/course';
type ChallengeViewProps = { type ChallengeViewProps = {
courseId: string;
chapterId: string;
lessonId: string;
title: string;
course: CourseFileType & {
chapters: ChapterFileType[];
};
lesson: LessonFileType; lesson: LessonFileType;
children: ReactNode; children: ReactNode;
}; };
export function ChallengeView(props: ChallengeViewProps) { export function ChallengeView(props: ChallengeViewProps) {
const { children, title, course, lesson, courseId, chapterId } = props; const { children, lesson } = props;
const { chapters } = course;
const { frontmatter } = lesson; const { frontmatter } = lesson;
const { defaultValue, initSteps, expectedResults } = frontmatter; const { defaultValue, initSteps, expectedResults } = frontmatter;
return ( return (
<CourseLayout
courseId={courseId}
chapterId={chapterId}
lessonId={lesson.id}
lesson={lesson}
title={title}
chapters={chapters}
completedPercentage={0}
>
<ResizablePanelGroup direction="horizontal"> <ResizablePanelGroup direction="horizontal">
<ResizablePanel defaultSize={60} minSize={20}> <ResizablePanel defaultSize={60} minSize={20}>
<div className="relative h-full"> <div className="relative h-full">
@ -62,6 +38,5 @@ export function ChallengeView(props: ChallengeViewProps) {
/> />
</ResizablePanel> </ResizablePanel>
</ResizablePanelGroup> </ResizablePanelGroup>
</CourseLayout>
); );
} }

@ -1,46 +1,17 @@
import { useState, type ReactNode } from 'react'; import { type ReactNode } from 'react';
import { CourseSidebar } from './CourseSidebar';
import { CourseLayout } from './CourseLayout';
import { Circle, CircleCheck, CircleX } from 'lucide-react';
import { cn } from '../../lib/classname';
import type {
ChapterFileType,
CourseFileType,
LessonFileType,
} from '../../lib/course';
type LessonViewProps = { type LessonViewProps = {
courseId: string;
chapterId: string;
lessonId: string;
title: string;
course: CourseFileType & {
chapters: ChapterFileType[];
};
lesson: LessonFileType;
children: ReactNode; children: ReactNode;
}; };
export function LessonView(props: LessonViewProps) { export function LessonView(props: LessonViewProps) {
const { children, title, course, lesson, courseId, chapterId } = props; const { children } = props;
const { chapters } = course;
return ( return (
<CourseLayout
courseId={courseId}
chapterId={chapterId}
lessonId={lesson.id}
lesson={lesson}
title={title}
chapters={chapters}
completedPercentage={0}
>
<div className="relative h-full"> <div className="relative h-full">
<div className="absolute inset-0 overflow-y-auto [scrollbar-color:#3f3f46_#27272a;]"> <div className="absolute inset-0 overflow-y-auto [scrollbar-color:#3f3f46_#27272a;]">
<div className="mx-auto max-w-xl p-4">{children}</div> <div className="mx-auto max-w-xl p-4">{children}</div>
</div> </div>
</div> </div>
</CourseLayout>
); );
} }

@ -1,28 +1,14 @@
import { useState } from 'react'; import { useState } from 'react';
import { CourseLayout } from './CourseLayout';
import { Circle, CircleCheck, CircleX } from 'lucide-react'; import { Circle, CircleCheck, CircleX } from 'lucide-react';
import { cn } from '../../lib/classname'; import { cn } from '../../lib/classname';
import type { import type { LessonFileType } from '../../lib/course';
ChapterFileType,
CourseFileType,
LessonFileType,
} from '../../lib/course';
type QuizViewProps = { type QuizViewProps = {
courseId: string;
chapterId: string;
lessonId: string;
title: string;
course: CourseFileType & {
chapters: ChapterFileType[];
};
lesson: LessonFileType; lesson: LessonFileType;
}; };
export function QuizView(props: QuizViewProps) { export function QuizView(props: QuizViewProps) {
const { title, course, lesson, courseId, lessonId, chapterId } = props; const { lesson } = props;
const { chapters } = course;
const { frontmatter } = lesson; const { frontmatter } = lesson;
const { questions = [] } = frontmatter; const { questions = [] } = frontmatter;
@ -45,15 +31,6 @@ export function QuizView(props: QuizViewProps) {
}).length; }).length;
return ( return (
<CourseLayout
courseId={courseId}
chapterId={chapterId}
lessonId={lesson.id}
lesson={lesson}
title={title}
chapters={chapters}
completedPercentage={0}
>
<div className="relative h-full"> <div className="relative h-full">
<div className="absolute inset-0 overflow-y-auto [scrollbar-color:#3f3f46_#27272a;]"> <div className="absolute inset-0 overflow-y-auto [scrollbar-color:#3f3f46_#27272a;]">
<div className="mx-auto max-w-xl p-4 py-10"> <div className="mx-auto max-w-xl p-4 py-10">
@ -111,8 +88,8 @@ export function QuizView(props: QuizViewProps) {
{isSubmitted && ( {isSubmitted && (
<div className="mt-8 flex items-center justify-between gap-2 rounded-xl border border-zinc-800 p-4"> <div className="mt-8 flex items-center justify-between gap-2 rounded-xl border border-zinc-800 p-4">
<span> <span>
You got {correctAnswerCount} out of {questions.length}{' '} You got {correctAnswerCount} out of {questions.length} questions
questions right right
</span> </span>
<a className="disabled:cusror-not-allowed rounded-xl border border-zinc-700 bg-zinc-800 p-2 px-4 text-sm font-medium text-white focus:outline-none"> <a className="disabled:cusror-not-allowed rounded-xl border border-zinc-700 bg-zinc-800 p-2 px-4 text-sm font-medium text-white focus:outline-none">
@ -123,7 +100,6 @@ export function QuizView(props: QuizViewProps) {
</div> </div>
</div> </div>
</div> </div>
</CourseLayout>
); );
} }

@ -14,8 +14,8 @@ export const editorDarkTheme = EditorView.theme(
'.cm-content': {}, '.cm-content': {},
// Line number styles // Line number styles
'.cm-lineNumbers .cm-gutterElement': { '.cm-lineNumbers .cm-gutterElement': {
color: '#757575', // Text color for line numbers color: '#757575',
paddingRight: '1em', minWidth: '24px',
}, },
// Scrollbar styles // Scrollbar styles
'.cm-scroller': { '.cm-scroller': {
@ -25,7 +25,7 @@ export const editorDarkTheme = EditorView.theme(
'.cm-scroller::-webkit-scrollbar': {}, '.cm-scroller::-webkit-scrollbar': {},
// Highlight active line // Highlight active line
'.cm-activeLine': { '.cm-activeLine': {
backgroundColor: '#27272a', // Active line background color backgroundColor: '#27272a',
}, },
// Cursor styles // Cursor styles
'.cm-cursor': { '.cm-cursor': {
@ -56,6 +56,9 @@ export const editorDarkTheme = EditorView.theme(
border: 'none', border: 'none',
backgroundColor: 'transparent', backgroundColor: 'transparent',
}, },
'& .cm-foldGutter .cm-gutterElement': {
paddingLeft: '4px',
},
}, },
{ {
dark: true, dark: true,

@ -4,9 +4,15 @@ description: Write a SQL query to find the total number of orders in the `orders
order: 300 order: 300
type: challenge type: challenge
defaultValue: | defaultValue: |
SELECT * FROM orders; SELECT
*
FROM
orders;
SELECT COUNT(*) FROM orders; SELECT
COUNT(*)
FROM
orders;
initSteps: initSteps:
- CREATE TABLE orders ( - CREATE TABLE orders (
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY,

@ -2,6 +2,7 @@
import { ChallengeView } from '../../../../components/Course/ChallengeView'; import { ChallengeView } from '../../../../components/Course/ChallengeView';
import { LessonView } from '../../../../components/Course/LessonView'; import { LessonView } from '../../../../components/Course/LessonView';
import { QuizView } from '../../../../components/Course/QuizView'; import { QuizView } from '../../../../components/Course/QuizView';
import { CourseLayout } from '../../../../components/Course/CourseLayout';
import SkeletonLayout from '../../../../layouts/SkeletonLayout.astro'; import SkeletonLayout from '../../../../layouts/SkeletonLayout.astro';
import { import {
getAllCourses, getAllCourses,
@ -70,17 +71,19 @@ const { course, chapter, lesson } = Astro.props;
--- ---
<SkeletonLayout title={course.frontmatter.title}> <SkeletonLayout title={course.frontmatter.title}>
{ <CourseLayout
lesson.frontmatter.type === 'challenge' && (
<ChallengeView
courseId={courseId} courseId={courseId}
chapterId={chapterId} chapterId={chapterId}
lessonId={lesson.id} lessonId={lesson.id}
title={course.frontmatter.title}
course={course}
lesson={lesson} lesson={lesson}
title={course.frontmatter.title}
chapters={course.chapters}
completedPercentage={0}
client:load client:load
> >
{
lesson.frontmatter.type === 'challenge' && (
<ChallengeView lesson={lesson} client:load>
<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'> <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 /> <lesson.Content />
</div> </div>
@ -90,15 +93,7 @@ const { course, chapter, lesson } = Astro.props;
{ {
lesson.frontmatter.type === 'lesson' && ( lesson.frontmatter.type === 'lesson' && (
<LessonView <LessonView client:load>
courseId={courseId}
chapterId={chapterId}
lessonId={lesson.id}
title={course.frontmatter.title}
course={course}
lesson={lesson}
client:load
>
<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'> <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 /> <lesson.Content />
</div> </div>
@ -108,15 +103,8 @@ const { course, chapter, lesson } = Astro.props;
{ {
lesson.frontmatter.type === 'quiz' && ( lesson.frontmatter.type === 'quiz' && (
<QuizView <QuizView lesson={lesson} client:load />
courseId={courseId}
chapterId={chapterId}
lessonId={lesson.id}
title={course.frontmatter.title}
course={course}
lesson={lesson}
client:load
/>
) )
} }
</CourseLayout>
</SkeletonLayout> </SkeletonLayout>

@ -0,0 +1,11 @@
import { QueryCache, QueryClient } from '@tanstack/react-query';
export const queryClient = new QueryClient({
queryCache: new QueryCache({}),
defaultOptions: {
queries: {
retry: false,
enabled: !import.meta.env.SSR,
},
},
});
Loading…
Cancel
Save