diff --git a/src/app/learn/LearnContent.tsx b/src/app/learn/LearnContent.tsx
new file mode 100644
index 000000000..7a7157100
--- /dev/null
+++ b/src/app/learn/LearnContent.tsx
@@ -0,0 +1,90 @@
+'use client'
+
+import Link from 'next/link'
+import { useSearchParams } from 'next/navigation'
+
+import { learnLevels, LearnLevelType, LearnContentType } from './constants'
+
+const LearnItem = ({
+ index,
+ id,
+ content
+}: {
+ index: number
+ id: string
+ content: LearnContentType
+}) => {
+ return (
+
+
+
+
+ {index}. {content.label}
+
+
+
+
+ )
+}
+
+const LearnList = ({ level }: { level: LearnLevelType }) => {
+ return (
+
+
+
{level.label}
+
+ {level.sections.map(section => {
+ return (
+
+ {/* Section header */}
+
{section.label}
+
+ {/* Section items */}
+ {true && (
+
+ {section.content.map((content, idx) => (
+ -
+
+
+ ))}
+
+ )}
+
+ )
+ })}
+
+
+
+ )
+}
+
+export const LearnContent = () => {
+ const searchParams = useSearchParams()
+ const level = searchParams.get('level')
+
+ return (
+
+
+ {learnLevels.map(learnLevel => {
+ return !level || level == learnLevel.value ? (
+
+ ) : (
+ <>>
+ )
+ })}
+
+
+ )
+}
diff --git a/src/app/learn/LearnSidebar.tsx b/src/app/learn/LearnSidebar.tsx
new file mode 100644
index 000000000..bcd5cf511
--- /dev/null
+++ b/src/app/learn/LearnSidebar.tsx
@@ -0,0 +1,52 @@
+'use client'
+
+import Link from 'next/link'
+import { useSearchParams } from 'next/navigation'
+
+import clsx from 'clsx'
+
+import { learnSidebarSelects } from './constants'
+
+export const LearnSidebar = () => {
+ const searchParams = useSearchParams()
+
+ const level = searchParams.get('level')
+
+ return (
+
+
+
+ {learnSidebarSelects.map(learnLevelSelect => {
+ return (
+
+
+ {learnLevelSelect.label}
+
+
+ )
+ })}
+
+
+
+ )
+}
diff --git a/src/app/learn/constants.ts b/src/app/learn/constants.ts
new file mode 100644
index 000000000..6c7f09c04
--- /dev/null
+++ b/src/app/learn/constants.ts
@@ -0,0 +1,231 @@
+export type LearnContentType = {
+ label: string
+ value: string
+}
+
+export type LearnSectionType = {
+ label: string
+ value: string
+ content: LearnContentType[]
+}
+
+export type LearnLevelType = {
+ label: string
+ value: string
+ sections: LearnSectionType[]
+}
+
+export const learnLevels: LearnLevelType[] = [
+ {
+ label: 'เตรียมตัวก่อนเข้าค่าย',
+ value: 'pre-posn',
+ sections: [
+ {
+ label: 'คณิตศาสตร์',
+ value: 'math',
+ content: [
+ {
+ label: 'บทนำ',
+ value: 'intro'
+ },
+ {
+ label: 'จำนวน ระบบจำนวน การดำเนินการของจำนวน',
+ value: 'number-operation'
+ },
+ {
+ label: 'ความสัมพันธ์ ฟังก์ชัน',
+ value: 'function-relation'
+ },
+ {
+ label: 'สมการ และ อสมการ',
+ value: 'equation-inequality'
+ },
+ {
+ label: 'เซต และ ตรรกศาสตร์',
+ value: 'logic-set'
+ },
+ {
+ label: 'การวัดและเรขาคณิต',
+ value: 'geometry'
+ },
+ {
+ label: 'สถิติและความน่าจะเป็น',
+ value: 'prob-stats'
+ }
+ ]
+ },
+ {
+ label: 'กระบวนการคิด',
+ value: 'problem-solving',
+ content: [
+ {
+ label: 'กระบวนการแก้ปัญหาที่มีการใช้ตรรกะ และฟังก์ชัน',
+ value: 'problem-solving'
+ }
+ ]
+ }
+ ]
+ },
+ {
+ label: 'ค่ายสอวน. 1',
+ value: 'posn',
+ sections: [
+ {
+ label: 'คณิตศาสตร์',
+ value: 'math',
+ content: [
+ {
+ label: 'ทฤษฎีตัวเลข เมทริกซ์',
+ value: 'matrix'
+ },
+ {
+ label: 'ลำดับและอนุกรม',
+ value: 'sequence-series'
+ },
+ {
+ label: 'เลขฐาน',
+ value: 'radix'
+ }
+ ]
+ },
+ {
+ label: 'การเขียนโปรแกรม',
+ value: 'programming',
+ content: [
+ {
+ label: 'ทำไมถึงใช้ C++',
+ value: 'why-cpp'
+ },
+ {
+ label: 'การติดตั้งโปรแกรม',
+ value: 'installation'
+ },
+ {
+ label: 'พื้นฐานภาษา C และ C++',
+ value: 'cpp-basic'
+ }
+ ]
+ }
+ ]
+ },
+ {
+ label: 'ค่ายสอวน. 2 และระดับชาติ',
+ value: 'toi',
+ sections: [
+ {
+ label: 'คณิตศาสตร์',
+ value: 'math',
+ content: [
+ {
+ label: 'เวกเตอร์และระบบพิกัดคาร์ทีเซียน',
+ value: 'vector-cartesian'
+ },
+ {
+ label: 'การนับ',
+ value: 'counting'
+ },
+ {
+ label: 'ทฤษฎีกราฟ',
+ value: 'graph'
+ }
+ ]
+ },
+ {
+ label: 'Data Structure',
+ value: 'datastructure',
+ content: [
+ {
+ label: 'Intro To DS',
+ value: 'intro'
+ },
+ {
+ label: 'Primitive',
+ value: 'primitive'
+ },
+ {
+ label: 'Array and Vector',
+ value: 'array-vector'
+ },
+ {
+ label: 'string',
+ value: 'string'
+ },
+ {
+ label: 'pair',
+ value: 'pair'
+ },
+ {
+ label: 'stack and queue',
+ value: 'stack-queue'
+ },
+ {
+ label: 'priority queue',
+ value: 'priority-queue'
+ },
+ {
+ label: 'set and map',
+ value: 'set-map'
+ },
+ {
+ label: 'choosing datastructure',
+ value: 'choosing-ds'
+ }
+ ]
+ },
+ {
+ label: 'Algorithm',
+ value: 'algorithm',
+ content: [
+ {
+ label: 'intro',
+ value: 'intro'
+ },
+ {
+ label: 'bruteforce',
+ value: 'bruteforce'
+ }
+ ]
+ }
+ ]
+ },
+ {
+ label: 'สสวท.',
+ value: 'ipst',
+ sections: [
+ {
+ label: 'คณิตศาสตร์',
+ value: 'math',
+ content: [
+ {
+ label: 'คณิตศาสตร์ระดับสูง',
+ value: 'advanced-math'
+ }
+ ]
+ }
+ ]
+ },
+ {
+ label: 'IOI',
+ value: 'ioi',
+ sections: [
+ {
+ label: 'คณิตศาสตร์',
+ value: 'math',
+ content: [
+ {
+ label: 'คณิตศาสตร์ระดับสูง',
+ value: 'advanced-math'
+ }
+ ]
+ }
+ ]
+ }
+]
+
+export const learnSidebarSelects = [
+ {
+ label: 'Overview',
+ value: 'overview'
+ },
+ ...learnLevels.map(({ label, value }) => ({ label, value }))
+]
diff --git a/src/app/learn/content/[...id]/RenderContent.tsx b/src/app/learn/content/[...id]/RenderContent.tsx
new file mode 100644
index 000000000..3fc54f26f
--- /dev/null
+++ b/src/app/learn/content/[...id]/RenderContent.tsx
@@ -0,0 +1,37 @@
+'use client'
+
+import { ExclamationCircleIcon } from '@heroicons/react/24/solid'
+import { MDXRemote, MDXRemoteSerializeResult } from 'next-mdx-remote'
+
+import components from '@/components/common/MDXComponents'
+
+const RenderUnderConstruction = () => {
+ return (
+
+
+
+
+ เนื้อหาส่วนนี้ยังไม่สมบูรณ์
+
+
ขออภัยในความไม่สะดวก
+
+ )
+}
+
+export const RenderContent = ({
+ content_md
+}: {
+ content_md: MDXRemoteSerializeResult | null
+}) => {
+ if (!content_md) return
+
+ return (
+
+ )
+}
diff --git a/src/app/learn/content/[...id]/page.tsx b/src/app/learn/content/[...id]/page.tsx
new file mode 100644
index 000000000..0d3824bfb
--- /dev/null
+++ b/src/app/learn/content/[...id]/page.tsx
@@ -0,0 +1,58 @@
+import { Suspense } from 'react'
+
+import Link from 'next/link'
+
+import { ArrowLeftIcon } from '@heroicons/react/24/solid'
+
+import { getLearnMaterial } from '@/lib/api/queries/getLearnMaterial'
+
+import { RenderContent } from './RenderContent'
+import { learnLevels } from '../../constants'
+
+const ContentSidebar = ({ id }: { id: string }) => {
+ const path = id.split('_')
+ if (path.length != 3) return <>>
+ const levelLabel = learnLevels.filter(lev => lev.value == path[0])[0].label
+ const label = learnLevels
+ .filter(lev => lev.value == path[0])[0]
+ .sections.filter(sec => sec.value == path[1])[0]
+ .content.filter(con => con.value == path[2])[0].label
+
+ return (
+
+
+
+
+
+
+
+ exit
+
+
+
+
เนื้อหา{levelLabel}
+
เรื่อง {label}
+
+
+
+ )
+}
+
+const Page = async ({ params }: { params: { id: string[] } }) => {
+ const learnMaterial = await getLearnMaterial(params.id[0])
+ return (
+
+ )
+}
+
+export default Page
diff --git a/src/app/learn/page.tsx b/src/app/learn/page.tsx
index 1d5ca449f..73d323500 100644
--- a/src/app/learn/page.tsx
+++ b/src/app/learn/page.tsx
@@ -1,6 +1,9 @@
+import { Suspense } from 'react'
+
import { Metadata, type NextPage } from 'next'
-import Link from 'next/link'
+import { LearnContent } from './LearnContent'
+import { LearnSidebar } from './LearnSidebar'
export const metadata: Metadata = {
title: 'Learn | programming.in.th'
@@ -8,14 +11,24 @@ export const metadata: Metadata = {
const Learn: NextPage = () => {
return (
-
-
- Coming Soon...
-
-
- กลับหน้าหลัก
-
-
+
+
+
+
+ Learn
+
+
+ Learn with programing.in.th
+
+
+
+
+
+
+
+
+
+
)
}
diff --git a/src/lib/api/queries/getLearnMaterial.ts b/src/lib/api/queries/getLearnMaterial.ts
new file mode 100644
index 000000000..1c047a0cd
--- /dev/null
+++ b/src/lib/api/queries/getLearnMaterial.ts
@@ -0,0 +1,34 @@
+import 'server-only'
+
+import { notFound } from 'next/navigation'
+
+import { MDXRemoteSerializeResult } from 'next-mdx-remote'
+
+import { mdxToHtml } from '@/lib/renderMarkdown'
+
+/**
+ * Get learn material (md file), only used in server components
+ *
+ * @throws 404 Error
+ */
+export async function getLearnMaterial(
+ id: string
+): Promise {
+ if (!id) return notFound()
+
+ let solution = null
+
+ try {
+ const solutionRes = await fetch(
+ `${process.env.NEXT_PUBLIC_AWS_URL}/tutorial/${id.replaceAll('_', '/')}.md`
+ )
+
+ if (solutionRes.status === 200) {
+ const raw = await solutionRes.text()
+ solution = await mdxToHtml(raw)
+ }
+ } catch (error) {
+ console.log(error)
+ }
+ return solution
+}