├── .eslintrc.json ├── .vscode └── extensions.json ├── public ├── favicon.ico ├── images │ ├── mux-logo.png │ ├── pscale-connect.png │ ├── pscale-prisma.png │ └── pscale-string.png ├── fonts │ ├── CalSans-SemiBold.woff │ ├── CalSans-SemiBold.woff2 │ ├── stylesheet.css │ └── demo.html └── vercel.svg ├── postcss.config.js ├── screenshots └── github-oauth.png ├── utils ├── webhooks │ └── mux │ │ └── types │ │ ├── index.ts │ │ └── video │ │ ├── index.ts │ │ └── asset │ │ ├── index.ts │ │ ├── created.ts │ │ └── ready.ts ├── formatDuration.ts ├── db.ts └── prisma.ts ├── styles ├── globals.css └── Home.module.css ├── components ├── forms │ ├── Field.tsx │ ├── Label.tsx │ ├── SubmitInput.tsx │ ├── TextInput.tsx │ ├── Checkbox.tsx │ ├── TextAreaInput.tsx │ ├── LessonForm.tsx │ └── CourseForm.tsx ├── Banner.tsx ├── EmptyState.tsx ├── layout.tsx ├── CourseGrid.tsx ├── Button.tsx ├── Heading.tsx ├── CourseCard.tsx ├── CourseOverview.tsx ├── Footer.tsx ├── Nav.tsx └── CourseViewer.tsx ├── next.config.js ├── types ├── next-auth.d.ts ├── environment.d.ts └── next.d.ts ├── .gitignore ├── pages ├── api │ ├── auth │ │ └── [...nextauth].ts │ ├── lessons │ │ ├── [id] │ │ │ ├── complete.ts │ │ │ └── index.ts │ │ └── index.ts │ ├── courses.ts │ ├── courses │ │ └── [id].ts │ └── webhooks │ │ └── mux.ts ├── _app.tsx ├── admin │ ├── courses │ │ ├── new.tsx │ │ └── [courseId] │ │ │ ├── lessons │ │ │ ├── [lessonId].tsx │ │ │ └── new.tsx │ │ │ └── index.tsx │ └── index.tsx ├── index.tsx └── courses │ └── [...slug].tsx ├── tsconfig.json ├── .env.example ├── package.json ├── tailwind.config.js ├── prisma └── schema.prisma └── README.md /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "prisma.prisma" 4 | ] 5 | } -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/muxinc/video-course-starter-kit/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /public/images/mux-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/muxinc/video-course-starter-kit/HEAD/public/images/mux-logo.png -------------------------------------------------------------------------------- /screenshots/github-oauth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/muxinc/video-course-starter-kit/HEAD/screenshots/github-oauth.png -------------------------------------------------------------------------------- /utils/webhooks/mux/types/index.ts: -------------------------------------------------------------------------------- 1 | import video from "./video" 2 | 3 | const dict = { video } 4 | 5 | export default dict; -------------------------------------------------------------------------------- /utils/webhooks/mux/types/video/index.ts: -------------------------------------------------------------------------------- 1 | import asset from "./asset" 2 | 3 | const dict = { asset } 4 | 5 | export default dict; -------------------------------------------------------------------------------- /public/images/pscale-connect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/muxinc/video-course-starter-kit/HEAD/public/images/pscale-connect.png -------------------------------------------------------------------------------- /public/images/pscale-prisma.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/muxinc/video-course-starter-kit/HEAD/public/images/pscale-prisma.png -------------------------------------------------------------------------------- /public/images/pscale-string.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/muxinc/video-course-starter-kit/HEAD/public/images/pscale-string.png -------------------------------------------------------------------------------- /styles/globals.css: -------------------------------------------------------------------------------- 1 | @import url("/fonts/stylesheet.css"); 2 | 3 | @tailwind base; 4 | @tailwind components; 5 | @tailwind utilities; -------------------------------------------------------------------------------- /utils/formatDuration.ts: -------------------------------------------------------------------------------- 1 | const fmtMSS = (s: number) => (s - (s %= 60)) / 60 + (9 < s ? ":" : ":0") + s; 2 | export default fmtMSS 3 | -------------------------------------------------------------------------------- /public/fonts/CalSans-SemiBold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/muxinc/video-course-starter-kit/HEAD/public/fonts/CalSans-SemiBold.woff -------------------------------------------------------------------------------- /public/fonts/CalSans-SemiBold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/muxinc/video-course-starter-kit/HEAD/public/fonts/CalSans-SemiBold.woff2 -------------------------------------------------------------------------------- /utils/webhooks/mux/types/video/asset/index.ts: -------------------------------------------------------------------------------- 1 | import created from "./created" 2 | import ready from "./ready" 3 | 4 | const dict = { created, ready } 5 | 6 | export default dict; -------------------------------------------------------------------------------- /utils/db.ts: -------------------------------------------------------------------------------- 1 | import { connect } from '@planetscale/database' 2 | 3 | const config = { 4 | host: process.env.DATABASE_URL, 5 | } 6 | 7 | const conn = connect(config) 8 | 9 | export default conn; -------------------------------------------------------------------------------- /components/forms/Field.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const Field = ({ children }: { children: React.ReactNode }) => ( 4 |
5 | {children} 6 |
7 | ) 8 | 9 | export default Field; -------------------------------------------------------------------------------- /public/fonts/stylesheet.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: "Cal Sans"; 3 | src: url("CalSans-SemiBold.woff2") format("woff2"), 4 | url("CalSans-SemiBold.woff") format("woff"); 5 | font-weight: 600; 6 | font-style: normal; 7 | font-display: swap; 8 | } 9 | -------------------------------------------------------------------------------- /components/forms/Label.tsx: -------------------------------------------------------------------------------- 1 | type Props = { 2 | htmlFor: string; 3 | children: React.ReactNode; 4 | } 5 | 6 | const Label = ({ htmlFor, children }: Props) => ( 7 | 8 | ) 9 | 10 | export default Label; -------------------------------------------------------------------------------- /components/Banner.tsx: -------------------------------------------------------------------------------- 1 | type Props = { 2 | children: React.ReactNode; 3 | }; 4 | 5 | const Banner = ({ children }: Props) => { 6 | return ( 7 |
8 | {children} 9 |
10 | ); 11 | }; 12 | 13 | export default Banner; 14 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: true, 4 | swcMinify: true, 5 | images: { 6 | remotePatterns: [ 7 | { 8 | protocol: "https", 9 | hostname: "image.mux.com", 10 | }, 11 | ], 12 | }, 13 | }; 14 | 15 | module.exports = nextConfig; 16 | -------------------------------------------------------------------------------- /components/EmptyState.tsx: -------------------------------------------------------------------------------- 1 | type Props = { 2 | children: React.ReactNode; 3 | }; 4 | 5 | const Banner = ({ children }: Props) => { 6 | return ( 7 |
8 | {children} 9 |
10 | ); 11 | }; 12 | 13 | export default Banner; 14 | -------------------------------------------------------------------------------- /components/layout.tsx: -------------------------------------------------------------------------------- 1 | import Footer from './Footer' 2 | import Nav from './Nav' 3 | 4 | export default function Layout({ children }: { children: React.ReactNode }) { 5 | return ( 6 | <> 7 |