├── .env.example ├── .gitignore ├── .sanity └── runtime │ ├── app.js │ └── index.html ├── .vscode └── settings.json ├── README.md ├── actions └── createStripeCheckout.ts ├── app ├── (admin) │ ├── layout.tsx │ └── studio │ │ └── [[...tool]] │ │ └── page.tsx ├── (dashboard) │ ├── dashboard │ │ └── courses │ │ │ └── [courseId] │ │ │ ├── layout.tsx │ │ │ ├── lessons │ │ │ └── [lessonId] │ │ │ │ ├── loading.tsx │ │ │ │ └── page.tsx │ │ │ ├── loading.tsx │ │ │ └── page.tsx │ ├── layout.tsx │ └── loading.tsx ├── (user) │ ├── courses │ │ └── [slug] │ │ │ ├── loading.tsx │ │ │ └── page.tsx │ ├── layout.tsx │ ├── loading.tsx │ ├── my-courses │ │ ├── loading.tsx │ │ └── page.tsx │ ├── page.tsx │ └── search │ │ └── [term] │ │ └── page.tsx ├── actions │ ├── completeLessonAction.ts │ ├── getLessonCompletionStatusAction.ts │ └── uncompleteLessonAction.ts ├── api │ ├── draft-mode │ │ ├── disable │ │ │ └── route.ts │ │ └── enable │ │ │ └── route.ts │ └── stripe-checkout │ │ └── webhook │ │ └── route.ts ├── favicon.ico ├── globals.css └── layout.tsx ├── components.json ├── components ├── CourseCard.tsx ├── CourseProgress.tsx ├── DarkModeToggle.tsx ├── DisableDraftMode.tsx ├── EnrollButton.tsx ├── Header.tsx ├── Hero.tsx ├── LessonCompleteButton.tsx ├── LoomEmbed.tsx ├── SearchInput.tsx ├── VideoPlayer.tsx ├── dashboard │ └── Sidebar.tsx ├── providers │ └── sidebar-provider.tsx ├── theme-provider.tsx └── ui │ ├── accordion.tsx │ ├── button.tsx │ ├── dropdown-menu.tsx │ ├── loader.tsx │ ├── progress.tsx │ ├── scroll-area.tsx │ ├── sheet.tsx │ ├── skeleton.tsx │ └── tooltip.tsx ├── eslint.config.mjs ├── lib ├── auth.ts ├── baseUrl.ts ├── courseProgress.ts ├── stripe.ts └── utils.ts ├── middleware.ts ├── next.config.ts ├── package.json ├── pnpm-lock.yaml ├── postcss.config.mjs ├── public ├── file.svg ├── globe.svg ├── next.svg ├── vercel.svg └── window.svg ├── sanity-typegen.json ├── sanity.cli.ts ├── sanity.config.ts ├── sanity.types.ts ├── sanity ├── env.ts ├── lib │ ├── adminClient.ts │ ├── client.ts │ ├── courses │ │ ├── getCourseById.ts │ │ ├── getCourseBySlug.ts │ │ ├── getCourses.ts │ │ └── searchCourses.ts │ ├── image.ts │ ├── lessons │ │ ├── completeLessonById.ts │ │ ├── getCourseProgress.ts │ │ ├── getLessonById.ts │ │ ├── getLessonCompletionStatus.ts │ │ ├── getLessonCompletions.ts │ │ └── uncompleteLessonById.ts │ ├── live.ts │ └── student │ │ ├── createEnrollment.ts │ │ ├── createStudentIfNotExists.ts │ │ ├── getEnrolledCourses.ts │ │ ├── getStudentByClerkId.ts │ │ └── isEnrolledInCourse.ts ├── schema.ts ├── schemaTypes │ ├── blockContent.ts │ ├── categoryType.ts │ ├── courseType.ts │ ├── enrollmentType.tsx │ ├── index.ts │ ├── instructorType.ts │ ├── lessonCompletionType.tsx │ ├── lessonType.ts │ ├── moduleType.ts │ └── studentType.tsx └── structure.ts ├── schema.json ├── tailwind.config.ts └── tsconfig.json /.env.example: -------------------------------------------------------------------------------- 1 | # Sanity 2 | NEXT_PUBLIC_SANITY_PROJECT_ID=your-project-id 3 | NEXT_PUBLIC_SANITY_DATASET=production 4 | # Read Token 5 | SANITY_API_TOKEN=your-sanity-read-token 6 | # Full Access Admin Token 7 | SANITY_API_ADMIN_TOKEN=your-sanity-admin-token 8 | 9 | # For Sanity Studio to read 10 | # This is NEEDED for sanity to see the required variables in the studio deployment 11 | SANITY_STUDIO_PROJECT_ID=your-project-id 12 | SANITY_STUDIO_DATASET=production 13 | 14 | # Next.js 15 | NEXT_PUBLIC_BASE_URL=http://localhost:3000 16 | 17 | # Stripe 18 | # https://dashboard.stripe.com/apikeys 19 | NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=your-stripe-publishable-key 20 | STRIPE_SECRET_KEY=your-stripe-secret-key 21 | # Set this environment variable to support webhooks — https://stripe.com/docs/webhooks#verify-events 22 | STRIPE_WEBHOOK_SECRET=your-stripe-webhook-secret 23 | 24 | # Clerk 25 | NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=your-clerk-publishable-key 26 | CLERK_SECRET_KEY=your-clerk-secret-key -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.* 7 | .yarn/* 8 | !.yarn/patches 9 | !.yarn/plugins 10 | !.yarn/releases 11 | !.yarn/versions 12 | 13 | # testing 14 | /coverage 15 | 16 | # next.js 17 | /.next/ 18 | /out/ 19 | 20 | # production 21 | /build 22 | 23 | # misc 24 | .DS_Store 25 | *.pem 26 | 27 | # debug 28 | npm-debug.log* 29 | yarn-debug.log* 30 | yarn-error.log* 31 | .pnpm-debug.log* 32 | 33 | # env files (can opt-in for committing if needed) 34 | .env 35 | .env.local 36 | .env.development.local 37 | .env.test.local 38 | .env.production.local 39 | !.env.example 40 | 41 | # vercel 42 | .vercel 43 | 44 | # typescript 45 | *.tsbuildinfo 46 | next-env.d.ts 47 | 48 | # sanity 49 | /dist 50 | -------------------------------------------------------------------------------- /.sanity/runtime/app.js: -------------------------------------------------------------------------------- 1 | 2 | // This file is auto-generated on 'sanity dev' 3 | // Modifications to this file is automatically discarded 4 | import {renderStudio} from "sanity" 5 | import studioConfig from "../../sanity.config.ts" 6 | 7 | renderStudio( 8 | document.getElementById("sanity"), 9 | studioConfig, 10 | {reactStrictMode: false, basePath: "/"} 11 | ) 12 | -------------------------------------------------------------------------------- /.sanity/runtime/index.html: -------------------------------------------------------------------------------- 1 | 2 | 6 |
{lesson.description}
34 | )} 35 | 36 |30 | This course has no content yet. Please check back later. 31 |
32 |68 | {course.description} 69 |
70 |153 | {course.instructor.bio} 154 |
155 | )} 156 |42 | You haven't enrolled in any courses yet. Browse our courses 43 | to get started! 44 |
45 | 50 | Browse Courses 51 | 52 |24 | Found {courses.length} result{courses.length === 1 ? "" : "s"} for 25 | "{decodedTerm}" 26 |
27 |34 | Try searching with different keywords 35 |
36 |66 | {course.description} 67 |
68 |13 | Discover a world of learning with our expertly crafted courses. 14 | Learn from industry professionals and take your skills to the next 15 | level. 16 |
17 |70 | {isCompleted 71 | ? "Lesson completed!" 72 | : "Ready to complete this lesson?"} 73 |
74 |75 | {isCompleted 76 | ? "You can mark it as incomplete if you need to revisit it." 77 | : "Mark it as complete when you're done."} 78 |
79 |138 | {module.title} 139 |
140 |141 | {module.lessons?.length || 0} lessons 142 |
143 |