|
|
|
@ -1,4 +1,4 @@ |
|
|
|
|
import { lazy, type ReactNode } from 'react'; |
|
|
|
|
import { lazy, useEffect, useState, type ReactNode } from 'react'; |
|
|
|
|
import type { LessonFileType } from '../../lib/course'; |
|
|
|
|
import { CourseLayout, type CourseLayoutProps } from './CourseLayout'; |
|
|
|
|
import { |
|
|
|
@ -6,6 +6,9 @@ import { |
|
|
|
|
ResizablePanel, |
|
|
|
|
ResizablePanelGroup, |
|
|
|
|
} from '../Resizable'; |
|
|
|
|
import { isLoggedIn } from '../../lib/jwt'; |
|
|
|
|
import { IdCardIcon, Loader2Icon } from 'lucide-react'; |
|
|
|
|
import { showLoginPopup } from '../../lib/popup'; |
|
|
|
|
|
|
|
|
|
const SqlCodeEditor = lazy(() => |
|
|
|
|
import('../SqlCodeEditor/SqlCodeEditor').then((module) => ({ |
|
|
|
@ -38,6 +41,45 @@ export function CourseView(props: CourseViewProps) { |
|
|
|
|
'lesson-quiz', |
|
|
|
|
].includes(lessonType); |
|
|
|
|
|
|
|
|
|
const [isLoading, setIsLoading] = useState(true); |
|
|
|
|
const isAuthenticated = isLoggedIn(); |
|
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
|
if (isAuthenticated === undefined) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
setIsLoading(false); |
|
|
|
|
}, [isAuthenticated]); |
|
|
|
|
|
|
|
|
|
if (isLoading) { |
|
|
|
|
return ( |
|
|
|
|
<div className="mx-auto flex h-screen max-w-sm flex-col items-center justify-center gap-2 p-4 text-gray-700"> |
|
|
|
|
<Loader2Icon className="size-10 animate-spin stroke-[2.5]" /> |
|
|
|
|
</div> |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!isAuthenticated) { |
|
|
|
|
return ( |
|
|
|
|
<div className="mx-auto flex h-screen max-w-sm flex-col items-center justify-center gap-2 p-4 text-gray-700"> |
|
|
|
|
<IdCardIcon className="size-24" /> |
|
|
|
|
<p className="text-balance text-center text-lg font-medium"> |
|
|
|
|
You need to be logged in to access this course. |
|
|
|
|
</p> |
|
|
|
|
|
|
|
|
|
<button |
|
|
|
|
className="flex items-center gap-1 rounded-lg border border-gray-400 px-4 py-2 text-sm font-medium leading-none transition-colors hover:bg-black hover:text-white disabled:opacity-60" |
|
|
|
|
onClick={() => { |
|
|
|
|
showLoginPopup(); |
|
|
|
|
}} |
|
|
|
|
> |
|
|
|
|
Login |
|
|
|
|
</button> |
|
|
|
|
</div> |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
|
<CourseLayout {...courseLayoutProps}> |
|
|
|
|
<ResizablePanelGroup direction="horizontal"> |
|
|
|
@ -49,10 +91,10 @@ export function CourseView(props: CourseViewProps) { |
|
|
|
|
<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"> |
|
|
|
|
<h3 className="text-4xl font-semibold mt-10"> |
|
|
|
|
<h3 className="mt-10 text-4xl font-semibold"> |
|
|
|
|
{lesson.frontmatter.title} |
|
|
|
|
</h3> |
|
|
|
|
<div className="course-content prose prose-pre:text-lg prose-pre:rounded-2xl prose-lg mt-8 text-black prose-headings:mb-3 prose-headings:mt-8 prose-li:my-1 prose-thead:border-zinc-800 prose-tr:border-zinc-800"> |
|
|
|
|
<div className="course-content prose prose-lg mt-8 text-black prose-headings:mb-3 prose-headings:mt-8 prose-pre:rounded-2xl prose-pre:text-lg prose-li:my-1 prose-thead:border-zinc-800 prose-tr:border-zinc-800"> |
|
|
|
|
{children} |
|
|
|
|
</div> |
|
|
|
|
</div> |
|
|
|
|