[
  {
    "path": ".eslintrc.json",
    "content": "{\n  \"extends\": \"next/core-web-vitals\"\n}\n"
  },
  {
    "path": ".gitignore",
    "content": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pnp\n.pnp.js\n.yarn/install-state.gz\n\n# testing\n/coverage\n\n# next.js\n/.next/\n/out/\n\n# production\n/build\n\n# misc\n.DS_Store\n*.pem\n\n# debug\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# local env files\n.env*.local\n.env\n\n# vercel\n.vercel\n\n# typescript\n*.tsbuildinfo\nnext-env.d.ts\n"
  },
  {
    "path": "README.md",
    "content": "This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).\n\n## Getting Started\n\nFirst, run the development server:\n\n```bash\nnpm run dev\n# or\nyarn dev\n# or\npnpm dev\n# or\nbun dev\n```\n\nOpen [http://localhost:3000](http://localhost:3000) with your browser to see the result.\n\nYou can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.\n\nThis project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.\n\n## Learn More\n\nTo learn more about Next.js, take a look at the following resources:\n\n- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.\n- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.\n\nYou can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!\n\n## Deploy on Vercel\n\nThe easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.\n\nCheck out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.\n"
  },
  {
    "path": "app/(auth)/layout.tsx",
    "content": "const AuthLayout = ({ children }: { children: React.ReactNode }) => {\n  return (\n    <div className=\"h-full flex items-center justify-center\">{children}</div>\n  )\n}\n\nexport default AuthLayout"
  },
  {
    "path": "app/(auth)/sign-in/[[...sign-in]]/page.tsx",
    "content": "import { SignIn } from \"@clerk/nextjs\";\n\nexport default function Page() {\n  return <SignIn />;\n}"
  },
  {
    "path": "app/(auth)/sign-up/[[...sign-up]]/page.tsx",
    "content": "import { SignUp } from \"@clerk/nextjs\";\n\nexport default function Page() {\n  return <SignUp />;\n}"
  },
  {
    "path": "app/(course)/courses/[courseId]/layout.tsx",
    "content": "import CourseSideBar from \"@/components/layout/CourseSideBar\";\nimport Topbar from \"@/components/layout/Topbar\";\nimport { db } from \"@/lib/db\";\nimport { auth } from \"@clerk/nextjs/server\";\nimport { redirect } from \"next/navigation\";\n\nconst CourseDetailsLayout = async ({\n  children,\n  params,\n}: {\n  children: React.ReactNode;\n  params: { courseId: string };\n}) => {\n  const { userId } = auth();\n\n  if (!userId) {\n    return redirect(\"/sign-in\");\n  }\n\n  const course = await db.course.findUnique({\n    where: {\n      id: params.courseId,\n    },\n    include: {\n      sections: {\n        where: {\n          isPublished: true,\n        },\n        orderBy: {\n          position: \"asc\",\n        },\n      },\n    },\n  });\n\n  if (!course) {\n    return redirect(\"/\");\n  }\n\n  return (\n    <div className=\"h-full flex flex-col\">\n      <Topbar />\n      <div className=\"flex-1 flex\">\n        <CourseSideBar course={course} studentId={userId} />\n        <div className=\"flex-1\">{children}</div>\n      </div>\n    </div>\n  );\n};\n\nexport default CourseDetailsLayout;\n"
  },
  {
    "path": "app/(course)/courses/[courseId]/overview/page.tsx",
    "content": "import { clerkClient } from \"@clerk/nextjs/server\";\nimport Image from \"next/image\";\nimport { redirect } from \"next/navigation\";\n\nimport { db } from \"@/lib/db\";\nimport ReadText from \"@/components/custom/ReadText\";\nimport SectionMenu from \"@/components/layout/SectionMenu\";\n\nconst CourseOverview = async ({ params }: { params: { courseId: string } }) => {\n  const course = await db.course.findUnique({\n    where: {\n      id: params.courseId,\n      isPublished: true,\n    },\n    include: {\n      sections: {\n        where: {\n          isPublished: true,\n        },\n      },\n    },\n  });\n\n  if (!course) {\n    return redirect(\"/\");\n  }\n\n  const instructor = await clerkClient.users.getUser(course.instructorId);\n\n  let level;\n\n  if (course.levelId) {\n    level = await db.level.findUnique({\n      where: {\n        id: course.levelId,\n      },\n    });\n  }\n\n  return (\n    <div className=\"px-6 py-4 flex flex-col gap-5 text-sm\">\n      <div className=\"flex justify-between\">\n        <h1 className=\"text-2xl font-bold\">{course.title}</h1>\n        <SectionMenu course={course} />\n      </div>\n\n      <p className=\"font-medium\">{course.subtitle}</p>\n\n      <div className=\"flex gap-2 items-center\">\n        <Image\n          src={\n            instructor.imageUrl\n              ? instructor.imageUrl\n              : \"/avatar_placeholder.jpg\"\n          }\n          alt={instructor.fullName ? instructor.fullName : \"Instructor photo\"}\n          width={30}\n          height={30}\n          className=\"rounded-full\"\n        />\n        <p className=\"font-bold\">Instructor:</p>\n        <p>{instructor.fullName}</p>\n      </div>\n\n      <div className=\"flex gap-2\">\n        <p className=\"font-bold\">Price:</p>\n        <p>${course.price}</p>\n      </div>\n\n      <div className=\"flex gap-2\">\n        <p className=\"font-bold\">Level:</p>\n        <p>{level?.name}</p>\n      </div>\n\n      <div className=\"flex flex-col gap-2\">\n        <p className=\"font-bold\">Description:</p>\n        <ReadText value={course.description!} />\n      </div>\n    </div>\n  );\n};\n\nexport default CourseOverview;\n"
  },
  {
    "path": "app/(course)/courses/[courseId]/sections/[sectionId]/page.tsx",
    "content": "import SectionsDetails from \"@/components/sections/SectionsDetails\";\nimport { db } from \"@/lib/db\";\nimport { auth } from \"@clerk/nextjs/server\";\nimport { Resource } from \"@prisma/client\";\nimport { redirect } from \"next/navigation\";\n\nconst SectionDetailsPage = async ({\n  params,\n}: {\n  params: { courseId: string; sectionId: string };\n}) => {\n  const { courseId, sectionId } = params;\n  const { userId } = auth();\n\n  if (!userId) {\n    return redirect(\"/sign-in\");\n  }\n\n  const course = await db.course.findUnique({\n    where: {\n      id: courseId,\n      isPublished: true,\n    },\n    include: {\n      sections: {\n        where: {\n          isPublished: true,\n        },\n      },\n    },\n  });\n\n  if (!course) {\n    return redirect(\"/\");\n  }\n\n  const section = await db.section.findUnique({\n    where: {\n      id: sectionId,\n      courseId,\n      isPublished: true,\n    },\n  });\n\n  if (!section) {\n    return redirect(`/courses/${courseId}/overview`);\n  }\n\n  const purchase = await db.purchase.findUnique({\n    where: {\n      customerId_courseId: {\n        customerId: userId,\n        courseId,\n      },\n    },\n  });\n\n  let muxData = null;\n  let resources: Resource[] = [];\n\n  if (section.isFree || purchase) {\n    muxData = await db.muxData.findUnique({\n      where: {\n        sectionId,\n      },\n    });\n  }\n\n  if (purchase) {\n    resources = await db.resource.findMany({\n      where: {\n        sectionId,\n      },\n    });\n  }\n\n  const progress = await db.progress.findUnique({\n    where: {\n      studentId_sectionId: {\n        studentId: userId,\n        sectionId,\n      },\n    },\n  });\n\n  return (\n    <SectionsDetails\n      course={course}\n      section={section}\n      purchase={purchase}\n      muxData={muxData}\n      resources={resources}\n      progress={progress}\n    />\n  );\n};\n\nexport default SectionDetailsPage;\n"
  },
  {
    "path": "app/(home)/categories/[categoryId]/page.tsx",
    "content": "import getCoursesByCategory from \"@/app/actions/getCourses\";\nimport CourseCard from \"@/components/courses/CourseCard\";\nimport Categories from \"@/components/custom/Categories\";\nimport { db } from \"@/lib/db\";\n\nconst CoursesByCategory = async ({\n  params,\n}: {\n  params: { categoryId: string };\n}) => {\n  const categories = await db.category.findMany({\n    orderBy: {\n      name: \"asc\",\n    },\n  });\n\n  const courses = await getCoursesByCategory(params.categoryId);\n\n  return (\n    <div className=\"md:mt-5 md:px-10 xl:px-16 pb-16\">\n      <Categories categories={categories} selectedCategory={params.categoryId} />\n      <div className=\"flex flex-wrap gap-7 justify-center\">\n        {courses.map((course) => (\n          <CourseCard key={course.id} course={course} />\n        ))}\n      </div>\n    </div>\n  );\n};\n\nexport default CoursesByCategory;\n"
  },
  {
    "path": "app/(home)/layout.tsx",
    "content": "import Topbar from \"@/components/layout/Topbar\"\n\nconst HomeLayout = ({ children }: { children: React.ReactNode }) => {\n  return (\n    <>\n      <Topbar />\n      {children}\n    </>\n  )\n}\n\nexport default HomeLayout"
  },
  {
    "path": "app/(home)/learning/page.tsx",
    "content": "import CourseCard from \"@/components/courses/CourseCard\"\nimport { db } from \"@/lib/db\"\nimport { auth } from \"@clerk/nextjs/server\"\nimport { redirect } from \"next/navigation\"\n\nconst LearningPage = async () => {\n  const { userId } = auth()\n\n  if (!userId) {\n    return redirect('/sign-in')\n  }\n\n  const purchasedCourses = await db.purchase.findMany({\n    where: {\n      customerId: userId,\n    },\n    select: {\n      course: {\n        include: {\n          category: true,\n          subCategory: true,\n          sections: {\n            where: {\n              isPublished: true,\n            },\n          }\n        }\n      }\n    }\n  })\n\n  return (\n    <div className=\"px-4 py-6 md:mt-5 md:px-10 xl:px-16\">\n      <h1 className=\"text-2xl font-bold\">\n        Your courses\n      </h1>\n      <div className=\"flex flex-wrap gap-7 mt-7\">\n        {purchasedCourses.map((purchase) => (\n          <CourseCard key={purchase.course.id} course={purchase.course} />\n        ))}\n      </div>\n    </div>\n  )\n}\n\nexport default LearningPage"
  },
  {
    "path": "app/(home)/page.tsx",
    "content": "import { db } from \"@/lib/db\";\nimport getCoursesByCategory from \"../actions/getCourses\";\nimport Categories from \"@/components/custom/Categories\";\nimport CourseCard from \"@/components/courses/CourseCard\";\n\nexport default async function Home() {\n  const categories = await db.category.findMany({\n    orderBy: {\n      name: \"asc\",\n    },\n    include: {\n      subCategories: {\n        orderBy: {\n          name: \"asc\",\n        },\n      },\n    },\n  });\n\n  const courses = await getCoursesByCategory(null);\n  return (\n    <div className=\"md:mt-5 md:px-10 xl:px-16 pb-16\">\n      <Categories categories={categories} selectedCategory={null} />\n      <div className=\"flex flex-wrap gap-7 justify-center\">\n        {courses.map((course) => (\n          <CourseCard key={course.id} course={course} />\n        ))}\n      </div>\n      \n    </div>\n  );\n}\n"
  },
  {
    "path": "app/(home)/search/page.tsx",
    "content": "import CourseCard from \"@/components/courses/CourseCard\";\nimport { db } from \"@/lib/db\"\n\nconst SearchPage = async ({ searchParams }: { searchParams: { query: string }}) => {\n  const queryText = searchParams.query || ''\n  const courses = await db.course.findMany({\n    where: {\n      isPublished: true,\n      OR: [\n        { title: { contains: queryText } },\n        { category: { name: { contains: queryText } }},\n        { subCategory: { name: { contains: queryText } }}\n      ]\n    },\n    include: {\n      category: true,\n      subCategory: true,\n      level: true,\n      sections: {\n        where: {\n          isPublished: true,\n        }\n      }\n    },\n    orderBy: {\n      createdAt: 'desc'\n    }\n  });\n\n  return (\n    <div className=\"px-4 py-6 md:px-10 xl:px-16\">\n      <p className=\"text-lg md:text-2xl font-semibold mb-10\">Recommended courses for {queryText}</p>\n      <div className=\"flex gap-4 flex-wrap\">\n        {courses.map((course) => (\n          <CourseCard key={course.id} course={course} />\n        ))}\n      </div>\n    </div>\n  )\n}\n\nexport default SearchPage"
  },
  {
    "path": "app/(instructor)/instructor/courses/[courseId]/basic/page.tsx",
    "content": "import EditCourseForm from \"@/components/courses/EditCourseForm\";\nimport AlertBanner from \"@/components/custom/AlertBanner\";\nimport { db } from \"@/lib/db\";\nimport { auth } from \"@clerk/nextjs/server\";\nimport { redirect } from \"next/navigation\";\n\nconst CourseBasics = async ({ params }: { params: { courseId: string } }) => {\n  const { userId } = auth();\n\n  if (!userId) {\n    return redirect(\"/sign-in\");\n  }\n\n  const course = await db.course.findUnique({\n    where: {\n      id: params.courseId,\n      instructorId: userId,\n    },\n    include: {\n      sections: true,\n    },\n  });\n\n  if (!course) {\n    return redirect(\"/instructor/courses\");\n  }\n\n  const categories = await db.category.findMany({\n    orderBy: {\n      name: \"asc\",\n    },\n    include: {\n      subCategories: true,\n    },\n  });\n\n  const levels = await db.level.findMany();\n\n  const requiredFields = [\n    course.title,\n    course.description,\n    course.categoryId,\n    course.subCategoryId,\n    course.levelId,\n    course.imageUrl,\n    course.price,\n    course.sections.some((section) => section.isPublished),\n  ];\n  const requiredFieldsCount = requiredFields.length;\n  const missingFields = requiredFields.filter((field) => !Boolean(field));\n  const missingFieldsCount = missingFields.length;\n  const isCompleted = requiredFields.every(Boolean);\n\n  return (\n    <div className=\"px-10\">\n      <AlertBanner\n        isCompleted={isCompleted}\n        missingFieldsCount={missingFieldsCount}\n        requiredFieldsCount={requiredFieldsCount}\n      />\n      <EditCourseForm\n        course={course}\n        categories={categories.map((category) => ({\n          label: category.name,\n          value: category.id,\n          subCategories: category.subCategories.map((subcategory) => ({\n            label: subcategory.name,\n            value: subcategory.id,\n          })),\n        }))}\n        levels={levels.map((level) => ({\n          label: level.name,\n          value: level.id,\n        }))}\n        isCompleted={isCompleted}\n      />\n    </div>\n  );\n};\n\nexport default CourseBasics;\n"
  },
  {
    "path": "app/(instructor)/instructor/courses/[courseId]/sections/[sectionId]/page.tsx",
    "content": "import AlertBanner from \"@/components/custom/AlertBanner\";\nimport EditSectionForm from \"@/components/sections/EditSectionForm\";\nimport { db } from \"@/lib/db\";\nimport { auth } from \"@clerk/nextjs/server\";\nimport { redirect } from \"next/navigation\";\n\nconst SectionDetailsPage = async ({\n  params,\n}: {\n  params: { courseId: string; sectionId: string };\n}) => {\n  const { userId } = auth();\n\n  if (!userId) {\n    return redirect(\"/sign-in\");\n  }\n\n  const course = await db.course.findUnique({\n    where: {\n      id: params.courseId,\n      instructorId: userId,\n    },\n  });\n\n  if (!course) {\n    return redirect(\"/instructor/courses\");\n  }\n\n  const section = await db.section.findUnique({\n    where: {\n      id: params.sectionId,\n      courseId: params.courseId,\n    },\n    include: {\n      muxData: true,\n      resources: true,\n    },\n  });\n\n  if (!section) {\n    return redirect(`/instructor/courses/${params.courseId}/sections`);\n  }\n\n  const requiredFields = [section.title, section.description, section.videoUrl];\n  const requiredFieldsCount = requiredFields.length;\n  const missingFields = requiredFields.filter((field) => !Boolean(field)); // Return falsy values: undefined, null, 0, false, NaN, ''\n  const missingFieldsCount = missingFields.length;\n  const isCompleted = requiredFields.every(Boolean);\n\n  return (\n    <div className=\"px-10\">\n      <AlertBanner\n        isCompleted={isCompleted}\n        requiredFieldsCount={requiredFieldsCount}\n        missingFieldsCount={missingFieldsCount}\n      />\n      <EditSectionForm\n        section={section}\n        courseId={params.courseId}\n        isCompleted={isCompleted}\n      />\n    </div>\n  );\n};\n\nexport default SectionDetailsPage;\n"
  },
  {
    "path": "app/(instructor)/instructor/courses/[courseId]/sections/page.tsx",
    "content": "import { auth } from \"@clerk/nextjs/server\";\nimport { redirect } from \"next/navigation\";\n\nimport CreateSectionForm from \"@/components/sections/CreateSectionForm\";\nimport { db } from \"@/lib/db\";\n\nconst CourseCurriculumPage = async ({ params }: { params: { courseId: string }}) => {\n  const { userId } = auth()\n\n  if (!userId) {\n    return redirect(\"/sign-in\")\n  }\n\n  const course = await db.course.findUnique({\n    where: {\n      id: params.courseId,\n      instructorId: userId,\n    },\n    include: {\n      sections: {\n        orderBy: {\n          position: \"asc\",\n        },\n      },\n    },\n  });\n\n  if (!course) {\n    return redirect(\"/instructor/courses\")\n  }\n\n  return (\n    <CreateSectionForm course={course} />\n  );\n}\n\nexport default CourseCurriculumPage;"
  },
  {
    "path": "app/(instructor)/instructor/courses/page.tsx",
    "content": "import { auth } from \"@clerk/nextjs/server\";\nimport Link from \"next/link\";\nimport { redirect } from \"next/navigation\";\n\nimport { Button } from \"@/components/ui/button\";\nimport { db } from \"@/lib/db\";\nimport { DataTable } from \"@/components/custom/DataTable\";\nimport { columns } from \"@/components/courses/Columns\";\n\nconst CoursesPage = async () => {\n  const { userId } = auth();\n\n  if (!userId) {\n    return redirect(\"/sign-in\");\n  }\n\n  const courses = await db.course.findMany({\n    where: {\n      instructorId: userId,\n    },\n    orderBy: {\n      createdAt: \"desc\",\n    },\n  });\n\n  return (\n    <div className=\"px-6 py-4\">\n      <Link href=\"/instructor/create-course\">\n        <Button>Create New Course</Button>\n      </Link>\n\n      <div className=\"mt-5\">\n        <DataTable columns={columns} data={courses} />\n      </div>\n    </div>\n  );\n};\n\nexport default CoursesPage;\n"
  },
  {
    "path": "app/(instructor)/instructor/create-course/page.tsx",
    "content": "import CreateCourseForm from \"@/components/courses/CreateCourseForm\"\nimport { db } from \"@/lib/db\"\n\nconst CreateCoursePage = async () => {\n  const categories = await db.category.findMany({\n    orderBy: {\n      name: \"asc\"\n    },\n    include: {\n      subCategories: true\n    }\n  })\n\n  return (\n    <div>\n      <CreateCourseForm categories={categories.map((category) => ({\n        label: category.name,\n        value: category.id,\n        subCategories: category.subCategories.map((subcategory) => ({\n          label: subcategory.name,\n          value: subcategory.id\n        }))\n      }))} />\n    </div>\n  )\n}\n\nexport default CreateCoursePage"
  },
  {
    "path": "app/(instructor)/instructor/performance/page.tsx",
    "content": "import { getPerformance } from \"@/app/actions/getPerformance\"\nimport Chart from \"@/components/performance/Chart\"\nimport DataCard from \"@/components/performance/DataCard\"\nimport { auth } from \"@clerk/nextjs/server\"\nimport { redirect } from \"next/navigation\"\n\nconst PerformancePage = async () => {\n  const { userId } = auth()\n\n  if (!userId) {\n    return redirect(\"/sign-in\")\n  }\n\n  const { data, totalRevenue, totalSales } = await getPerformance(userId)\n\n  return (\n    <div className=\"p-6\">\n      <div className=\"grid grid-cols-1 md:grid-cols-2 gap-4 mb-4\">\n        <DataCard value={totalRevenue} label=\"Total Revenue\" shouldFormat />\n        <DataCard value={totalSales} label=\"Total Sales\" />\n        <Chart data={data} />\n      </div>\n    </div>\n  )\n}\n\nexport default PerformancePage"
  },
  {
    "path": "app/(instructor)/layout.tsx",
    "content": "import { auth } from \"@clerk/nextjs/server\";\nimport { redirect } from \"next/navigation\";\n\nimport Topbar from \"@/components/layout/Topbar\";\nimport Sidebar from \"@/components/layout/Sidebar\";\n\nconst InstructorLayout = ({ children }: { children: React.ReactNode }) => {\n  const { userId } = auth()\n\n  if (!userId) {\n    return redirect(\"/sign-in\")\n  }\n\n  return (\n    <div className=\"h-full flex flex-col\">\n      <Topbar />\n      <div className=\"flex-1 flex\">\n        <Sidebar />\n        <div className=\"flex-1\">{children}</div>\n      </div>\n    </div>\n  );\n};\n\nexport default InstructorLayout;\n"
  },
  {
    "path": "app/actions/getCourses.tsx",
    "content": "import { db } from \"@/lib/db\"\nimport { Course } from \"@prisma/client\"\n\nconst getCoursesByCategory = async (categoryId: string | null): Promise<Course[]> => {\n  const whereClause: any = {\n    ...(categoryId ? { categoryId, isPublished: true } : { isPublished: true }),\n  }\n  const courses = await db.course.findMany({\n    where: whereClause,\n    include: {\n      category: true,\n      subCategory: true,\n      level: true,\n      sections: {\n        where: {\n          isPublished: true,\n        }\n      },\n    },\n    orderBy: {\n      createdAt: \"desc\",\n    },\n  })\n\n  return courses\n}\n\nexport default getCoursesByCategory"
  },
  {
    "path": "app/actions/getPerformance.ts",
    "content": "import { db } from \"@/lib/db\";\nimport { Course, Purchase } from \"@prisma/client\";\n\ntype PurchaseWithCourse = Purchase & { course: Course };\n\nconst groupByCourse = (purchases: PurchaseWithCourse[]) => {\n  const grouped: { [courseTitle: string]: { total: number; count: number } } =\n    {};\n\n  purchases.forEach((purchase) => {\n    const courseTitle = purchase.course.title;\n    if (!grouped[courseTitle]) {\n      grouped[courseTitle] = { total: 0, count: 0 };\n    }\n    grouped[courseTitle].total += purchase.course.price!;\n    grouped[courseTitle].count += 1;\n  });\n\n  return grouped;\n};\n\nexport const getPerformance = async (userId: string) => {\n  try {\n    const purchases = await db.purchase.findMany({\n      where: { course: { instructorId: userId } },\n      include: { course: true },\n    });\n\n    const groupedEarnings = groupByCourse(purchases); \n    \n    const data = Object.entries(groupedEarnings).map(\n      ([courseTitle, { total, count }]) => ({\n        name: courseTitle,\n        total,\n        count,\n      })\n    );\n\n    const totalRevenue = data.reduce((acc, current) => acc + current.total, 0);\n    const totalSales = purchases.length\n\n    return {\n      data,\n      totalRevenue,\n      totalSales,\n    };\n  } catch (err) {\n    console.log(\"[getPerformance]\", err);\n    return {\n      data: [],\n      totalRevenue: 0,\n      totalSales: 0,\n    };\n  }\n};\n"
  },
  {
    "path": "app/api/courses/[courseId]/checkout/route.ts",
    "content": "import Stripe from \"stripe\";\nimport { NextRequest, NextResponse } from \"next/server\";\nimport { currentUser } from \"@clerk/nextjs/server\";\n\nimport { db } from \"@/lib/db\";\nimport { stripe } from \"@/lib/stripe\";\n\nexport const POST = async (\n  req: NextRequest,\n  { params }: { params: { courseId: string } }\n) => {\n  try {\n    const user = await currentUser();\n\n    if (!user || !user.id || !user.emailAddresses?.[0]?.emailAddress) {\n      return new NextResponse(\"Unauthorized\", { status: 401 });\n    }\n\n    const course = await db.course.findUnique({\n      where: { id: params.courseId, isPublished: true },\n    });\n\n    if (!course) {\n      return new NextResponse(\"Course Not Found\", { status: 404 });\n    }\n\n    const purchase = await db.purchase.findUnique({\n      where: {\n        customerId_courseId: { customerId: user.id, courseId: course.id },\n      },\n    });\n\n    if (purchase) {\n      return new NextResponse(\"Course Already Purchased\", { status: 400 });\n    }\n\n    const line_items: Stripe.Checkout.SessionCreateParams.LineItem[] = [\n      {\n        quantity: 1,\n        price_data: {\n          currency: \"cad\",\n          product_data: {\n            name: course.title,\n          },\n          unit_amount: Math.round(course.price! * 100),\n        },\n      }\n    ]\n\n    let stripeCustomer = await db.stripeCustomer.findUnique({\n      where: { customerId: user.id },\n      select: { stripeCustomerId: true },\n    });\n\n    if (!stripeCustomer) {\n      const customer = await stripe.customers.create({\n        email: user.emailAddresses[0].emailAddress,\n      });\n\n      stripeCustomer = await db.stripeCustomer.create({\n        data: {\n          customerId: user.id,\n          stripeCustomerId: customer.id,\n        },\n      });\n    }\n\n    const session = await stripe.checkout.sessions.create({\n      customer: stripeCustomer.stripeCustomerId,\n      payment_method_types: [\"card\"],\n      line_items,\n      mode: \"payment\",\n      success_url: `${process.env.NEXT_PUBLIC_BASE_URL}/courses/${course.id}/overview?success=true`,\n      cancel_url: `${process.env.NEXT_PUBLIC_BASE_URL}/courses/${course.id}/overview?canceled=true`,\n      metadata: {\n        courseId: course.id,\n        customerId: user.id,\n      }\n    });\n\n    return NextResponse.json({ url: session.url })\n  } catch (err) {\n    console.log(\"[courseId_checkout_POST]\", err);\n    return new NextResponse(\"Internal Server Error\", { status: 500 });\n  }\n};\n"
  },
  {
    "path": "app/api/courses/[courseId]/publish/route.ts",
    "content": "import { db } from \"@/lib/db\";\nimport { auth } from \"@clerk/nextjs/server\";\nimport { NextRequest, NextResponse } from \"next/server\";\n\nexport const POST = async (\n  req: NextRequest,\n  { params }: { params: { courseId: string } }\n) => {\n  try {\n    const { userId } = auth();\n    const { courseId } = params;\n\n    if (!userId) {\n      return new Response(\"Unauthorized\", { status: 401 });\n    }\n\n    const course = await db.course.findUnique({\n      where: { id: courseId, instructorId: userId },\n      include: {\n        sections: {\n          include: {\n            muxData: true,\n          },\n        },\n      },\n    });\n\n    if (!course) {\n      return new Response(\"Course not found\", { status: 404 });\n    }\n\n    const isPublishedSections = course.sections.some(\n      (section) => section.isPublished\n    );\n\n    if (\n      !course.title ||\n      !course.description ||\n      !course.categoryId ||\n      !course.subCategoryId ||\n      !course.levelId ||\n      !course.imageUrl ||\n      !course.price ||\n      !isPublishedSections\n    ) {\n      return new NextResponse(\"Missing required fields\", { status: 400 });\n    }\n\n    const pusblishedCourse = await db.course.update({\n      where: { id: courseId, instructorId: userId },\n      data: { isPublished: true },\n    });\n\n    return NextResponse.json(pusblishedCourse, { status: 200 });\n  } catch (err) {\n    console.log(\"[courseId_publish_POST]\", err);\n    return new Response(\"Internal Server Error\", { status: 500 });\n  }\n};\n"
  },
  {
    "path": "app/api/courses/[courseId]/route.ts",
    "content": "import { db } from \"@/lib/db\";\nimport { auth } from \"@clerk/nextjs/server\";\nimport { NextRequest, NextResponse } from \"next/server\";\nimport Mux from \"@mux/mux-node\";\n\nconst { video } = new Mux({\n  tokenId: process.env.MUX_TOKEN_ID,\n  tokenSecret: process.env.MUX_TOKEN_SECRET,\n});\n\nexport const PATCH = async (\n  req: NextRequest,\n  { params }: { params: { courseId: string } }\n) => {\n  try {\n    const { userId } = auth();\n    const { courseId } = params;\n    const values = await req.json();\n\n    if (!userId) {\n      return new Response(\"Unauthorized\", { status: 401 });\n    }\n\n    const course = await db.course.update({\n      where: { id: courseId, instructorId: userId },\n      data: { ...values },\n    });\n\n    return NextResponse.json(course, { status: 200 });\n  } catch (err) {\n    console.error([\"courseId_PATCH\", err]);\n    return new Response(\"Internal Server Error\", { status: 500 });\n  }\n};\n\nexport const DELETE = async (\n  req: NextRequest,\n  { params }: { params: { courseId: string } }\n) => {\n  try {\n    const { userId } = auth();\n    const { courseId } = params;\n\n    if (!userId) {\n      return new NextResponse(\"Unauthorized\", { status: 401 });\n    }\n\n    const course = await db.course.findUnique({\n      where: { id: courseId, instructorId: userId},\n      include: {\n        sections: {\n          include: {\n            muxData: true,\n          }\n        }\n      }\n    });\n\n    if (!course) {\n      return new NextResponse(\"Course not found\", { status: 404 });\n    }\n\n    for (const section of course.sections) {\n      if (section.muxData?.assetId) {\n        await video.assets.delete(section.muxData.assetId);\n      }\n    }\n\n    await db.course.delete({\n      where: { id: courseId, instructorId: userId },\n    });\n\n    return new NextResponse(\"Course Deleted\", { status: 200 });\n  } catch (err) {\n    console.error([\"courseId_DELETE\", err]);\n    return new NextResponse(\"Internal Server Error\", { status: 500 });\n  }\n};\n"
  },
  {
    "path": "app/api/courses/[courseId]/sections/[sectionId]/progress/route.ts",
    "content": "import { db } from \"@/lib/db\";\nimport { auth } from \"@clerk/nextjs/server\";\nimport { NextRequest, NextResponse } from \"next/server\";\n\nexport const POST = async (\n  req: NextRequest,\n  { params }: { params: { courseId: string; sectionId: string } }\n) => {\n  try {\n    const { userId } = auth();\n    const { isCompleted } = await req.json();\n\n    if (!userId) {\n      return new NextResponse(\"Unauthorized\", { status: 401 });\n    }\n\n    const { courseId, sectionId } = params;\n\n    const course = await db.course.findUnique({\n      where: {\n        id: courseId,\n      },\n    });\n\n    if (!course) {\n      return new NextResponse(\"Course Not Found\", { status: 404 });\n    }\n\n    const section = await db.section.findUnique({\n      where: {\n        id: sectionId,\n        courseId,\n      },\n    });\n\n    if (!section) {\n      return new NextResponse(\"Section Not Found\", { status: 404 });\n    }\n\n    let progress = await db.progress.findUnique({\n      where: {\n        studentId_sectionId: {\n          studentId: userId,\n          sectionId,\n        },\n      },\n    });\n\n    if (progress) {\n      progress = await db.progress.update({\n        where: {\n          studentId_sectionId: {\n            studentId: userId,\n            sectionId,\n          },\n        },\n        data: {\n          isCompleted,\n        },\n      });\n    } else {\n      progress = await db.progress.create({\n        data: {\n          studentId: userId,\n          sectionId,\n          isCompleted,\n        },\n      });\n    }\n\n    return NextResponse.json(progress, { status: 200 });\n  } catch (err) {\n    console.log(\"[sectionId_progress_POST]\", err);\n    return new NextResponse(\"Internal Server Error\", { status: 500 });\n  }\n};\n"
  },
  {
    "path": "app/api/courses/[courseId]/sections/[sectionId]/publish/route.ts",
    "content": "import { db } from \"@/lib/db\";\nimport { auth } from \"@clerk/nextjs/server\";\nimport { NextRequest, NextResponse } from \"next/server\";\n\nexport const POST = async (\n  req: NextRequest,\n  { params }: { params: { courseId: string; sectionId: string } }\n) => {\n  try {\n    const { userId } = auth();\n\n    if (!userId) {\n      return new NextResponse(\"Unauthorized\", { status: 401 });\n    }\n\n    const { courseId, sectionId } = params;\n\n    const course = await db.course.findUnique({\n      where: {\n        id: courseId,\n        instructorId: userId,\n      },\n    });\n\n    if (!course) {\n      return new NextResponse(\"Course Not Found\", { status: 404 });\n    }\n\n    const section = await db.section.findUnique({\n      where: {\n        id: sectionId,\n        courseId,\n      },\n    });\n\n    const muxData = await db.muxData.findUnique({\n      where: {\n        sectionId,\n      },\n    });\n\n    if (!section || !muxData || !section.title || !section.description || !section.videoUrl) {\n      return new NextResponse(\"Missing required fields\", { status: 400 });\n    }\n\n    const publishedSection = await db.section.update({\n      where: {\n        id: sectionId,\n        courseId,\n      },\n      data: {\n        isPublished: true,\n      },\n    });\n\n    return NextResponse.json(publishedSection, { status: 200 });\n  } catch (err) {\n    console.log(\"[section_publish_POST]\", err)\n    return new NextResponse(\"Internal Server Error\", { status: 500 });\n  }\n}"
  },
  {
    "path": "app/api/courses/[courseId]/sections/[sectionId]/resources/[resourceId]/route.ts",
    "content": "import { db } from \"@/lib/db\";\nimport { auth } from \"@clerk/nextjs/server\";\nimport { NextRequest, NextResponse } from \"next/server\";\n\nexport const POST = async (\n  req: NextRequest,\n  { params }: { params: { courseId: string; sectionId: string, resourceId: string } }\n) => {\n  try {\n    const { userId } = auth()\n\n    if (!userId) {\n      return new NextResponse(\"Unauthorized\", { status: 401 });\n    }\n\n    const { courseId, sectionId, resourceId } = params;\n\n    const course = await db.course.findUnique({\n      where: {\n        id: courseId,\n        instructorId: userId,\n      },\n    });\n\n    if (!course) {\n      return new NextResponse(\"Course Not Found\", { status: 404 });\n    }\n\n    const section = await db.section.findUnique({\n      where: {\n        id: sectionId,\n        courseId,\n      },\n    });\n\n    if (!section) {\n      return new NextResponse(\"Section Not Found\", { status: 404 });\n    }\n\n    await db.resource.delete({\n      where: {\n        id: resourceId,\n        sectionId,\n      },\n    });\n    \n    return NextResponse.json(\"Resource deleted\", { status: 200 });\n  } catch (err) {\n    console.log(\"[resourceId_DELETE\", err);\n    return new NextResponse(\"Internal Server Error\", { status: 500 });\n  }\n};\n"
  },
  {
    "path": "app/api/courses/[courseId]/sections/[sectionId]/resources/route.ts",
    "content": "import { db } from \"@/lib/db\";\nimport { auth } from \"@clerk/nextjs/server\";\nimport { NextRequest, NextResponse } from \"next/server\";\n\nexport const POST = async (\n  req: NextRequest,\n  { params }: { params: { courseId: string; sectionId: string } }\n) => {\n  try {\n    const { userId } = auth()\n\n    if (!userId) {\n      return new NextResponse(\"Unauthorized\", { status: 401 });\n    }\n\n    const { courseId, sectionId } = params;\n\n    const course = await db.course.findUnique({\n      where: {\n        id: courseId,\n        instructorId: userId,\n      },\n    });\n\n    if (!course) {\n      return new NextResponse(\"Course Not Found\", { status: 404 });\n    }\n\n    const section = await db.section.findUnique({\n      where: {\n        id: sectionId,\n        courseId,\n      },\n    });\n\n    if (!section) {\n      return new NextResponse(\"Section Not Found\", { status: 404 });\n    }\n\n    const { name, fileUrl } = await req.json();\n\n    const resource = await db.resource.create({\n      data: {\n        name,\n        fileUrl,\n        sectionId,\n      },\n    });\n\n    return NextResponse.json(resource, { status: 200 });\n  } catch (err) {\n    console.log(\"[resources_POST\", err);\n    return new NextResponse(\"Internal Server Error\", { status: 500 });\n  }\n};\n"
  },
  {
    "path": "app/api/courses/[courseId]/sections/[sectionId]/route.ts",
    "content": "import { db } from \"@/lib/db\";\nimport { auth } from \"@clerk/nextjs/server\";\nimport { NextRequest, NextResponse } from \"next/server\";\nimport Mux from \"@mux/mux-node\";\n\nconst { video } = new Mux({\n  tokenId: process.env.MUX_TOKEN_ID,\n  tokenSecret: process.env.MUX_TOKEN_SECRET,\n});\n\nexport const POST = async (\n  req: NextRequest,\n  { params }: { params: { courseId: string; sectionId: string } }\n) => {\n  try {\n    const { userId } = auth();\n\n    if (!userId) {\n      return new NextResponse(\"Unauthorized\", { status: 401 });\n    }\n\n    const values = await req.json();\n\n    const { courseId, sectionId } = params;\n\n    const course = await db.course.findUnique({\n      where: {\n        id: courseId,\n        instructorId: userId,\n      },\n    });\n\n    if (!course) {\n      return new NextResponse(\"Course Not Found\", { status: 404 });\n    }\n\n    const section = await db.section.update({\n      where: {\n        id: sectionId,\n        courseId,\n      },\n      data: {\n        ...values,\n      },\n    });\n\n    if (values.videoUrl) {\n      const existingMuxData = await db.muxData.findFirst({\n        where: {\n          sectionId,\n        },\n      });\n\n      if (existingMuxData) {\n        await video.assets.delete(existingMuxData.assetId);\n        await db.muxData.delete({\n          where: {\n            id: existingMuxData.id,\n          },\n        });\n      }\n\n      const asset = await video.assets.create({\n        input: values.videoUrl,\n        playback_policy: [\"public\"],\n        test: false,\n      });\n\n      await db.muxData.create({\n        data: {\n          assetId: asset.id,\n          playbackId: asset.playback_ids?.[0]?.id,\n          sectionId,\n        },\n      });\n    }\n\n    return NextResponse.json(section, { status: 200 });\n  } catch (err) {\n    console.log(\"[sectionId_POST]\", err);\n    return new NextResponse(\"Internal Server Error\", { status: 500 });\n  }\n};\n\nexport const DELETE = async (req: NextRequest,\n  { params }: { params: { courseId: string; sectionId: string } }\n) => {\n  try {\n    const { userId } = auth();\n\n    if (!userId) {\n      return new NextResponse(\"Unauthorized\", { status: 401 });\n    }\n\n    const { courseId, sectionId } = params;\n\n    const course = await db.course.findUnique({\n      where: {\n        id: courseId,\n        instructorId: userId,\n      },\n    });\n\n    if (!course) {\n      return new NextResponse(\"Course Not Found\", { status: 404 });\n    }\n\n    const section = await db.section.findUnique({\n      where: {\n        id: sectionId,\n        courseId,\n      }\n    });\n\n    if (!section) {\n      return new NextResponse(\"Section Not Found\", { status: 404 });\n    }\n\n    if (section.videoUrl) {\n      const existingMuxData = await db.muxData.findFirst({\n        where: {\n          sectionId,\n        },\n      });\n\n      if (existingMuxData) {\n        await video.assets.delete(existingMuxData.assetId);\n        await db.muxData.delete({\n          where: {\n            id: existingMuxData.id,\n          },\n        });\n      }\n    }\n\n    await db.section.delete({\n      where: {\n        id: sectionId,\n        courseId,\n      },\n    });\n\n    const publishedSectionsInCourse = await db.section.findMany({\n      where: {\n        courseId,\n        isPublished: true,\n      },\n    });\n\n    if (!publishedSectionsInCourse.length) {\n      await db.course.update({\n        where: {\n          id: courseId,\n        },\n        data: {\n          isPublished: false,\n        },\n      });\n    }\n\n    return new NextResponse(\"Section Deleted\", { status: 200 });\n  } catch (err) {\n    console.log(\"[sectionId_DELETE]\", err);\n    return new NextResponse(\"Internal Server Error\", { status: 500 });\n  }\n}\n"
  },
  {
    "path": "app/api/courses/[courseId]/sections/[sectionId]/unpublish/route.ts",
    "content": "import { db } from \"@/lib/db\";\nimport { auth } from \"@clerk/nextjs/server\";\nimport { NextRequest, NextResponse } from \"next/server\";\n\nexport const POST = async (\n  req: NextRequest,\n  { params }: { params: { courseId: string; sectionId: string } }\n) => {\n  try {\n    const { userId } = auth();\n\n    if (!userId) {\n      return new NextResponse(\"Unauthorized\", { status: 401 });\n    }\n\n    const { courseId, sectionId } = params;\n\n    const course = await db.course.findUnique({\n      where: {\n        id: courseId,\n        instructorId: userId,\n      },\n    });\n\n    if (!course) {\n      return new NextResponse(\"Course Not Found\", { status: 404 });\n    }\n\n    const unpublishedSection = await db.section.update({\n      where: {\n        id: sectionId,\n        courseId,\n      },\n      data: {\n        isPublished: false,\n      },\n    });\n\n    const publishedSectionsInCourse = await db.section.findMany({\n      where: {\n        courseId,\n        isPublished: true,\n      },\n    });\n\n    if (publishedSectionsInCourse.length === 0) {\n      await db.course.update({\n        where: {\n          id: courseId,\n          instructorId: userId,\n        },\n        data: {\n          isPublished: false,\n        },\n      });\n    }\n\n    return NextResponse.json(unpublishedSection, { status: 200 });\n  } catch (err) {\n    console.log(\"[sectionId_unpublish_POST]\", err);\n    return new NextResponse(\"Internal Server Error\", { status: 500 });\n  }\n}"
  },
  {
    "path": "app/api/courses/[courseId]/sections/reorder/route.ts",
    "content": "import { db } from \"@/lib/db\";\nimport { auth } from \"@clerk/nextjs/server\";\nimport { NextRequest, NextResponse } from \"next/server\";\n\nexport const PUT = async (\n  req: NextRequest,\n  { params }: { params: { courseId: string } }\n) => {\n  try {\n    const { userId } = auth();\n\n    if (!userId) {\n      return new NextResponse(\"Unauthorized\", { status: 401 });\n    }\n\n    const { list } = await req.json();\n\n    const course = await db.course.findUnique({\n      where: {\n        id: params.courseId,\n        instructorId: userId,\n      },\n    });\n\n    if (!course) {\n      return new NextResponse(\"Course not found\", { status: 404 });\n    }\n\n    for (let item of list) {\n      await db.section.update({\n        where: {\n          id: item.id,\n        },\n        data: {\n          position: item.position,\n        },\n      });\n    }\n\n    return new NextResponse(\"Reorder sections successfully\", { status: 200 });\n  } catch (err) {\n    console.log(\"[reorder_PUT]\", err);\n    return new NextResponse(\"Internal Server Error\", { status: 500 });\n  }\n};\n"
  },
  {
    "path": "app/api/courses/[courseId]/sections/route.ts",
    "content": "import { db } from \"@/lib/db\";\nimport { auth } from \"@clerk/nextjs/server\";\nimport { NextRequest, NextResponse } from \"next/server\";\n\nexport const POST = async (\n  req: NextRequest,\n  { params }: { params: { courseId: string } }\n) => {\n  try {\n    const { userId } = auth();\n\n    if (!userId) {\n      return new NextResponse(\"Unauthorized\", { status: 401 });\n    }\n\n    const course = await db.course.findUnique({\n      where: { id: params.courseId, instructorId: userId },\n    });\n\n    if (!course) {\n      return new NextResponse(\"Course Not Found\", { status: 404 });\n    }\n\n    const lastSection = await db.section.findFirst({\n      where: { courseId: params.courseId },\n      orderBy: { position: \"desc\" },\n    });\n\n    const newPosition = lastSection ? lastSection.position + 1 : 0;\n\n    const { title } = await req.json();\n\n    const newSection = await db.section.create({\n      data: {\n        title,\n        courseId: params.courseId,\n        position: newPosition,\n      },\n    });\n\n    return NextResponse.json(newSection, { status: 200 });\n  } catch (err) {\n    console.log(\"[sections_POST]\", err);\n    return new NextResponse(\"Internal Server Error\", { status: 500 });\n  }\n};\n"
  },
  {
    "path": "app/api/courses/[courseId]/unpublish/route.ts",
    "content": "import { db } from \"@/lib/db\";\nimport { auth } from \"@clerk/nextjs/server\";\nimport { NextRequest, NextResponse } from \"next/server\";\n\nexport const POST = async (\n  req: NextRequest,\n  { params }: { params: { courseId: string } }\n) => {\n  try {\n    const { userId } = auth();\n    const { courseId } = params;\n\n    if (!userId) {\n      return new Response(\"Unauthorized\", { status: 401 });\n    }\n\n    const course = await db.course.findUnique({\n      where: { id: courseId, instructorId: userId },\n    });\n\n    if (!course) {\n      return new Response(\"Course not found\", { status: 404 });\n    }\n\n    const unpusblishedCourse = await db.course.update({\n      where: { id: courseId, instructorId: userId },\n      data: { isPublished: false },\n    });\n\n    return NextResponse.json(unpusblishedCourse, { status: 200 });\n  } catch (err) {\n    console.log(\"[courseId_unpublish_POST]\", err);\n    return new Response(\"Internal Server Error\", { status: 500 });\n  }\n};"
  },
  {
    "path": "app/api/courses/route.ts",
    "content": "import { db } from \"@/lib/db\";\nimport { auth } from \"@clerk/nextjs/server\";\nimport { NextRequest, NextResponse } from \"next/server\";\n\nexport const POST = async (req: NextRequest) => {\n  try {\n    const { userId } = auth()\n\n    if (!userId) {\n      return new NextResponse(\"Unauthorized\", { status: 401 })\n    }\n\n    const { title, categoryId, subCategoryId } = await req.json()\n\n    const newCourse = await db.course.create({\n      data: {\n        title,\n        categoryId,\n        subCategoryId,\n        instructorId: userId\n      }\n    })\n\n    return NextResponse.json(newCourse, {status: 200 })\n  } catch (err) {\n    console.log(\"[courses_POST]\", err)\n    return new NextResponse(\"Internal Server Error\", { status: 500 })\n  }\n}"
  },
  {
    "path": "app/api/uploadthing/core.ts",
    "content": "import { auth } from \"@clerk/nextjs/server\";\nimport { createUploadthing, type FileRouter } from \"uploadthing/next\";\nimport { UploadThingError } from \"uploadthing/server\";\n\nconst f = createUploadthing();\n\nconst handleAuth = () => {\n  const { userId } = auth();\n  if (!userId) throw new Error(\"Unauthorized\");\n  return { userId };\n};\n\n// FileRouter for your app, can contain multiple FileRoutes\nexport const ourFileRouter = {\n  courseBanner: f({ image: { maxFileSize: \"4MB\", maxFileCount: 1 } })\n  .middleware(handleAuth)\n  .onUploadComplete(() => {}),\n  sectionVideo: f({ video: { maxFileSize: \"512GB\", maxFileCount: 1 } })\n  .middleware(handleAuth)\n  .onUploadComplete(() => {}),\n  sectionResource: f([\"text\", \"image\", \"video\", \"audio\", \"pdf\"])\n  .middleware(handleAuth)\n  .onUploadComplete(() => {}),\n} satisfies FileRouter;\n\nexport type OurFileRouter = typeof ourFileRouter;\n"
  },
  {
    "path": "app/api/uploadthing/route.ts",
    "content": "import { createRouteHandler } from \"uploadthing/next\";\n \nimport { ourFileRouter } from \"./core\";\n \n// Export routes for Next App Router\nexport const { GET, POST } = createRouteHandler({\n  router: ourFileRouter,\n});"
  },
  {
    "path": "app/api/webhook/route.ts",
    "content": "import { db } from \"@/lib/db\";\nimport { stripe } from \"@/lib/stripe\";\nimport { headers } from \"next/headers\";\nimport { NextRequest, NextResponse } from \"next/server\";\nimport Stripe from \"stripe\";\n\nexport const POST = async (req: NextRequest) => {\n  const rawBody = await req.text();\n  const signature = headers().get(\"Stripe-Signature\") as string;\n\n  let event: Stripe.Event;\n\n  try {\n    event = stripe.webhooks.constructEvent(\n      rawBody,\n      signature,\n      process.env.STRIPE_WEBHOOK_SECRET!\n    );\n  } catch (err: any) {\n    return new NextResponse(`Webhook error: ${err.message}`, { status: 400 });\n  }\n\n  const session = event.data.object as Stripe.Checkout.Session;\n  const customerId = session?.metadata?.customerId;\n  const courseId = session?.metadata?.courseId;\n\n  if (event.type === \"checkout.session.completed\") {\n    if (!customerId || !courseId) {\n      return new NextResponse(\"Missing metadata\", { status: 400 });\n    }\n\n    await db.purchase.create({\n      data: {\n        customerId,\n        courseId,\n      },\n    });\n  } else {\n    return new NextResponse(`Unhandled event type: ${event.type}`, {\n      status: 400,\n    });\n  }\n\n  return new NextResponse(\"Success\", { status: 200 });\n};\n"
  },
  {
    "path": "app/globals.css",
    "content": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\nhtml,\nbody,\n:root {\n  height: 100%;\n}\n\n@layer base {\n  :root {\n    --background: 0 0% 100%;\n    --foreground: 222.2 84% 4.9%;\n\n    --card: 0 0% 100%;\n    --card-foreground: 222.2 84% 4.9%;\n\n    --popover: 0 0% 100%;\n    --popover-foreground: 222.2 84% 4.9%;\n\n    --primary: 222.2 47.4% 11.2%;\n    --primary-foreground: 210 40% 98%;\n\n    --secondary: 210 40% 96.1%;\n    --secondary-foreground: 222.2 47.4% 11.2%;\n\n    --muted: 210 40% 96.1%;\n    --muted-foreground: 215.4 16.3% 46.9%;\n\n    --accent: 210 40% 96.1%;\n    --accent-foreground: 222.2 47.4% 11.2%;\n\n    --destructive: 0 84.2% 60.2%;\n    --destructive-foreground: 210 40% 98%;\n\n    --border: 214.3 31.8% 91.4%;\n    --input: 214.3 31.8% 91.4%;\n    --ring: 222.2 84% 4.9%;\n\n    --radius: 0.5rem;\n  }\n\n  .dark {\n    --background: 222.2 84% 4.9%;\n    --foreground: 210 40% 98%;\n\n    --card: 222.2 84% 4.9%;\n    --card-foreground: 210 40% 98%;\n\n    --popover: 222.2 84% 4.9%;\n    --popover-foreground: 210 40% 98%;\n\n    --primary: 210 40% 98%;\n    --primary-foreground: 222.2 47.4% 11.2%;\n\n    --secondary: 217.2 32.6% 17.5%;\n    --secondary-foreground: 210 40% 98%;\n\n    --muted: 217.2 32.6% 17.5%;\n    --muted-foreground: 215 20.2% 65.1%;\n\n    --accent: 217.2 32.6% 17.5%;\n    --accent-foreground: 210 40% 98%;\n\n    --destructive: 0 62.8% 30.6%;\n    --destructive-foreground: 210 40% 98%;\n\n    --border: 217.2 32.6% 17.5%;\n    --input: 217.2 32.6% 17.5%;\n    --ring: 212.7 26.8% 83.9%;\n  }\n}\n\n@layer base {\n  * {\n    @apply border-border;\n  }\n  body {\n    @apply bg-background text-foreground;\n  }\n}\n\n.cl-formButtonPrimary {\n  color: black;\n  background-color: #FDAB04;\n}\n\n.cl-formButtonPrimary:hover {\n  background-color: rgba(253, 171, 4, 0.8);\n}\n\n@import \"@uploadthing/react/styles.css\";"
  },
  {
    "path": "app/layout.tsx",
    "content": "import type { Metadata } from \"next\";\nimport { Inter } from \"next/font/google\";\nimport { ClerkProvider } from \"@clerk/nextjs\";\n\nimport \"./globals.css\";\nimport ToasterProvider from \"@/components/providers/ToasterProvider\";\n\nconst inter = Inter({ subsets: [\"latin\"] });\n\nexport const metadata: Metadata = {\n  title: \"Tech Vision Academy\",\n  description: \"Empowering minds, shaping future\",\n};\n\nexport default function RootLayout({\n  children,\n}: Readonly<{\n  children: React.ReactNode;\n}>) {\n  return (\n    <ClerkProvider>\n      <html lang=\"en\">\n        <body className={inter.className}>\n          <ToasterProvider />\n          {children}\n          </body>\n      </html>\n    </ClerkProvider>\n  );\n}\n"
  },
  {
    "path": "components/courses/Columns.tsx",
    "content": "\"use client\";\n\nimport { Course } from \"@prisma/client\";\nimport { ColumnDef } from \"@tanstack/react-table\";\nimport { Pencil } from \"lucide-react\";\nimport Link from \"next/link\";\nimport { ArrowUpDown } from \"lucide-react\";\n\nimport { Badge } from \"../ui/badge\";\nimport { Button } from \"../ui/button\";\n\nexport const columns: ColumnDef<Course>[] = [\n  {\n    accessorKey: \"title\", // course.title\n    header: ({ column }) => {\n      return (\n        <Button\n          variant=\"ghost\"\n          onClick={() => column.toggleSorting(column.getIsSorted() === \"asc\")}\n        >\n          Title\n          <ArrowUpDown className=\"ml-2 h-4 w-4\" />\n        </Button>\n      );\n    },\n  },\n  {\n    accessorKey: \"price\",\n    header: ({ column }) => {\n      return (\n        <Button\n          variant=\"ghost\"\n          onClick={() => column.toggleSorting(column.getIsSorted() === \"asc\")}\n        >\n          Price\n          <ArrowUpDown className=\"ml-2 h-4 w-4\" />\n        </Button>\n      );\n    },\n    cell: ({ row }) => {\n      const price = parseFloat(row.getValue(\"price\"));\n      const formatted = new Intl.NumberFormat(\"en-US\", {\n        style: \"currency\",\n        currency: \"USD\",\n      }).format(price);\n\n      return <div>{formatted}</div>;\n    },\n  },\n  {\n    accessorKey: \"isPublished\",\n    header: \"Status\",\n    cell: ({ row }) => {\n      const isPublished = row.getValue(\"isPublished\") || false;\n\n      return (\n        <Badge\n          className={`${\n            isPublished && \"bg-[#FDAB04] text-black hover:bg-[#FDAB04]\"\n          }`}\n        >\n          {isPublished ? \"Published\" : \"Draft\"}\n        </Badge>\n      );\n    },\n  },\n  {\n    id: \"actions\",\n    cell: ({ row }) => (\n      <Link\n        href={`/instructor/courses/${row.original.id}/basic`}\n        className=\"flex gap-2 items-center hover:text-[#FDAB04]\"\n      >\n        <Pencil className=\"h-4 w-4\" /> Edit\n      </Link>\n    ),\n  },\n];\n"
  },
  {
    "path": "components/courses/CourseCard.tsx",
    "content": "import { db } from \"@/lib/db\";\nimport { clerkClient } from \"@clerk/nextjs/server\";\nimport { Course } from \"@prisma/client\";\nimport { Gem } from \"lucide-react\";\nimport Image from \"next/image\";\nimport Link from \"next/link\";\n\nconst CourseCard = async ({ course }: { course: Course }) => {\n  const instructor = await clerkClient.users.getUser(course.instructorId);\n\n  let level;\n\n  if (course.levelId) {\n    level = await db.level.findUnique({\n      where: {\n        id: course.levelId,\n      },\n    });\n  }\n\n  return (\n    <Link\n      href={`/courses/${course.id}/overview`}\n      className=\"border rounded-lg cursor-pointer\"\n    >\n      <Image\n        src={course.imageUrl ? course.imageUrl : \"/image_placeholder.webp\"}\n        alt={course.title}\n        width={500}\n        height={300}\n        className=\"rounded-t-xl w-[320px] h-[180px] object-cover\"\n      />\n      <div className=\"px-4 py-3 flex flex-col gap-2\">\n        <h2 className=\"text-lg font-bold hover:[#FDAB04]\">{course.title}</h2>\n        <div className=\"flex justify-between text-sm font-medium\">\n          {instructor && (\n            <div className=\"flex gap-2 items-center\">\n              <Image\n                src={\n                  instructor.imageUrl\n                    ? instructor.imageUrl\n                    : \"/avatar_placeholder.jpg\"\n                }\n                alt={\n                  instructor.fullName ? instructor.fullName : \"Instructor photo\"\n                }\n                width={30}\n                height={30}\n                className=\"rounded-full\"\n              />\n              <p>{instructor.fullName}</p>\n            </div>\n          )}\n          {level && (\n            <div className=\"flex gap-2\">\n              <Gem size={20} />\n              <p>{level.name}</p>\n            </div>\n          )}\n        </div>\n\n        <p className=\"text-sm font-bold\">$ {course.price}</p>\n      </div>\n    </Link>\n  );\n};\n\nexport default CourseCard;\n"
  },
  {
    "path": "components/courses/CreateCourseForm.tsx",
    "content": "\"use client\";\n\nimport { z } from \"zod\";\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport { useForm } from \"react-hook-form\";\nimport axios from \"axios\";\n\nimport { Button } from \"@/components/ui/button\";\nimport {\n  Form,\n  FormControl,\n  FormDescription,\n  FormField,\n  FormItem,\n  FormLabel,\n  FormMessage,\n} from \"@/components/ui/form\";\nimport { Input } from \"@/components/ui/input\";\nimport { ComboBox } from \"@/components/custom/ComboBox\";\nimport { useRouter } from \"next/navigation\";\nimport toast from \"react-hot-toast\";\nimport { Loader2 } from \"lucide-react\";\n\nconst formSchema = z.object({\n  title: z.string().min(2, {\n    message: \"Title is required and minimum 2 characters\",\n  }),\n  categoryId: z.string().min(1, {\n    message: \"Category is required\",\n  }),\n  subCategoryId: z.string().min(1, {\n    message: \"Subcategory is required\",\n  }),\n});\n\ninterface CreateCourseFormProps {\n  categories: {\n    label: string; // name of category\n    value: string; // categoryId\n    subCategories: { label: string; value: string }[];\n  }[];\n}\n\nconst CreateCourseForm = ({ categories }: CreateCourseFormProps) => {\n  const router = useRouter();\n\n  // 1. Define your form.\n  const form = useForm<z.infer<typeof formSchema>>({\n    resolver: zodResolver(formSchema),\n    defaultValues: {\n      title: \"\",\n      categoryId: \"\",\n      subCategoryId: \"\",\n    },\n  });\n\n  const { isValid, isSubmitting } = form.formState;\n\n  // 2. Define a submit handler.\n  const onSubmit = async (values: z.infer<typeof formSchema>) => {\n    try {\n      const response = await axios.post(\"/api/courses\", values);\n      router.push(`/instructor/courses/${response.data.id}/basic`);\n      toast.success(\"New Course Created\");\n    } catch (err) {\n      console.log(\"Failed to create new course\", err);\n      toast.error(\"Something went wrong!\");\n    }\n  };\n\n  return (\n    <div className=\"p-10\">\n      <h1 className=\"text-xl font-bold\">\n        Let give some basics for your course\n      </h1>\n      <p className=\"text-sm mt-3\">\n        It is ok if you cannot think of a good title or correct category now.\n        You can change them later.\n      </p>\n\n      <Form {...form}>\n        <form\n          onSubmit={form.handleSubmit(onSubmit)}\n          className=\"space-y-8 mt-10\"\n        >\n          <FormField\n            control={form.control}\n            name=\"title\"\n            render={({ field }) => (\n              <FormItem>\n                <FormLabel>Title</FormLabel>\n                <FormControl>\n                  <Input\n                    placeholder=\"Ex: Web Development for Beginners\"\n                    {...field}\n                  />\n                </FormControl>\n                <FormMessage />\n              </FormItem>\n            )}\n          />\n\n          <FormField\n            control={form.control}\n            name=\"categoryId\"\n            render={({ field }) => (\n              <FormItem className=\"flex flex-col\">\n                <FormLabel>Category</FormLabel>\n                <FormControl>\n                  <ComboBox options={categories} {...field} />\n                </FormControl>\n                <FormMessage />\n              </FormItem>\n            )}\n          />\n\n          <FormField\n            control={form.control}\n            name=\"subCategoryId\"\n            render={({ field }) => (\n              <FormItem className=\"flex flex-col\">\n                <FormLabel>Subcategory</FormLabel>\n                <FormControl>\n                  <ComboBox\n                    options={\n                      categories.find(\n                        (category) =>\n                          category.value === form.watch(\"categoryId\")\n                      )?.subCategories || []\n                    }\n                    {...field}\n                  />\n                </FormControl>\n                <FormMessage />\n              </FormItem>\n            )}\n          />\n\n          <Button type=\"submit\" disabled={!isValid || isSubmitting}>\n            {isSubmitting ? (\n              <Loader2 className=\"h-4 w-4 animate-spin\" />\n            ) : (\n              \"Create\"\n            )}\n          </Button>\n        </form>\n      </Form>\n    </div>\n  );\n};\n\nexport default CreateCourseForm;\n"
  },
  {
    "path": "components/courses/EditCourseForm.tsx",
    "content": "\"use client\";\n\nimport { z } from \"zod\";\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport { useForm } from \"react-hook-form\";\nimport { Course } from \"@prisma/client\";\n\nimport { Button } from \"@/components/ui/button\";\nimport {\n  Form,\n  FormControl,\n  FormField,\n  FormItem,\n  FormLabel,\n  FormMessage,\n} from \"@/components/ui/form\";\nimport { Input } from \"@/components/ui/input\";\nimport RichEditor from \"@/components/custom/RichEditor\";\nimport { ComboBox } from \"../custom/ComboBox\";\nimport FileUpload from \"../custom/FileUpload\";\nimport Link from \"next/link\";\nimport axios from \"axios\";\nimport { usePathname, useRouter } from \"next/navigation\";\nimport toast from \"react-hot-toast\";\nimport { Loader2, Trash } from \"lucide-react\";\nimport Delete from \"../custom/Delete\";\nimport PublishButton from \"../custom/PublishButton\";\n\nconst formSchema = z.object({\n  title: z.string().min(2, {\n    message: \"Title is required and must be at least 2 characters long\",\n  }),\n  subtitle: z.string().optional(),\n  description: z.string().optional(),\n  categoryId: z.string().min(1, {\n    message: \"Category is required\",\n  }),\n  subCategoryId: z.string().min(1, {\n    message: \"Subcategory is required\",\n  }),\n  levelId: z.string().optional(),\n  imageUrl: z.string().optional(),\n  price: z.coerce.number().optional(),\n});\n\ninterface EditCourseFormProps {\n  course: Course;\n  categories: {\n    label: string; // name of category\n    value: string; // categoryId\n    subCategories: { label: string; value: string }[];\n  }[];\n  levels: { label: string; value: string }[];\n  isCompleted: boolean;\n}\n\nconst EditCourseForm = ({\n  course,\n  categories,\n  levels,\n  isCompleted,\n}: EditCourseFormProps) => {\n  const router = useRouter();\n  const pathname = usePathname();\n\n  // 1. Define your form.\n  const form = useForm<z.infer<typeof formSchema>>({\n    resolver: zodResolver(formSchema),\n    defaultValues: {\n      title: course.title,\n      subtitle: course.subtitle || \"\",\n      description: course.description || \"\",\n      categoryId: course.categoryId,\n      subCategoryId: course.subCategoryId,\n      levelId: course.levelId || \"\",\n      imageUrl: course.imageUrl || \"\",\n      price: course.price || undefined,\n    },\n  });\n\n  const { isValid, isSubmitting } = form.formState;\n\n  // 2. Define a submit handler.\n  const onSubmit = async (values: z.infer<typeof formSchema>) => {\n    try {\n      await axios.patch(`/api/courses/${course.id}`, values);\n      toast.success(\"Course Updated\");\n      router.refresh();\n    } catch (err) {\n      console.log(\"Failed to update the course\", err);\n      toast.error(\"Something went wrong!\");\n    }\n  };\n\n  const routes = [\n    {\n      label: \"Basic Information\",\n      path: `/instructor/courses/${course.id}/basic`,\n    },\n    { label: \"Curriculum\", path: `/instructor/courses/${course.id}/sections` },\n  ];\n\n  return (\n    <>\n      <div className=\"flex flex-col gap-2 sm:flex-row sm:justify-between mb-7\">\n        <div className=\"flex gap-5\">\n          {routes.map((route) => (\n            <Link key={route.path} href={route.path}>\n              <Button variant={pathname === route.path ? \"default\" : \"outline\"}>\n                {route.label}\n              </Button>\n            </Link>\n          ))}\n        </div>\n\n        <div className=\"flex gap-5 items-start\">\n          <PublishButton\n            disabled={!isCompleted}\n            courseId={course.id}\n            isPublished={course.isPublished}\n            page=\"Course\"\n          />\n          <Delete item=\"course\" courseId={course.id} />\n        </div>\n      </div>\n\n      <Form {...form}>\n        <form onSubmit={form.handleSubmit(onSubmit)} className=\"space-y-8\">\n          <FormField\n            control={form.control}\n            name=\"title\"\n            render={({ field }) => (\n              <FormItem>\n                <FormLabel>\n                  Title <span className=\"text-red-500\">*</span>\n                </FormLabel>\n                <FormControl>\n                  <Input\n                    placeholder=\"Ex: Web Development for Beginners\"\n                    {...field}\n                  />\n                </FormControl>\n                <FormMessage />\n              </FormItem>\n            )}\n          />\n\n          <FormField\n            control={form.control}\n            name=\"subtitle\"\n            render={({ field }) => (\n              <FormItem>\n                <FormLabel>Subtitle</FormLabel>\n                <FormControl>\n                  <Input\n                    placeholder=\"Ex: Become a Full-stack Developer with just ONE course. HTML, CSS, Javascript, Node, React, MongoDB and more!\"\n                    {...field}\n                  />\n                </FormControl>\n                <FormMessage />\n              </FormItem>\n            )}\n          />\n\n          <FormField\n            control={form.control}\n            name=\"description\"\n            render={({ field }) => (\n              <FormItem>\n                <FormLabel>\n                  Description <span className=\"text-red-500\">*</span>\n                </FormLabel>\n                <FormControl>\n                  <RichEditor\n                    placeholder=\"What is this course about?\"\n                    {...field}\n                  />\n                </FormControl>\n                <FormMessage />\n              </FormItem>\n            )}\n          />\n\n          <div className=\"flex flex-wrap gap-10\">\n            <FormField\n              control={form.control}\n              name=\"categoryId\"\n              render={({ field }) => (\n                <FormItem className=\"flex flex-col\">\n                  <FormLabel>\n                    Category <span className=\"text-red-500\">*</span>\n                  </FormLabel>\n                  <FormControl>\n                    <ComboBox options={categories} {...field} />\n                  </FormControl>\n                  <FormMessage />\n                </FormItem>\n              )}\n            />\n\n            <FormField\n              control={form.control}\n              name=\"subCategoryId\"\n              render={({ field }) => (\n                <FormItem className=\"flex flex-col\">\n                  <FormLabel>\n                    Subcategory <span className=\"text-red-500\">*</span>\n                  </FormLabel>\n                  <FormControl>\n                    <ComboBox\n                      options={\n                        categories.find(\n                          (category) =>\n                            category.value === form.watch(\"categoryId\")\n                        )?.subCategories || []\n                      }\n                      {...field}\n                    />\n                  </FormControl>\n                  <FormMessage />\n                </FormItem>\n              )}\n            />\n\n            <FormField\n              control={form.control}\n              name=\"levelId\"\n              render={({ field }) => (\n                <FormItem className=\"flex flex-col\">\n                  <FormLabel>\n                    Level <span className=\"text-red-500\">*</span>\n                  </FormLabel>\n                  <FormControl>\n                    <ComboBox options={levels} {...field} />\n                  </FormControl>\n                  <FormMessage />\n                </FormItem>\n              )}\n            />\n          </div>\n\n          <FormField\n            control={form.control}\n            name=\"imageUrl\"\n            render={({ field }) => (\n              <FormItem className=\"flex flex-col\">\n                <FormLabel>\n                  Couse Banner <span className=\"text-red-500\">*</span>\n                </FormLabel>\n                <FormControl>\n                  <FileUpload\n                    value={field.value || \"\"}\n                    onChange={(url) => field.onChange(url)}\n                    endpoint=\"courseBanner\"\n                    page=\"Edit Course\"\n                  />\n                </FormControl>\n                <FormMessage />\n              </FormItem>\n            )}\n          />\n\n          <FormField\n            control={form.control}\n            name=\"price\"\n            render={({ field }) => (\n              <FormItem>\n                <FormLabel>\n                  Price <span className=\"text-red-500\">*</span> (USD)\n                </FormLabel>\n                <FormControl>\n                  <Input\n                    type=\"number\"\n                    step=\"0.01\"\n                    placeholder=\"29.99\"\n                    {...field}\n                  />\n                </FormControl>\n                <FormMessage />\n              </FormItem>\n            )}\n          />\n\n          <div className=\"flex gap-5\">\n            <Link href=\"/instructor/courses\">\n              <Button variant=\"outline\" type=\"button\">\n                Cancel\n              </Button>\n            </Link>\n            <Button type=\"submit\" disabled={!isValid || isSubmitting}>\n              {isSubmitting ? (\n                <Loader2 className=\"h-4 w-4 animate-spin\" />\n              ) : (\n                \"Save\"\n              )}\n            </Button>\n          </div>\n        </form>\n      </Form>\n    </>\n  );\n};\n\nexport default EditCourseForm;\n"
  },
  {
    "path": "components/custom/AlertBanner.tsx",
    "content": "import { Alert, AlertDescription, AlertTitle } from \"@/components/ui/alert\";\nimport { Rocket, TriangleAlert } from \"lucide-react\";\n\ninterface AlertBannerProps {\n  isCompleted: boolean;\n  requiredFieldsCount: number;\n  missingFieldsCount: number;\n}\n\nconst AlertBanner = ({\n  isCompleted,\n  requiredFieldsCount,\n  missingFieldsCount,\n}: AlertBannerProps) => {\n  return (\n    <Alert\n      className=\"my-4\"\n      variant={`${isCompleted ? \"complete\" : \"destructive\"}`}\n    >\n      {isCompleted ? (\n        <Rocket className=\"h-4 w-4\" />\n      ) : (\n        <TriangleAlert className=\"h-4 w-4\" />\n      )}\n      <AlertTitle className=\"text-xs font-medium\">\n        {missingFieldsCount} missing field(s) / {requiredFieldsCount} required\n        fields\n      </AlertTitle>\n      <AlertDescription className=\"text-xs\">\n        {isCompleted\n          ? \"Great job! Ready to publish\"\n          : \"You can only publish when all the required fields are completed\"}\n      </AlertDescription>\n    </Alert>\n  );\n};\n\nexport default AlertBanner;\n"
  },
  {
    "path": "components/custom/Categories.tsx",
    "content": "\"use client\"\n\nimport { Category } from \"@prisma/client\";\nimport { Button } from \"@/components/ui/button\";\nimport { useRouter } from \"next/navigation\";\n\ninterface CategoriesProps {\n  categories: Category[];\n  selectedCategory: string | null;\n}\n\nconst Categories = ({ categories, selectedCategory }: CategoriesProps) => {\n  const router = useRouter();\n\n  const onClick = (categoryId: string | null) => {\n    router.push(categoryId ? `/categories/${categoryId}` : \"/\");\n  };\n\n  return (\n    <div className=\"flex flex-wrap px-4 gap-7 justify-center my-10\">\n      <Button\n        variant={selectedCategory === null ? \"default\" : \"outline\"}\n        onClick={() => onClick(null)}\n      >\n        All Categories\n      </Button>\n      {categories.map((category) => (\n        <Button\n          key={category.id}\n          variant={selectedCategory === category.id ? \"default\" : \"outline\"}\n          onClick={() => onClick(category.id)}\n        >\n          {category.name}\n        </Button>\n      ))}\n    </div>\n  );\n};\n\nexport default Categories;\n"
  },
  {
    "path": "components/custom/ComboBox.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Check, ChevronsUpDown } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Button } from \"@/components/ui/button\"\nimport {\n  Command,\n  CommandEmpty,\n  CommandGroup,\n  CommandInput,\n  CommandItem,\n} from \"@/components/ui/command\"\nimport {\n  Popover,\n  PopoverContent,\n  PopoverTrigger,\n} from \"@/components/ui/popover\"\n\ninterface ComboBoxProps {\n  options: { label: string, value: string }[]\n  value?: string\n  onChange: (value: string) => void\n}\n\nexport function ComboBox({ options, value, onChange }: ComboBoxProps) {\n  const [open, setOpen] = React.useState(false)\n\n  return (\n    <Popover open={open} onOpenChange={setOpen}>\n      <PopoverTrigger asChild>\n        <Button\n          variant=\"outline\"\n          role=\"combobox\"\n          aria-expanded={open}\n          className=\"w-[200px] justify-between\"\n        >\n          {value\n            ? options.find((option) => option.value === value)?.label\n            : \"Select option...\"}\n          <ChevronsUpDown className=\"ml-2 h-4 w-4 shrink-0 opacity-50\" />\n        </Button>\n      </PopoverTrigger>\n      <PopoverContent className=\"w-[200px] p-0\">\n        <Command>\n          <CommandInput placeholder=\"Search option...\" />\n          <CommandEmpty>No option found.</CommandEmpty>\n          <CommandGroup>\n            {options.map((option) => (\n              <CommandItem\n                key={option.value}\n                value={option.value}\n                onSelect={() => {\n                  onChange(option.value === value ? \"\" : option.value)\n                  setOpen(false)\n                }}\n              >\n                <Check\n                  className={cn(\n                    \"mr-2 h-4 w-4\",\n                    value === option.value ? \"opacity-100\" : \"opacity-0\"\n                  )}\n                />\n                {option.label}\n              </CommandItem>\n            ))}\n          </CommandGroup>\n        </Command>\n      </PopoverContent>\n    </Popover>\n  )\n}\n"
  },
  {
    "path": "components/custom/DataTable.tsx",
    "content": "\"use client\";\n\nimport {\n  ColumnDef,\n  flexRender,\n  getCoreRowModel,\n  getPaginationRowModel,\n  SortingState,\n  getSortedRowModel,\n  ColumnFiltersState,\n  getFilteredRowModel,\n  useReactTable,\n} from \"@tanstack/react-table\";\n\nimport {\n  Table,\n  TableBody,\n  TableCell,\n  TableHead,\n  TableHeader,\n  TableRow,\n} from \"@/components/ui/table\";\n\nimport { Button } from \"@/components/ui/button\";\nimport { Input } from \"@/components/ui/input\"\n\nimport { useState } from \"react\";\n\ninterface DataTableProps<TData, TValue> {\n  columns: ColumnDef<TData, TValue>[];\n  data: TData[];\n}\n\nexport function DataTable<TData, TValue>({\n  columns,\n  data,\n}: DataTableProps<TData, TValue>) {\n  const [sorting, setSorting] = useState<SortingState>([])\n  const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>(\n    []\n  )\n\n  const table = useReactTable({\n    data,\n    columns,\n    getCoreRowModel: getCoreRowModel(),\n    getPaginationRowModel: getPaginationRowModel(),\n    onSortingChange: setSorting,\n    getSortedRowModel: getSortedRowModel(),\n    onColumnFiltersChange: setColumnFilters,\n    getFilteredRowModel: getFilteredRowModel(),\n    state: {\n      sorting,\n      columnFilters\n    }\n  });\n\n  return (\n    <div>\n      <div className=\"flex items-center py-4\">\n        <Input\n          placeholder=\"Filter courses by title...\"\n          value={(table.getColumn(\"title\")?.getFilterValue() as string) ?? \"\"}\n          onChange={(event) =>\n            table.getColumn(\"title\")?.setFilterValue(event.target.value)\n          }\n          className=\"max-w-sm\"\n        />\n      </div>\n\n      <div className=\"rounded-md border\">\n        <Table>\n          <TableHeader>\n            {table.getHeaderGroups().map((headerGroup) => (\n              <TableRow key={headerGroup.id}>\n                {headerGroup.headers.map((header) => {\n                  return (\n                    <TableHead key={header.id}>\n                      {header.isPlaceholder\n                        ? null\n                        : flexRender(\n                            header.column.columnDef.header,\n                            header.getContext()\n                          )}\n                    </TableHead>\n                  );\n                })}\n              </TableRow>\n            ))}\n          </TableHeader>\n          <TableBody className=\"text-sm font-medium\">\n            {table.getRowModel().rows?.length ? (\n              table.getRowModel().rows.map((row) => (\n                <TableRow\n                  key={row.id}\n                  data-state={row.getIsSelected() && \"selected\"}\n                >\n                  {row.getVisibleCells().map((cell) => (\n                    <TableCell key={cell.id}>\n                      {flexRender(\n                        cell.column.columnDef.cell,\n                        cell.getContext()\n                      )}\n                    </TableCell>\n                  ))}\n                </TableRow>\n              ))\n            ) : (\n              <TableRow>\n                <TableCell\n                  colSpan={columns.length}\n                  className=\"h-24 text-center\"\n                >\n                  No results.\n                </TableCell>\n              </TableRow>\n            )}\n          </TableBody>\n        </Table>\n      </div>\n      <div className=\"flex items-center justify-end space-x-2 py-4\">\n        <Button\n          variant=\"outline\"\n          size=\"sm\"\n          onClick={() => table.previousPage()}\n          disabled={!table.getCanPreviousPage()}\n        >\n          Previous\n        </Button>\n        <Button\n          variant=\"outline\"\n          size=\"sm\"\n          onClick={() => table.nextPage()}\n          disabled={!table.getCanNextPage()}\n        >\n          Next\n        </Button>\n      </div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "components/custom/Delete.tsx",
    "content": "import {\n  AlertDialog,\n  AlertDialogAction,\n  AlertDialogCancel,\n  AlertDialogContent,\n  AlertDialogDescription,\n  AlertDialogFooter,\n  AlertDialogHeader,\n  AlertDialogTitle,\n  AlertDialogTrigger,\n} from \"@/components/ui/alert-dialog\";\nimport axios from \"axios\";\nimport { Loader2, Trash } from \"lucide-react\";\nimport { useRouter } from \"next/navigation\";\nimport { useState } from \"react\";\nimport toast from \"react-hot-toast\";\nimport { Button } from \"../ui/button\";\n\ninterface DeleteProps {\n  item: string;\n  courseId: string;\n  sectionId?: string;\n}\n\nconst Delete = ({ item, courseId, sectionId }: DeleteProps) => {\n  const router = useRouter();\n  const [isDeleting, setIsDeleting] = useState(false);\n\n  const onDelete = async () => {\n    try {\n      setIsDeleting(true);\n      const url =\n        item === \"course\"\n          ? `/api/courses/${courseId}`\n          : `/api/courses/${courseId}/sections/${sectionId}`;\n      await axios.delete(url);\n\n      setIsDeleting(false);\n      const pushedUrl =\n        item === \"course\"\n          ? \"/instructor/courses\"\n          : `/instructor/courses/${courseId}/sections`;\n      router.push(pushedUrl);\n      router.refresh();\n      toast.success(`${item} deleted`);\n    } catch (err) {\n      toast.error(`Something went wrong!`);\n      console.log(`Failed to delete the ${item}`, err);\n    }\n  };\n\n  return (\n    <AlertDialog>\n      <AlertDialogTrigger>\n        <Button>\n          {isDeleting ? (\n            <Loader2 className=\"h-4 w-4 animate-spin\" />\n          ) : (\n            <Trash className=\"h-4 w-4\" />\n          )}\n        </Button>\n      </AlertDialogTrigger>\n      <AlertDialogContent>\n        <AlertDialogHeader>\n          <AlertDialogTitle className=\"text-red-500\">\n            Are you absolutely sure?\n          </AlertDialogTitle>\n          <AlertDialogDescription>\n            This action cannot be undone. This will permanently delete your {item}\n          </AlertDialogDescription>\n        </AlertDialogHeader>\n        <AlertDialogFooter>\n          <AlertDialogCancel>Cancel</AlertDialogCancel>\n          <AlertDialogAction className=\"bg-[#FDAB04]\" onClick={onDelete}>Delete</AlertDialogAction>\n        </AlertDialogFooter>\n      </AlertDialogContent>\n    </AlertDialog>\n  );\n};\n\nexport default Delete;\n"
  },
  {
    "path": "components/custom/FileUpload.tsx",
    "content": "\"use client\";\n\nimport { ourFileRouter } from \"@/app/api/uploadthing/core\";\nimport { UploadDropzone } from \"@/lib/uploadthing\";\nimport Image from \"next/image\";\nimport toast from \"react-hot-toast\";\n\ninterface FileUploadProps {\n  value: string;\n  onChange: (url?: string) => void;\n  endpoint: keyof typeof ourFileRouter;\n  page: string;\n}\n\nconst FileUpload = ({ value, onChange, endpoint, page }: FileUploadProps) => {\n  return (\n    <div className=\"flex flex-col gap-2\">\n      {page === \"Edit Course\" && value !== \"\" && (\n        <Image\n          src={value}\n          alt=\"image\"\n          width={500}\n          height={500}\n          className=\"w-[280px] h-[200px] object-cover rounded-xl\"\n        />\n      )}\n\n      {page === \"Edit Section\" && value !== \"\" && (\n        <p className=\"text-sm font-medium\">{value}</p>\n      )}\n\n      <UploadDropzone\n        endpoint={endpoint}\n        onClientUploadComplete={(res) => {\n          onChange(res?.[0].url);\n        }}\n        onUploadError={(error: Error) => {\n          toast.error(error.message);\n        }}\n        className=\"w-[280px] h-[200px]\"\n      />\n    </div>\n  );\n};\n\nexport default FileUpload;\n"
  },
  {
    "path": "components/custom/PublishButton.tsx",
    "content": "\"use client\";\n\nimport { Button } from \"@/components/ui/button\";\nimport axios from \"axios\";\nimport { Loader2 } from \"lucide-react\";\nimport { useRouter } from \"next/navigation\";\nimport { useState } from \"react\";\nimport toast from \"react-hot-toast\";\n\ninterface PublishButtonProps {\n  disabled: boolean;\n  courseId: string;\n  sectionId?: string;\n  isPublished: boolean;\n  page: string;\n}\n\nconst PublishButton = ({\n  disabled,\n  courseId,\n  sectionId,\n  isPublished,\n  page,\n}: PublishButtonProps) => {\n  const router = useRouter();\n  const [isLoading, setIsLoading] = useState(false);\n\n  const onClick = async () => {\n    let url = `/api/courses/${courseId}`;\n    if (page === \"Section\") {\n      url += `/sections/${sectionId}`;\n    }\n\n    try {\n      setIsLoading(true);\n      isPublished\n        ? await axios.post(`${url}/unpublish`)\n        : await axios.post(`${url}/publish`);\n\n      toast.success(`${page} ${isPublished ? \"unpublished\" : \"published\"}`);\n      router.refresh();\n    } catch (err) {\n      toast.error(\"Something went wrong!\");\n      console.log(\n        `Failed to ${isPublished ? \"unpublish\" : \"publish\"} ${page}`,\n        err\n      );\n    } finally {\n      setIsLoading(false);\n    }\n  };\n\n  return (\n    <Button\n      variant=\"outline\"\n      disabled={disabled || isLoading}\n      onClick={onClick}\n    >\n      {isLoading ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : isPublished ? \"Unpublish\" : \"Publish\"}\n    </Button>\n  );\n};\n\nexport default PublishButton;\n"
  },
  {
    "path": "components/custom/ReadText.tsx",
    "content": "\"use client\"\n\nimport dynamic from \"next/dynamic\";\nimport { useMemo } from \"react\";\nimport \"react-quill/dist/quill.bubble.css\";\n\n\nconst ReadText = ({ value }: { value: string }) => {\n  const ReactQuill = useMemo(\n    () => dynamic(() => import(\"react-quill\"), { ssr: false }),\n    []\n  );\n\n  return (\n    <ReactQuill\n      theme=\"bubble\"\n      value={value}\n      readOnly\n    />\n  );\n};\n\nexport default ReadText;\n"
  },
  {
    "path": "components/custom/RichEditor.tsx",
    "content": "\"use client\"\n\nimport \"react-quill/dist/quill.snow.css\";\nimport dynamic from \"next/dynamic\";\nimport { useMemo } from \"react\";\n\ninterface RichEditorProps {\n  placeholder: string;\n  onChange: (value: string) => void;\n  value?: string;\n}\n\nconst RichEditor = ({ placeholder, onChange, value }: RichEditorProps) => {\n  const ReactQuill = useMemo(\n    () => dynamic(() => import(\"react-quill\"), { ssr: false }),\n    []\n  );\n\n  return (\n    <ReactQuill\n      theme=\"snow\"\n      placeholder={placeholder}\n      value={value}\n      onChange={onChange}\n    />\n  );\n};\n\nexport default RichEditor;\n"
  },
  {
    "path": "components/layout/CourseSideBar.tsx",
    "content": "import { db } from \"@/lib/db\";\nimport { Course, Section } from \"@prisma/client\";\nimport Link from \"next/link\";\nimport { Progress } from \"../ui/progress\";\n\ninterface CourseSideBarProps {\n  course: Course & { sections: Section[] };\n  studentId: string;\n}\n\nconst CourseSideBar = async ({ course, studentId }: CourseSideBarProps) => {\n  const publishedSections = await db.section.findMany({\n    where: {\n      courseId: course.id,\n      isPublished: true,\n    },\n    orderBy: {\n      position: \"asc\",\n    },\n  });\n\n  const publishedSectionIds = publishedSections.map((section) => section.id);\n\n  const purchase = await db.purchase.findUnique({\n    where: {\n      customerId_courseId: {\n        customerId: studentId,\n        courseId: course.id,\n      },\n    },\n  });\n\n  const completedSections = await db.progress.count({\n    where:{\n      studentId,\n      sectionId: {\n        in: publishedSectionIds,\n      },\n      isCompleted: true,\n    }\n  });\n\n  const progressPercentage = (completedSections / publishedSectionIds.length) * 100;\n\n  return (\n    <div className=\"hidden md:flex flex-col w-64 border-r shadow-md px-3 my-4 text-sm font-medium\">\n      <h1 className=\"text-lg font-bold text-center mb-4\">{course.title}</h1>\n      {purchase && (\n        <div>\n          <Progress value={progressPercentage} className=\"h-2\" />\n          <p className=\"text-xs\">{Math.round(progressPercentage)}% completed</p>\n        </div>\n      )}\n      <Link\n        href={`/courses/${course.id}/overview`}\n        className={`p-3 rounded-lg hover:bg-[#FFF8EB] mt-4`}\n      >\n        Overview\n      </Link>\n      {publishedSections.map((section) => (\n        <Link\n          key={section.id}\n          href={`/courses/${course.id}/sections/${section.id}`}\n          className=\"p-3 rounded-lg hover:bg-[#FFF8EB] mt-4\"\n        >\n          {section.title}\n        </Link>\n      ))}\n    </div>\n  );\n};\n\nexport default CourseSideBar;\n"
  },
  {
    "path": "components/layout/SectionMenu.tsx",
    "content": "import { Course, Section } from \"@prisma/client\";\nimport React from \"react\";\nimport { Sheet, SheetContent, SheetTrigger } from \"../ui/sheet\";\nimport { Button } from \"../ui/button\";\nimport Link from \"next/link\";\n\ninterface SectionMenuProps {\n  course: Course & { sections: Section[] };\n}\n\nconst SectionMenu = ({ course }: SectionMenuProps) => {\n  return (\n    <div className=\"z-60 md:hidden\">\n      <Sheet>\n        <SheetTrigger>\n          <Button>Sections</Button>\n        </SheetTrigger>\n        <SheetContent className=\"flex flex-col\">\n          <Link\n            href={`/courses/${course.id}/overview`}\n            className={`p-3 rounded-lg hover:bg-[#FFF8EB] mt-4`}\n          >\n            Overview\n          </Link>\n          {course.sections.map((section) => (\n            <Link\n              key={section.id}\n              href={`/courses/${course.id}/sections/${section.id}`}\n              className=\"p-3 rounded-lg hover:bg-[#FFF8EB] mt-4\"\n            >\n              {section.title}\n            </Link>\n          ))}\n        </SheetContent>\n      </Sheet>\n    </div>\n  );\n};\n\nexport default SectionMenu;\n"
  },
  {
    "path": "components/layout/Sidebar.tsx",
    "content": "\"use client\";\n\nimport { BarChart4, MonitorPlay } from \"lucide-react\";\nimport Link from \"next/link\";\nimport { usePathname } from \"next/navigation\";\n\nconst Sidebar = () => {\n  const pathname = usePathname();\n\n  const sidebarRoutes = [\n    { icon: <MonitorPlay />, label: \"Courses\", path: \"/instructor/courses\" },\n    {\n      icon: <BarChart4 />,\n      label: \"Performance\",\n      path: \"/instructor/performance\",\n    },\n  ];\n\n  return (\n    <div className=\"max-sm:hidden flex flex-col w-64 border-r shadow-md px-3 my-4 gap-4 text-sm font-medium\">\n      {sidebarRoutes.map((route) => (\n        <Link\n          href={route.path}\n          key={route.path}\n          className={`flex items-center gap-4 p-3 rounded-lg hover:bg-[#FFF8EB]\n          ${pathname.startsWith(route.path) && \"bg-[#FDAB04] hover:bg-[#FDAB04]/80\"}\n          `}\n        >\n          {route.icon} {route.label}\n        </Link>\n      ))}\n    </div>\n  );\n};\n\nexport default Sidebar;\n"
  },
  {
    "path": "components/layout/Topbar.tsx",
    "content": "\"use client\";\n\nimport { UserButton, useAuth } from \"@clerk/nextjs\";\nimport { Menu, Search } from \"lucide-react\";\nimport Image from \"next/image\";\nimport Link from \"next/link\";\nimport { useState } from \"react\";\nimport { usePathname, useRouter } from \"next/navigation\";\n\nimport { Button } from \"@/components/ui/button\";\nimport {\n  Sheet,\n  SheetContent,\n  SheetDescription,\n  SheetHeader,\n  SheetTitle,\n  SheetTrigger,\n} from \"@/components/ui/sheet\";\n\nconst Topbar = () => {\n  const { isSignedIn } = useAuth();\n  const router = useRouter();\n  const pathName = usePathname();\n\n  const topRoutes = [\n    { label: \"Instructor\", path: \"/instructor/courses\" },\n    { label: \"Learning\", path: \"/learning\" },\n  ];\n\n  const sidebarRoutes = [\n    { label: \"Courses\", path: \"/instructor/courses\" },\n    {\n      label: \"Performance\",\n      path: \"/instructor/performance\",\n    },\n  ];\n\n  const [searchInput, setSearchInput] = useState(\"\");\n\n  const handleSearch = () => {\n    if (searchInput.trim() !== \"\") {\n      router.push(`/search?query=${searchInput}`);\n    }\n    setSearchInput(\"\");\n  };\n\n  return (\n    <div className=\"flex justify-between items-center p-4\">\n      <Link href=\"/\">\n        <Image src=\"/logo.png\" height={100} width={200} alt=\"logo\" />\n      </Link>\n\n      <div className=\"max-md:hidden w-[400px] rounded-full flex\">\n        <input\n          className=\"flex-grow bg-[#FFF8EB] rounded-l-full border-none outline-none text-sm pl-4 py-3\"\n          placeholder=\"Search for courses\"\n          value={searchInput}\n          onChange={(e) => setSearchInput(e.target.value)}\n        />\n        <button\n          className=\"bg-[#FDAB04] rounded-r-full border-none outline-none cursor-pointer px-4 py-3 hover:bg-[#FDAB04]/80\"\n          disabled={searchInput.trim() === \"\"}\n          onClick={handleSearch}\n        >\n          <Search className=\"h-4 w-4\" />\n        </button>\n      </div>\n\n      <div className=\"flex gap-6 items-center\">\n        <div className=\"max-sm:hidden flex gap-6\">\n          {topRoutes.map((route) => (\n            <Link\n              href={route.path}\n              key={route.path}\n              className=\"text-sm font-medium hover:text-[#FDAB04]\"\n            >\n              {route.label}\n            </Link>\n          ))}\n        </div>\n\n        <div className=\"z-20 sm:hidden\">\n          <Sheet>\n            <SheetTrigger>\n              <Menu className=\"w-5 h-5\" />\n            </SheetTrigger>\n            <SheetContent className=\"flex flex-col gap-4\">\n              <div className=\"flex flex-col gap-4\">\n                {topRoutes.map((route) => (\n                  <Link\n                    href={route.path}\n                    key={route.path}\n                    className=\"text-sm font-medium hover:text-[#FDAB04]\"\n                  >\n                    {route.label}\n                  </Link>\n                ))}\n              </div>\n              \n              {pathName.startsWith(\"/instructor\") && (\n                <div className=\"flex flex-col gap-4\">\n                  {sidebarRoutes.map((route) => (\n                    <Link\n                      href={route.path}\n                      key={route.path}\n                      className=\"text-sm font-medium hover:text-[#FDAB04]\"\n                    >\n                      {route.label}\n                    </Link>\n                  ))}\n                </div>\n              )}\n            </SheetContent>\n          </Sheet>\n        </div>\n\n        {isSignedIn ? (\n          <UserButton afterSignOutUrl=\"/sign-in\" />\n        ) : (\n          <Link href=\"/sign-in\">\n            <Button>Sign In</Button>\n          </Link>\n        )}\n      </div>\n    </div>\n  );\n};\n\nexport default Topbar;\n"
  },
  {
    "path": "components/performance/Chart.tsx",
    "content": "\"use client\"\n\nimport {\n  ResponsiveContainer,\n  BarChart,\n  Bar,\n  XAxis,\n  YAxis,\n  Tooltip,\n} from \"recharts\";\nimport { Card } from \"@/components/ui/card\";\n\nconst Chart = ({ data }: { data: { name: string; total: number }[] }) => {\n  return (\n    <Card>\n      <ResponsiveContainer width=\"100%\" height={400}>\n        <BarChart data={data}>\n          <XAxis\n            dataKey=\"name\"\n            stroke=\"888888\"\n            fontSize={12}\n            axisLine={false}\n            tickLine={false}\n          />\n          <YAxis\n            stroke=\"888888\"\n            fontSize={12}\n            axisLine={false}\n            tickLine={false}\n            tickFormatter={(value) => `$${value}`}\n          />\n          <Tooltip />\n          <Bar dataKey=\"total\" fill=\"#8884d8\" radius={[4, 4, 0, 0]} />\n        </BarChart>\n      </ResponsiveContainer>\n    </Card>\n  );\n};\n\nexport default Chart;\n"
  },
  {
    "path": "components/performance/DataCard.tsx",
    "content": "import { Card, CardContent, CardHeader, CardTitle } from \"@/components/ui/card\";\nimport { formatPrice } from \"@/lib/formatPrice\";\n\ninterface DataCardProps {\n  value: number;\n  label: string;\n  shouldFormat?: boolean;\n}\n\nconst DataCard = ({ value, label, shouldFormat }: DataCardProps) => {\n  return (\n    <Card>\n      <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n        <CardTitle className=\"text-sm font-medium\">{label}</CardTitle>\n      </CardHeader>\n      <CardContent>\n        <div className=\"text-lg font-bold\">\n          {shouldFormat ? formatPrice(value) : value}\n        </div>\n      </CardContent>\n    </Card>\n  );\n};\n\nexport default DataCard;\n"
  },
  {
    "path": "components/providers/ToasterProvider.tsx",
    "content": "\"use client\"\n\nimport { Toaster } from \"react-hot-toast\"\n\nconst ToasterProvider = () => {\n  return (\n    <Toaster />\n  )\n}\n\nexport default ToasterProvider"
  },
  {
    "path": "components/sections/CreateSectionForm.tsx",
    "content": "\"use client\";\n\nimport { Course, Section } from \"@prisma/client\";\nimport Link from \"next/link\";\nimport { usePathname, useRouter } from \"next/navigation\";\nimport { z } from \"zod\";\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport { useForm } from \"react-hook-form\";\n\nimport { Button } from \"@/components/ui/button\";\nimport {\n  Form,\n  FormControl,\n  FormField,\n  FormItem,\n  FormLabel,\n  FormMessage,\n} from \"@/components/ui/form\";\nimport { Input } from \"@/components/ui/input\";\nimport toast from \"react-hot-toast\";\nimport axios from \"axios\";\nimport SectionList from \"@/components/sections/SectionList\";\nimport { Loader2 } from \"lucide-react\";\n\nconst formSchema = z.object({\n  title: z.string().min(2, {\n    message: \"Title is required and must be at least 2 characters long\",\n  }),\n});\n\nconst CreateSectionForm = ({\n  course,\n}: {\n  course: Course & { sections: Section[] };\n}) => {\n  const pathname = usePathname();\n  const router = useRouter();\n\n  const routes = [\n    {\n      label: \"Basic Information\",\n      path: `/instructor/courses/${course.id}/basic`,\n    },\n    { label: \"Curriculum\", path: `/instructor/courses/${course.id}/sections` },\n  ];\n\n  // 1. Define your form.\n  const form = useForm<z.infer<typeof formSchema>>({\n    resolver: zodResolver(formSchema),\n    defaultValues: {\n      title: \"\",\n    },\n  });\n\n  const { isValid, isSubmitting } = form.formState;\n\n  // 2. Define a submit handler.\n  const onSubmit = async (values: z.infer<typeof formSchema>) => {\n    try {\n      const response = await axios.post(\n        `/api/courses/${course.id}/sections`,\n        values\n      );\n      router.push(\n        `/instructor/courses/${course.id}/sections/${response.data.id}`\n      );\n      toast.success(\"New Section created!\");\n    } catch (err) {\n      toast.error(\"Something went wrong!\");\n      console.log(\"Failed to create a new section\", err);\n    }\n  };\n\n  const onReorder = async (updateData: { id: string; position: number }[]) => {\n    try {\n      await axios.put(`/api/courses/${course.id}/sections/reorder`, {\n        list: updateData,\n      });\n      toast.success(\"Sections reordered successfully\");\n    } catch (err) {\n      console.log(\"Failed to reorder sections\", err);\n      toast.error(\"Something went wrong!\");\n    }\n  };\n\n  return (\n    <div className=\"px-10 py-6\">\n      <div className=\"flex gap-5\">\n        {routes.map((route) => (\n          <Link key={route.path} href={route.path}>\n            <Button variant={pathname === route.path ? \"default\" : \"outline\"}>\n              {route.label}\n            </Button>\n          </Link>\n        ))}\n      </div>\n\n      <SectionList\n        items={course.sections || []}\n        onReorder={onReorder}\n        onEdit={(id) =>\n          router.push(`/instructor/courses/${course.id}/sections/${id}`)\n        }\n      />\n\n      <h1 className=\"text-xl font-bold mt-5\">Add New Section</h1>\n\n      <Form {...form}>\n        <form onSubmit={form.handleSubmit(onSubmit)} className=\"space-y-8 mt-5\">\n          <FormField\n            control={form.control}\n            name=\"title\"\n            render={({ field }) => (\n              <FormItem>\n                <FormLabel>Title</FormLabel>\n                <FormControl>\n                  <Input placeholder=\"Ex: Introduction\" {...field} />\n                </FormControl>\n                <FormMessage />\n              </FormItem>\n            )}\n          />\n\n          <div className=\"flex gap-5\">\n            <Link href={`/instructor/courses/${course.id}/basic`}>\n              <Button variant=\"outline\" type=\"button\">\n                Cancel\n              </Button>\n            </Link>\n            <Button type=\"submit\" disabled={!isValid || isSubmitting}>\n              {isSubmitting ? (\n                <Loader2 className=\"h-4 w-4 animate-spin\" />\n              ) : (\n                \"Create\"\n              )}\n            </Button>\n          </div>\n        </form>\n      </Form>\n    </div>\n  );\n};\n\nexport default CreateSectionForm;\n"
  },
  {
    "path": "components/sections/EditSectionForm.tsx",
    "content": "\"use client\";\n\nimport { z } from \"zod\";\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport { useForm } from \"react-hook-form\";\nimport { MuxData, Resource, Section } from \"@prisma/client\";\nimport Link from \"next/link\";\nimport axios from \"axios\";\nimport { useRouter } from \"next/navigation\";\nimport toast from \"react-hot-toast\";\nimport { ArrowLeft, Loader2, Trash } from \"lucide-react\";\nimport MuxPlayer from \"@mux/mux-player-react\";\n\nimport { Button } from \"@/components/ui/button\";\nimport {\n  Form,\n  FormControl,\n  FormDescription,\n  FormField,\n  FormItem,\n  FormLabel,\n  FormMessage,\n} from \"@/components/ui/form\";\nimport { Input } from \"@/components/ui/input\";\nimport RichEditor from \"@/components/custom/RichEditor\";\nimport FileUpload from \"../custom/FileUpload\";\nimport { Switch } from \"@/components/ui/switch\";\nimport ResourceForm from \"@/components/sections/ResourceForm\";\nimport Delete from \"@/components/custom/Delete\";\nimport PublishButton from \"@/components/custom/PublishButton\";\n\nconst formSchema = z.object({\n  title: z.string().min(2, {\n    message: \"Title is required and must be at least 2 characters long\",\n  }),\n  description: z.string().optional(),\n  videoUrl: z.string().optional(),\n  isFree: z.boolean().optional(),\n});\n\ninterface EditSectionFormProps {\n  section: Section & { resources: Resource[]; muxData?: MuxData | null };\n  courseId: string;\n  isCompleted: boolean;\n}\n\nconst EditSectionForm = ({\n  section,\n  courseId,\n  isCompleted,\n}: EditSectionFormProps) => {\n  const router = useRouter();\n\n  // 1. Define your form.\n  const form = useForm<z.infer<typeof formSchema>>({\n    resolver: zodResolver(formSchema),\n    defaultValues: {\n      title: section.title,\n      description: section.description || \"\",\n      videoUrl: section.videoUrl || \"\",\n      isFree: section.isFree,\n    },\n  });\n\n  const { isValid, isSubmitting } = form.formState;\n\n  // 2. Define a submit handler.\n  const onSubmit = async (values: z.infer<typeof formSchema>) => {\n    try {\n      await axios.post(\n        `/api/courses/${courseId}/sections/${section.id}`,\n        values\n      );\n      toast.success(\"Section Updated\");\n      router.refresh();\n    } catch (err) {\n      console.log(\"Failed to update the section\", err);\n      toast.error(\"Something went wrong!\");\n    }\n  };\n\n  return (\n    <>\n      <div className=\"flex flex-col gap-2 sm:flex-row sm:justify-between mb-7\">\n        <Link href={`/instructor/courses/${courseId}/sections`}>\n          <Button variant=\"outline\" className=\"text-sm font-medium\">\n            <ArrowLeft className=\"h-4 w-4 mr-2\" />\n            Back to curriculum\n          </Button>\n        </Link>\n\n        <div className=\"flex gap-5 items-start\">\n          <PublishButton\n            disabled={!isCompleted}\n            courseId={courseId}\n            sectionId={section.id}\n            isPublished={section.isPublished}\n            page=\"Section\"\n          />\n          <Delete item=\"section\" courseId={courseId} sectionId={section.id} />\n        </div>\n      </div>\n\n      <h1 className=\"text-xl font-bold\">Section Details</h1>\n      <p className=\"text-sm font-medium mt-2\">\n        Complete this section with detailed information, good video and\n        resources to give your students the best learning experience\n      </p>\n\n      <Form {...form}>\n        <form onSubmit={form.handleSubmit(onSubmit)} className=\"space-y-8 mt-5\">\n          <FormField\n            control={form.control}\n            name=\"title\"\n            render={({ field }) => (\n              <FormItem>\n                <FormLabel>\n                  Title <span className=\"text-red-500\">*</span>\n                </FormLabel>\n                <FormControl>\n                  <Input\n                    placeholder=\"Ex: Introduction to Web Development\"\n                    {...field}\n                  />\n                </FormControl>\n                <FormMessage />\n              </FormItem>\n            )}\n          />\n\n          <FormField\n            control={form.control}\n            name=\"description\"\n            render={({ field }) => (\n              <FormItem>\n                <FormLabel>\n                  Description <span className=\"text-red-500\">*</span>\n                </FormLabel>\n                <FormControl>\n                  <RichEditor\n                    placeholder=\"What is this section about?\"\n                    {...field}\n                  />\n                </FormControl>\n                <FormMessage />\n              </FormItem>\n            )}\n          />\n\n          {section.videoUrl && (\n            <div className=\"my-5\">\n              <MuxPlayer\n                playbackId={section.muxData?.playbackId || \"\"}\n                className=\"md:max-w-[600px]\"\n              />\n            </div>\n          )}\n          <FormField\n            control={form.control}\n            name=\"videoUrl\"\n            render={({ field }) => (\n              <FormItem className=\"flex flex-col\">\n                <FormLabel>\n                  Video <span className=\"text-red-500\">*</span>\n                </FormLabel>\n                <FormControl>\n                  <FileUpload\n                    value={field.value || \"\"}\n                    onChange={(url) => field.onChange(url)}\n                    endpoint=\"sectionVideo\"\n                    page=\"Edit Section\"\n                  />\n                </FormControl>\n                <FormMessage />\n              </FormItem>\n            )}\n          />\n\n          <FormField\n            control={form.control}\n            name=\"isFree\"\n            render={({ field }) => (\n              <FormItem className=\"flex flex-row items-center justify-between rounded-lg border p-3 shadow-sm\">\n                <div className=\"space-y-0.5\">\n                  <FormLabel>Accessibility</FormLabel>\n                  <FormDescription>\n                    Everyone can access this section for FREE\n                  </FormDescription>\n                </div>\n                <FormControl>\n                  <Switch\n                    checked={field.value}\n                    onCheckedChange={field.onChange}\n                  />\n                </FormControl>\n              </FormItem>\n            )}\n          />\n\n          <div className=\"flex gap-5\">\n            <Link href={`/instructor/courses/${courseId}/sections`}>\n              <Button variant=\"outline\" type=\"button\">\n                Cancel\n              </Button>\n            </Link>\n            <Button type=\"submit\" disabled={!isValid || isSubmitting}>\n              {isSubmitting ? (\n                <Loader2 className=\"h-4 w-4 animate-spin\" />\n              ) : (\n                \"Save\"\n              )}\n            </Button>\n          </div>\n        </form>\n      </Form>\n\n      <ResourceForm section={section} courseId={courseId} />\n    </>\n  );\n};\n\nexport default EditSectionForm;\n"
  },
  {
    "path": "components/sections/ProgressButton.tsx",
    "content": "\"use client\";\n\nimport axios from \"axios\";\nimport { useRouter } from \"next/navigation\";\nimport { useState } from \"react\";\nimport toast from \"react-hot-toast\";\n\nimport { Button } from \"@/components/ui/button\";\nimport { CheckCircle, Loader2 } from \"lucide-react\";\n\ninterface ProgressButtonProps {\n  courseId: string;\n  sectionId: string;\n  isCompleted: boolean;\n}\n\nconst ProgressButton = ({\n  courseId,\n  sectionId,\n  isCompleted,\n}: ProgressButtonProps) => {\n  const router = useRouter();\n  const [isLoading, setIsLoading] = useState(false);\n\n  const onClick = async () => {\n    try {\n      setIsLoading(true);\n      await axios.post(`/api/courses/${courseId}/sections/${sectionId}/progress`, {\n        isCompleted: !isCompleted,\n      });\n      toast.success(\"Progress updated!\");\n      router.refresh();\n    } catch (err) {\n      console.log(\"Failed to update progress\", err);\n      toast.error(\"Something went wrong!\");\n    } finally {\n      setIsLoading(false);\n    }\n  };\n\n  return (\n    <Button variant={isCompleted ? \"complete\" : \"default\"} onClick={onClick}>\n      {isLoading ? (\n        <Loader2 className=\"h-4 w-4 animate-spin\" />\n      ) : isCompleted ? (\n        <div className=\"flex items-center\">\n          <CheckCircle className=\"h-4 w-4 mr-2\" />\n          <span>Completed</span>\n        </div>\n      ) : (\n        \"Mark as complete\"\n      )}\n    </Button>\n  );\n};\n\nexport default ProgressButton;\n"
  },
  {
    "path": "components/sections/ResourceForm.tsx",
    "content": "\"use client\";\n\nimport { Resource, Section } from \"@prisma/client\";\nimport Link from \"next/link\";\nimport { useRouter } from \"next/navigation\";\nimport { z } from \"zod\";\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport { useForm } from \"react-hook-form\";\nimport toast from \"react-hot-toast\";\nimport axios from \"axios\";\nimport { File, Loader2, PlusCircle, X } from \"lucide-react\";\n\nimport { Button } from \"@/components/ui/button\";\nimport {\n  Form,\n  FormControl,\n  FormField,\n  FormItem,\n  FormLabel,\n  FormMessage,\n} from \"@/components/ui/form\";\nimport { Input } from \"@/components/ui/input\";\nimport FileUpload from \"@/components/custom/FileUpload\";\n\nconst formSchema = z.object({\n  name: z.string().min(2, {\n    message: \"Name is required and must be at least 2 characters long\",\n  }),\n  fileUrl: z.string().min(1, {\n    message: \"File is required\",\n  }),\n});\n\ninterface ResourceFormProps {\n  section: Section & { resources: Resource[] };\n  courseId: string;\n}\n\nconst ResourceForm = ({ section, courseId }: ResourceFormProps) => {\n  const router = useRouter();\n\n  // 1. Define your form.\n  const form = useForm<z.infer<typeof formSchema>>({\n    resolver: zodResolver(formSchema),\n    defaultValues: {\n      name: \"\",\n      fileUrl: \"\",\n    },\n  });\n\n  const { isValid, isSubmitting } = form.formState;\n\n  // 2. Define a submit handler.\n  const onSubmit = async (values: z.infer<typeof formSchema>) => {\n    try {\n      await axios.post(\n        `/api/courses/${courseId}/sections/${section.id}/resources`,\n        values\n      );\n      toast.success(\"New Resource uploaded!\");\n      form.reset();\n      router.refresh();\n    } catch (err) {\n      toast.error(\"Something went wrong!\");\n      console.log(\"Failed to upload resource\", err);\n    }\n  };\n\n  const onDelete = async (id: string) => {\n    try {\n      await axios.post(\n        `/api/courses/${courseId}/sections/${section.id}/resources/${id}`\n      );\n      toast.success(\"Resource deleted!\");\n      router.refresh();\n    } catch (err) {\n      toast.error(\"Something went wrong!\");\n      console.log(\"Failed to delete resource\", err);\n    }\n  };\n\n  return (\n    <>\n      <div className=\"flex gap-2 items-center text-xl font-bold mt-12\">\n        <PlusCircle />\n        Add Resources (optional)\n      </div>\n\n      <p className=\"text-sm font-medium mt-2\">\n        Add resources to this section to help students learn better.\n      </p>\n\n      <div className=\"mt-5 flex flex-col gap-5\">\n        {section.resources.map((resource) => (\n          <div key={resource.id} className=\"flex justify-between bg-[#FFF8EB] rounded-lg text-sm font-medium p-3\">\n            <div className=\"flex items-center\">\n              <File className=\"h-4 w-4 mr-4\" />\n              {resource.name}\n            </div>\n            <button\n              className=\"text-[#FDAB04]\"\n              disabled={isSubmitting}\n              onClick={() => onDelete(resource.id)}\n            >\n              {isSubmitting ? (\n                <Loader2 className=\"h-4 w-4 animate-spin\" />\n              ) : (\n                <X className=\"h-4 w-4\" />\n              )}\n            </button>\n          </div>\n        ))}\n\n        <Form {...form}>\n          <form\n            onSubmit={form.handleSubmit(onSubmit)}\n            className=\"space-y-8 my-5\"\n          >\n            <FormField\n              control={form.control}\n              name=\"name\"\n              render={({ field }) => (\n                <FormItem>\n                  <FormLabel>File Name</FormLabel>\n                  <FormControl>\n                    <Input placeholder=\"Ex: Textbook\" {...field} />\n                  </FormControl>\n                  <FormMessage />\n                </FormItem>\n              )}\n            />\n\n            <FormField\n              control={form.control}\n              name=\"fileUrl\"\n              render={({ field }) => (\n                <FormItem className=\"flex flex-col\">\n                  <FormLabel>Upload File</FormLabel>\n                  <FormControl>\n                    <FileUpload\n                      value={field.value || \"\"}\n                      onChange={(url) => field.onChange(url)}\n                      endpoint=\"sectionResource\"\n                      page=\"Edit Section\"\n                    />\n                  </FormControl>\n                  <FormMessage />\n                </FormItem>\n              )}\n            />\n\n            <Button type=\"submit\" disabled={!isValid || isSubmitting}>\n              {isSubmitting ? (\n                <Loader2 className=\"h-4 w-4 animate-spin\" />\n              ) : (\n                \"Upload\"\n              )}\n            </Button>\n          </form>\n        </Form>\n      </div>\n    </>\n  );\n};\n\nexport default ResourceForm;\n"
  },
  {
    "path": "components/sections/SectionList.tsx",
    "content": "import { Section } from \"@prisma/client\";\nimport {\n  DragDropContext,\n  Droppable,\n  Draggable,\n  DropResult,\n} from \"@hello-pangea/dnd\";\nimport { useEffect, useState } from \"react\";\nimport { Grip, Pencil } from \"lucide-react\";\n\ninterface SectionListProps {\n  items: Section[];\n  onReorder: (updateData: { id: string; position: number }[]) => void;\n  onEdit: (id: string) => void;\n}\n\nconst SectionList = ({ items, onReorder, onEdit }: SectionListProps) => {\n  const [isMounted, setIsMounted] = useState(false);\n  const [sections, setSections] = useState(items);\n\n  useEffect(() => {\n    setIsMounted(true);\n  }, []);\n\n  useEffect(() => {\n    setSections(items);\n  }, [items]);\n\n  const onDragEnd = (result: DropResult) => {\n    if (!result.destination) return;\n\n    const items = Array.from(sections);\n    const [reorderedItem] = items.splice(result.source.index, 1);\n    items.splice(result.destination.index, 0, reorderedItem);\n\n    const startIndex = Math.min(result.source.index, result.destination.index);\n    const endIndex = Math.max(result.source.index, result.destination.index);\n\n    const updatedSections = items.slice(startIndex, endIndex + 1);\n\n    setSections(items);\n\n    const bulkUpdateData = updatedSections.map((section) => ({\n      id: section.id,\n      position: items.findIndex((item) => item.id === section.id),\n    }));\n\n    onReorder(bulkUpdateData);\n  };\n\n  if (!isMounted) return null;\n\n  return (\n    <DragDropContext onDragEnd={onDragEnd}>\n      <Droppable droppableId=\"sections\">\n        {(provided) => (\n          <div\n            {...provided.droppableProps}\n            ref={provided.innerRef}\n            className={`${\n              sections.length > 0 ? \"my-10\" : \"mt-7\"\n            } flex flex-col gap-5`}\n          >\n            {sections.map((section, index) => (\n              <Draggable\n                key={section.id}\n                draggableId={section.id}\n                index={index}\n              >\n                {(provided) => (\n                  <div\n                    {...provided.draggableProps}\n                    ref={provided.innerRef}\n                    className=\"flex items-center bg-[#FFF8EB] rounded-lg text-sm font-medium p-3\"\n                  >\n                    <div {...provided.dragHandleProps}>\n                      <Grip className=\"h-4 w-4 cursor-pointer mr-4 hover:text-[#FDAB04]\" />\n                    </div>\n                    {section.title}\n                    <div className=\"ml-auto\">\n                      <Pencil\n                        className=\"h-4 w-4 cursor-pointer hover:text-[#FDAB04]\"\n                        onClick={() => onEdit(section.id)}\n                      />\n                    </div>\n                  </div>\n                )}\n              </Draggable>\n            ))}\n            {provided.placeholder}\n          </div>\n        )}\n      </Droppable>\n    </DragDropContext>\n  );\n};\n\nexport default SectionList;\n"
  },
  {
    "path": "components/sections/SectionsDetails.tsx",
    "content": "\"use client\";\n\nimport {\n  Course,\n  MuxData,\n  Progress,\n  Purchase,\n  Resource,\n  Section,\n} from \"@prisma/client\";\nimport toast from \"react-hot-toast\";\nimport { useState } from \"react\";\nimport axios from \"axios\";\nimport { File, Loader2, Lock } from \"lucide-react\";\n\nimport { Button } from \"@/components/ui/button\";\nimport ReadText from \"@/components/custom/ReadText\";\nimport MuxPlayer from \"@mux/mux-player-react\";\nimport Link from \"next/link\";\nimport ProgressButton from \"./ProgressButton\";\nimport SectionMenu from \"../layout/SectionMenu\";\n\ninterface SectionsDetailsProps {\n  course: Course & { sections: Section[] };\n  section: Section;\n  purchase: Purchase | null;\n  muxData: MuxData | null;\n  resources: Resource[] | [];\n  progress: Progress | null;\n}\n\nconst SectionsDetails = ({\n  course,\n  section,\n  purchase,\n  muxData,\n  resources,\n  progress,\n}: SectionsDetailsProps) => {\n  const [isLoading, setIsLoading] = useState(false);\n  const isLocked = !purchase && !section.isFree;\n\n  const buyCourse = async () => {\n    try {\n      setIsLoading(true);\n      const response = await axios.post(`/api/courses/${course.id}/checkout`);\n      window.location.assign(response.data.url);\n    } catch (err) {\n      console.log(\"Failed to chechout course\", err);\n      toast.error(\"Something went wrong!\");\n    } finally {\n      setIsLoading(false);\n    }\n  };\n\n  return (\n    <div className=\"px-6 py-4 flex flex-col gap-5\">\n      <div className=\"flex flex-col md:flex-row md:justify-between md:items-center\">\n        <h1 className=\"text-2xl font-bold max-md:mb-4\">{section.title}</h1>\n\n        <div className=\"flex gap-4\">\n          <SectionMenu course={course} />\n          {!purchase ? (\n            <Button onClick={buyCourse}>\n              {isLoading ? (\n                <Loader2 className=\"h-4 w-4 animate-spin\" />\n              ) : (\n                <p>Buy this course</p>\n              )}\n            </Button>\n          ) : (\n            <ProgressButton\n              courseId={course.id}\n              sectionId={section.id}\n              isCompleted={!!progress?.isCompleted}\n            /> // !! converts falsy values to boolean false\n          )}\n        </div>\n      </div>\n\n      <ReadText value={section.description!} />\n\n      {isLocked ? (\n        <div className=\"px-10 flex flex-col gap-5 items-center bg-[#FFF8EB]\">\n          <Lock className=\"h-8 w-8\" />\n          <p className=\"text-sm font-bold\">\n            Video for this section is locked!. Please buy the course to access\n          </p>\n        </div>\n      ) : (\n        <MuxPlayer\n          playbackId={muxData?.playbackId || \"\"}\n          className=\"md:max-w-[600px]\"\n        />\n      )}\n\n      <div>\n        <h2 className=\"text-xl font-bold mb-5\">Resources</h2>\n        {resources.map((resource) => (\n          <Link\n            key={resource.id}\n            href={resource.fileUrl}\n            target=\"_blank\"\n            className=\"flex items-center bg-[#FFF8EB] rounded-lg text-sm font-medium p-3\"\n          >\n            <File className=\"h-4 w-4 mr-4\" />\n            {resource.name}\n          </Link>\n        ))}\n      </div>\n    </div>\n  );\n};\n\nexport default SectionsDetails;\n"
  },
  {
    "path": "components/ui/alert-dialog.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as AlertDialogPrimitive from \"@radix-ui/react-alert-dialog\"\n\nimport { cn } from \"@/lib/utils\"\nimport { buttonVariants } from \"@/components/ui/button\"\n\nconst AlertDialog = AlertDialogPrimitive.Root\n\nconst AlertDialogTrigger = AlertDialogPrimitive.Trigger\n\nconst AlertDialogPortal = AlertDialogPrimitive.Portal\n\nconst AlertDialogOverlay = React.forwardRef<\n  React.ElementRef<typeof AlertDialogPrimitive.Overlay>,\n  React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>\n>(({ className, ...props }, ref) => (\n  <AlertDialogPrimitive.Overlay\n    className={cn(\n      \"fixed inset-0 z-50 bg-black/80  data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n      className\n    )}\n    {...props}\n    ref={ref}\n  />\n))\nAlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName\n\nconst AlertDialogContent = React.forwardRef<\n  React.ElementRef<typeof AlertDialogPrimitive.Content>,\n  React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content>\n>(({ className, ...props }, ref) => (\n  <AlertDialogPortal>\n    <AlertDialogOverlay />\n    <AlertDialogPrimitive.Content\n      ref={ref}\n      className={cn(\n        \"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg\",\n        className\n      )}\n      {...props}\n    />\n  </AlertDialogPortal>\n))\nAlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName\n\nconst AlertDialogHeader = ({\n  className,\n  ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n  <div\n    className={cn(\n      \"flex flex-col space-y-2 text-center sm:text-left\",\n      className\n    )}\n    {...props}\n  />\n)\nAlertDialogHeader.displayName = \"AlertDialogHeader\"\n\nconst AlertDialogFooter = ({\n  className,\n  ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n  <div\n    className={cn(\n      \"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2\",\n      className\n    )}\n    {...props}\n  />\n)\nAlertDialogFooter.displayName = \"AlertDialogFooter\"\n\nconst AlertDialogTitle = React.forwardRef<\n  React.ElementRef<typeof AlertDialogPrimitive.Title>,\n  React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title>\n>(({ className, ...props }, ref) => (\n  <AlertDialogPrimitive.Title\n    ref={ref}\n    className={cn(\"text-lg font-semibold\", className)}\n    {...props}\n  />\n))\nAlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName\n\nconst AlertDialogDescription = React.forwardRef<\n  React.ElementRef<typeof AlertDialogPrimitive.Description>,\n  React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description>\n>(({ className, ...props }, ref) => (\n  <AlertDialogPrimitive.Description\n    ref={ref}\n    className={cn(\"text-sm text-muted-foreground\", className)}\n    {...props}\n  />\n))\nAlertDialogDescription.displayName =\n  AlertDialogPrimitive.Description.displayName\n\nconst AlertDialogAction = React.forwardRef<\n  React.ElementRef<typeof AlertDialogPrimitive.Action>,\n  React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action>\n>(({ className, ...props }, ref) => (\n  <AlertDialogPrimitive.Action\n    ref={ref}\n    className={cn(buttonVariants(), className)}\n    {...props}\n  />\n))\nAlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName\n\nconst AlertDialogCancel = React.forwardRef<\n  React.ElementRef<typeof AlertDialogPrimitive.Cancel>,\n  React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Cancel>\n>(({ className, ...props }, ref) => (\n  <AlertDialogPrimitive.Cancel\n    ref={ref}\n    className={cn(\n      buttonVariants({ variant: \"outline\" }),\n      \"mt-2 sm:mt-0\",\n      className\n    )}\n    {...props}\n  />\n))\nAlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName\n\nexport {\n  AlertDialog,\n  AlertDialogPortal,\n  AlertDialogOverlay,\n  AlertDialogTrigger,\n  AlertDialogContent,\n  AlertDialogHeader,\n  AlertDialogFooter,\n  AlertDialogTitle,\n  AlertDialogDescription,\n  AlertDialogAction,\n  AlertDialogCancel,\n}\n"
  },
  {
    "path": "components/ui/alert.tsx",
    "content": "import * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst alertVariants = cva(\n  \"relative w-full rounded-lg border p-2 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground\",\n  {\n    variants: {\n      variant: {\n        default: \"bg-background text-foreground\",\n        complete: \"bg-[#C5DDD6] text-foreground\",\n        destructive:\n          \"border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n    },\n  }\n)\n\nconst Alert = React.forwardRef<\n  HTMLDivElement,\n  React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>\n>(({ className, variant, ...props }, ref) => (\n  <div\n    ref={ref}\n    role=\"alert\"\n    className={cn(alertVariants({ variant }), className)}\n    {...props}\n  />\n))\nAlert.displayName = \"Alert\"\n\nconst AlertTitle = React.forwardRef<\n  HTMLParagraphElement,\n  React.HTMLAttributes<HTMLHeadingElement>\n>(({ className, ...props }, ref) => (\n  <h5\n    ref={ref}\n    className={cn(\"mb-1 font-medium leading-none tracking-tight\", className)}\n    {...props}\n  />\n))\nAlertTitle.displayName = \"AlertTitle\"\n\nconst AlertDescription = React.forwardRef<\n  HTMLParagraphElement,\n  React.HTMLAttributes<HTMLParagraphElement>\n>(({ className, ...props }, ref) => (\n  <div\n    ref={ref}\n    className={cn(\"text-sm [&_p]:leading-relaxed\", className)}\n    {...props}\n  />\n))\nAlertDescription.displayName = \"AlertDescription\"\n\nexport { Alert, AlertTitle, AlertDescription }\n"
  },
  {
    "path": "components/ui/badge.tsx",
    "content": "import * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst badgeVariants = cva(\n  \"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2\",\n  {\n    variants: {\n      variant: {\n        default:\n          \"border-transparent bg-primary text-primary-foreground hover:bg-primary/80\",\n        secondary:\n          \"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n        destructive:\n          \"border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80\",\n        outline: \"text-foreground\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n    },\n  }\n)\n\nexport interface BadgeProps\n  extends React.HTMLAttributes<HTMLDivElement>,\n    VariantProps<typeof badgeVariants> {}\n\nfunction Badge({ className, variant, ...props }: BadgeProps) {\n  return (\n    <div className={cn(badgeVariants({ variant }), className)} {...props} />\n  )\n}\n\nexport { Badge, badgeVariants }\n"
  },
  {
    "path": "components/ui/button.tsx",
    "content": "import * as React from \"react\"\nimport { Slot } from \"@radix-ui/react-slot\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst buttonVariants = cva(\n  \"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50\",\n  {\n    variants: {\n      variant: {\n        default: \"bg-[#FDAB04] text-black-foreground hover:bg-[#FDAB04]/90\",\n        destructive:\n          \"bg-destructive text-destructive-foreground hover:bg-destructive/90\",\n        outline:\n          \"border border-input bg-background hover:bg-[#FFF8EB] hover:text-accent-foreground\",\n        secondary:\n          \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n        ghost: \"hover:bg-accent hover:text-accent-foreground\",\n        link: \"text-primary underline-offset-4 hover:underline\",\n        complete: \"bg-[#C5DDD6] text-black-foreground hover:bg-[#C5DDD6]/90\"\n      },\n      size: {\n        default: \"h-10 px-4 py-2\",\n        sm: \"h-9 rounded-md px-3\",\n        lg: \"h-11 rounded-md px-8\",\n        icon: \"h-10 w-10\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n      size: \"default\",\n    },\n  }\n)\n\nexport interface ButtonProps\n  extends React.ButtonHTMLAttributes<HTMLButtonElement>,\n    VariantProps<typeof buttonVariants> {\n  asChild?: boolean\n}\n\nconst Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n  ({ className, variant, size, asChild = false, ...props }, ref) => {\n    const Comp = asChild ? Slot : \"button\"\n    return (\n      <Comp\n        className={cn(buttonVariants({ variant, size, className }))}\n        ref={ref}\n        {...props}\n      />\n    )\n  }\n)\nButton.displayName = \"Button\"\n\nexport { Button, buttonVariants }\n"
  },
  {
    "path": "components/ui/card.tsx",
    "content": "import * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst Card = React.forwardRef<\n  HTMLDivElement,\n  React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n  <div\n    ref={ref}\n    className={cn(\n      \"rounded-lg border bg-card text-card-foreground shadow-sm\",\n      className\n    )}\n    {...props}\n  />\n))\nCard.displayName = \"Card\"\n\nconst CardHeader = React.forwardRef<\n  HTMLDivElement,\n  React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n  <div\n    ref={ref}\n    className={cn(\"flex flex-col space-y-1.5 p-6\", className)}\n    {...props}\n  />\n))\nCardHeader.displayName = \"CardHeader\"\n\nconst CardTitle = React.forwardRef<\n  HTMLParagraphElement,\n  React.HTMLAttributes<HTMLHeadingElement>\n>(({ className, ...props }, ref) => (\n  <h3\n    ref={ref}\n    className={cn(\n      \"text-2xl font-semibold leading-none tracking-tight\",\n      className\n    )}\n    {...props}\n  />\n))\nCardTitle.displayName = \"CardTitle\"\n\nconst CardDescription = React.forwardRef<\n  HTMLParagraphElement,\n  React.HTMLAttributes<HTMLParagraphElement>\n>(({ className, ...props }, ref) => (\n  <p\n    ref={ref}\n    className={cn(\"text-sm text-muted-foreground\", className)}\n    {...props}\n  />\n))\nCardDescription.displayName = \"CardDescription\"\n\nconst CardContent = React.forwardRef<\n  HTMLDivElement,\n  React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n  <div ref={ref} className={cn(\"p-6 pt-0\", className)} {...props} />\n))\nCardContent.displayName = \"CardContent\"\n\nconst CardFooter = React.forwardRef<\n  HTMLDivElement,\n  React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n  <div\n    ref={ref}\n    className={cn(\"flex items-center p-6 pt-0\", className)}\n    {...props}\n  />\n))\nCardFooter.displayName = \"CardFooter\"\n\nexport { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }\n"
  },
  {
    "path": "components/ui/command.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { type DialogProps } from \"@radix-ui/react-dialog\"\nimport { Command as CommandPrimitive } from \"cmdk\"\nimport { Search } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Dialog, DialogContent } from \"@/components/ui/dialog\"\n\nconst Command = React.forwardRef<\n  React.ElementRef<typeof CommandPrimitive>,\n  React.ComponentPropsWithoutRef<typeof CommandPrimitive>\n>(({ className, ...props }, ref) => (\n  <CommandPrimitive\n    ref={ref}\n    className={cn(\n      \"flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground\",\n      className\n    )}\n    {...props}\n  />\n))\nCommand.displayName = CommandPrimitive.displayName\n\ninterface CommandDialogProps extends DialogProps {}\n\nconst CommandDialog = ({ children, ...props }: CommandDialogProps) => {\n  return (\n    <Dialog {...props}>\n      <DialogContent className=\"overflow-hidden p-0 shadow-lg\">\n        <Command className=\"[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5\">\n          {children}\n        </Command>\n      </DialogContent>\n    </Dialog>\n  )\n}\n\nconst CommandInput = React.forwardRef<\n  React.ElementRef<typeof CommandPrimitive.Input>,\n  React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>\n>(({ className, ...props }, ref) => (\n  <div className=\"flex items-center border-b px-3\" cmdk-input-wrapper=\"\">\n    <Search className=\"mr-2 h-4 w-4 shrink-0 opacity-50\" />\n    <CommandPrimitive.Input\n      ref={ref}\n      className={cn(\n        \"flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50\",\n        className\n      )}\n      {...props}\n    />\n  </div>\n))\n\nCommandInput.displayName = CommandPrimitive.Input.displayName\n\nconst CommandList = React.forwardRef<\n  React.ElementRef<typeof CommandPrimitive.List>,\n  React.ComponentPropsWithoutRef<typeof CommandPrimitive.List>\n>(({ className, ...props }, ref) => (\n  <CommandPrimitive.List\n    ref={ref}\n    className={cn(\"max-h-[300px] overflow-y-auto overflow-x-hidden\", className)}\n    {...props}\n  />\n))\n\nCommandList.displayName = CommandPrimitive.List.displayName\n\nconst CommandEmpty = React.forwardRef<\n  React.ElementRef<typeof CommandPrimitive.Empty>,\n  React.ComponentPropsWithoutRef<typeof CommandPrimitive.Empty>\n>((props, ref) => (\n  <CommandPrimitive.Empty\n    ref={ref}\n    className=\"py-6 text-center text-sm\"\n    {...props}\n  />\n))\n\nCommandEmpty.displayName = CommandPrimitive.Empty.displayName\n\nconst CommandGroup = React.forwardRef<\n  React.ElementRef<typeof CommandPrimitive.Group>,\n  React.ComponentPropsWithoutRef<typeof CommandPrimitive.Group>\n>(({ className, ...props }, ref) => (\n  <CommandPrimitive.Group\n    ref={ref}\n    className={cn(\n      \"overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground\",\n      className\n    )}\n    {...props}\n  />\n))\n\nCommandGroup.displayName = CommandPrimitive.Group.displayName\n\nconst CommandSeparator = React.forwardRef<\n  React.ElementRef<typeof CommandPrimitive.Separator>,\n  React.ComponentPropsWithoutRef<typeof CommandPrimitive.Separator>\n>(({ className, ...props }, ref) => (\n  <CommandPrimitive.Separator\n    ref={ref}\n    className={cn(\"-mx-1 h-px bg-border\", className)}\n    {...props}\n  />\n))\nCommandSeparator.displayName = CommandPrimitive.Separator.displayName\n\nconst CommandItem = React.forwardRef<\n  React.ElementRef<typeof CommandPrimitive.Item>,\n  React.ComponentPropsWithoutRef<typeof CommandPrimitive.Item>\n>(({ className, ...props }, ref) => (\n  <CommandPrimitive.Item\n    ref={ref}\n    className={cn(\n      \"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none aria-selected:bg-accent aria-selected:text-accent-foreground data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50\",\n      className\n    )}\n    {...props}\n  />\n))\n\nCommandItem.displayName = CommandPrimitive.Item.displayName\n\nconst CommandShortcut = ({\n  className,\n  ...props\n}: React.HTMLAttributes<HTMLSpanElement>) => {\n  return (\n    <span\n      className={cn(\n        \"ml-auto text-xs tracking-widest text-muted-foreground\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\nCommandShortcut.displayName = \"CommandShortcut\"\n\nexport {\n  Command,\n  CommandDialog,\n  CommandInput,\n  CommandList,\n  CommandEmpty,\n  CommandGroup,\n  CommandItem,\n  CommandShortcut,\n  CommandSeparator,\n}\n"
  },
  {
    "path": "components/ui/dialog.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as DialogPrimitive from \"@radix-ui/react-dialog\"\nimport { X } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst Dialog = DialogPrimitive.Root\n\nconst DialogTrigger = DialogPrimitive.Trigger\n\nconst DialogPortal = DialogPrimitive.Portal\n\nconst DialogClose = DialogPrimitive.Close\n\nconst DialogOverlay = React.forwardRef<\n  React.ElementRef<typeof DialogPrimitive.Overlay>,\n  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>\n>(({ className, ...props }, ref) => (\n  <DialogPrimitive.Overlay\n    ref={ref}\n    className={cn(\n      \"fixed inset-0 z-50 bg-black/80  data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n      className\n    )}\n    {...props}\n  />\n))\nDialogOverlay.displayName = DialogPrimitive.Overlay.displayName\n\nconst DialogContent = React.forwardRef<\n  React.ElementRef<typeof DialogPrimitive.Content>,\n  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>\n>(({ className, children, ...props }, ref) => (\n  <DialogPortal>\n    <DialogOverlay />\n    <DialogPrimitive.Content\n      ref={ref}\n      className={cn(\n        \"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg\",\n        className\n      )}\n      {...props}\n    >\n      {children}\n      <DialogPrimitive.Close className=\"absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground\">\n        <X className=\"h-4 w-4\" />\n        <span className=\"sr-only\">Close</span>\n      </DialogPrimitive.Close>\n    </DialogPrimitive.Content>\n  </DialogPortal>\n))\nDialogContent.displayName = DialogPrimitive.Content.displayName\n\nconst DialogHeader = ({\n  className,\n  ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n  <div\n    className={cn(\n      \"flex flex-col space-y-1.5 text-center sm:text-left\",\n      className\n    )}\n    {...props}\n  />\n)\nDialogHeader.displayName = \"DialogHeader\"\n\nconst DialogFooter = ({\n  className,\n  ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n  <div\n    className={cn(\n      \"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2\",\n      className\n    )}\n    {...props}\n  />\n)\nDialogFooter.displayName = \"DialogFooter\"\n\nconst DialogTitle = React.forwardRef<\n  React.ElementRef<typeof DialogPrimitive.Title>,\n  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>\n>(({ className, ...props }, ref) => (\n  <DialogPrimitive.Title\n    ref={ref}\n    className={cn(\n      \"text-lg font-semibold leading-none tracking-tight\",\n      className\n    )}\n    {...props}\n  />\n))\nDialogTitle.displayName = DialogPrimitive.Title.displayName\n\nconst DialogDescription = React.forwardRef<\n  React.ElementRef<typeof DialogPrimitive.Description>,\n  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>\n>(({ className, ...props }, ref) => (\n  <DialogPrimitive.Description\n    ref={ref}\n    className={cn(\"text-sm text-muted-foreground\", className)}\n    {...props}\n  />\n))\nDialogDescription.displayName = DialogPrimitive.Description.displayName\n\nexport {\n  Dialog,\n  DialogPortal,\n  DialogOverlay,\n  DialogClose,\n  DialogTrigger,\n  DialogContent,\n  DialogHeader,\n  DialogFooter,\n  DialogTitle,\n  DialogDescription,\n}\n"
  },
  {
    "path": "components/ui/form.tsx",
    "content": "import * as React from \"react\"\nimport * as LabelPrimitive from \"@radix-ui/react-label\"\nimport { Slot } from \"@radix-ui/react-slot\"\nimport {\n  Controller,\n  ControllerProps,\n  FieldPath,\n  FieldValues,\n  FormProvider,\n  useFormContext,\n} from \"react-hook-form\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Label } from \"@/components/ui/label\"\n\nconst Form = FormProvider\n\ntype FormFieldContextValue<\n  TFieldValues extends FieldValues = FieldValues,\n  TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>\n> = {\n  name: TName\n}\n\nconst FormFieldContext = React.createContext<FormFieldContextValue>(\n  {} as FormFieldContextValue\n)\n\nconst FormField = <\n  TFieldValues extends FieldValues = FieldValues,\n  TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>\n>({\n  ...props\n}: ControllerProps<TFieldValues, TName>) => {\n  return (\n    <FormFieldContext.Provider value={{ name: props.name }}>\n      <Controller {...props} />\n    </FormFieldContext.Provider>\n  )\n}\n\nconst useFormField = () => {\n  const fieldContext = React.useContext(FormFieldContext)\n  const itemContext = React.useContext(FormItemContext)\n  const { getFieldState, formState } = useFormContext()\n\n  const fieldState = getFieldState(fieldContext.name, formState)\n\n  if (!fieldContext) {\n    throw new Error(\"useFormField should be used within <FormField>\")\n  }\n\n  const { id } = itemContext\n\n  return {\n    id,\n    name: fieldContext.name,\n    formItemId: `${id}-form-item`,\n    formDescriptionId: `${id}-form-item-description`,\n    formMessageId: `${id}-form-item-message`,\n    ...fieldState,\n  }\n}\n\ntype FormItemContextValue = {\n  id: string\n}\n\nconst FormItemContext = React.createContext<FormItemContextValue>(\n  {} as FormItemContextValue\n)\n\nconst FormItem = React.forwardRef<\n  HTMLDivElement,\n  React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => {\n  const id = React.useId()\n\n  return (\n    <FormItemContext.Provider value={{ id }}>\n      <div ref={ref} className={cn(\"space-y-2\", className)} {...props} />\n    </FormItemContext.Provider>\n  )\n})\nFormItem.displayName = \"FormItem\"\n\nconst FormLabel = React.forwardRef<\n  React.ElementRef<typeof LabelPrimitive.Root>,\n  React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>\n>(({ className, ...props }, ref) => {\n  const { error, formItemId } = useFormField()\n\n  return (\n    <Label\n      ref={ref}\n      className={cn(error && \"text-destructive\", className)}\n      htmlFor={formItemId}\n      {...props}\n    />\n  )\n})\nFormLabel.displayName = \"FormLabel\"\n\nconst FormControl = React.forwardRef<\n  React.ElementRef<typeof Slot>,\n  React.ComponentPropsWithoutRef<typeof Slot>\n>(({ ...props }, ref) => {\n  const { error, formItemId, formDescriptionId, formMessageId } = useFormField()\n\n  return (\n    <Slot\n      ref={ref}\n      id={formItemId}\n      aria-describedby={\n        !error\n          ? `${formDescriptionId}`\n          : `${formDescriptionId} ${formMessageId}`\n      }\n      aria-invalid={!!error}\n      {...props}\n    />\n  )\n})\nFormControl.displayName = \"FormControl\"\n\nconst FormDescription = React.forwardRef<\n  HTMLParagraphElement,\n  React.HTMLAttributes<HTMLParagraphElement>\n>(({ className, ...props }, ref) => {\n  const { formDescriptionId } = useFormField()\n\n  return (\n    <p\n      ref={ref}\n      id={formDescriptionId}\n      className={cn(\"text-sm text-muted-foreground\", className)}\n      {...props}\n    />\n  )\n})\nFormDescription.displayName = \"FormDescription\"\n\nconst FormMessage = React.forwardRef<\n  HTMLParagraphElement,\n  React.HTMLAttributes<HTMLParagraphElement>\n>(({ className, children, ...props }, ref) => {\n  const { error, formMessageId } = useFormField()\n  const body = error ? String(error?.message) : children\n\n  if (!body) {\n    return null\n  }\n\n  return (\n    <p\n      ref={ref}\n      id={formMessageId}\n      className={cn(\"text-sm font-medium text-destructive\", className)}\n      {...props}\n    >\n      {body}\n    </p>\n  )\n})\nFormMessage.displayName = \"FormMessage\"\n\nexport {\n  useFormField,\n  Form,\n  FormItem,\n  FormLabel,\n  FormControl,\n  FormDescription,\n  FormMessage,\n  FormField,\n}\n"
  },
  {
    "path": "components/ui/input.tsx",
    "content": "import * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nexport interface InputProps\n  extends React.InputHTMLAttributes<HTMLInputElement> {}\n\nconst Input = React.forwardRef<HTMLInputElement, InputProps>(\n  ({ className, type, ...props }, ref) => {\n    return (\n      <input\n        type={type}\n        className={cn(\n          \"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50\",\n          className\n        )}\n        ref={ref}\n        {...props}\n      />\n    )\n  }\n)\nInput.displayName = \"Input\"\n\nexport { Input }\n"
  },
  {
    "path": "components/ui/label.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as LabelPrimitive from \"@radix-ui/react-label\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst labelVariants = cva(\n  \"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70\"\n)\n\nconst Label = React.forwardRef<\n  React.ElementRef<typeof LabelPrimitive.Root>,\n  React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &\n    VariantProps<typeof labelVariants>\n>(({ className, ...props }, ref) => (\n  <LabelPrimitive.Root\n    ref={ref}\n    className={cn(labelVariants(), className)}\n    {...props}\n  />\n))\nLabel.displayName = LabelPrimitive.Root.displayName\n\nexport { Label }\n"
  },
  {
    "path": "components/ui/popover.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as PopoverPrimitive from \"@radix-ui/react-popover\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst Popover = PopoverPrimitive.Root\n\nconst PopoverTrigger = PopoverPrimitive.Trigger\n\nconst PopoverContent = React.forwardRef<\n  React.ElementRef<typeof PopoverPrimitive.Content>,\n  React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>\n>(({ className, align = \"center\", sideOffset = 4, ...props }, ref) => (\n  <PopoverPrimitive.Portal>\n    <PopoverPrimitive.Content\n      ref={ref}\n      align={align}\n      sideOffset={sideOffset}\n      className={cn(\n        \"z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2\",\n        className\n      )}\n      {...props}\n    />\n  </PopoverPrimitive.Portal>\n))\nPopoverContent.displayName = PopoverPrimitive.Content.displayName\n\nexport { Popover, PopoverTrigger, PopoverContent }\n"
  },
  {
    "path": "components/ui/progress.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as ProgressPrimitive from \"@radix-ui/react-progress\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst Progress = React.forwardRef<\n  React.ElementRef<typeof ProgressPrimitive.Root>,\n  React.ComponentPropsWithoutRef<typeof ProgressPrimitive.Root>\n>(({ className, value, ...props }, ref) => (\n  <ProgressPrimitive.Root\n    ref={ref}\n    className={cn(\n      \"relative h-4 w-full overflow-hidden rounded-full bg-secondary\",\n      className\n    )}\n    {...props}\n  >\n    <ProgressPrimitive.Indicator\n      className=\"h-full w-full flex-1 bg-[#FDAB04] transition-all\"\n      style={{ transform: `translateX(-${100 - (value || 0)}%)` }}\n    />\n  </ProgressPrimitive.Root>\n))\nProgress.displayName = ProgressPrimitive.Root.displayName\n\nexport { Progress }\n"
  },
  {
    "path": "components/ui/sheet.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as SheetPrimitive from \"@radix-ui/react-dialog\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { X } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst Sheet = SheetPrimitive.Root\n\nconst SheetTrigger = SheetPrimitive.Trigger\n\nconst SheetClose = SheetPrimitive.Close\n\nconst SheetPortal = SheetPrimitive.Portal\n\nconst SheetOverlay = React.forwardRef<\n  React.ElementRef<typeof SheetPrimitive.Overlay>,\n  React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>\n>(({ className, ...props }, ref) => (\n  <SheetPrimitive.Overlay\n    className={cn(\n      \"fixed inset-0 z-50 bg-black/80  data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n      className\n    )}\n    {...props}\n    ref={ref}\n  />\n))\nSheetOverlay.displayName = SheetPrimitive.Overlay.displayName\n\nconst sheetVariants = cva(\n  \"fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500\",\n  {\n    variants: {\n      side: {\n        top: \"inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top\",\n        bottom:\n          \"inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom\",\n        left: \"inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm\",\n        right:\n          \"inset-y-0 right-0 h-full w-3/4  border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm\",\n      },\n    },\n    defaultVariants: {\n      side: \"right\",\n    },\n  }\n)\n\ninterface SheetContentProps\n  extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,\n    VariantProps<typeof sheetVariants> {}\n\nconst SheetContent = React.forwardRef<\n  React.ElementRef<typeof SheetPrimitive.Content>,\n  SheetContentProps\n>(({ side = \"right\", className, children, ...props }, ref) => (\n  <SheetPortal>\n    <SheetOverlay />\n    <SheetPrimitive.Content\n      ref={ref}\n      className={cn(sheetVariants({ side }), className)}\n      {...props}\n    >\n      {children}\n      <SheetPrimitive.Close className=\"absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary\">\n        <X className=\"h-4 w-4\" />\n        <span className=\"sr-only\">Close</span>\n      </SheetPrimitive.Close>\n    </SheetPrimitive.Content>\n  </SheetPortal>\n))\nSheetContent.displayName = SheetPrimitive.Content.displayName\n\nconst SheetHeader = ({\n  className,\n  ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n  <div\n    className={cn(\n      \"flex flex-col space-y-2 text-center sm:text-left\",\n      className\n    )}\n    {...props}\n  />\n)\nSheetHeader.displayName = \"SheetHeader\"\n\nconst SheetFooter = ({\n  className,\n  ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n  <div\n    className={cn(\n      \"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2\",\n      className\n    )}\n    {...props}\n  />\n)\nSheetFooter.displayName = \"SheetFooter\"\n\nconst SheetTitle = React.forwardRef<\n  React.ElementRef<typeof SheetPrimitive.Title>,\n  React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>\n>(({ className, ...props }, ref) => (\n  <SheetPrimitive.Title\n    ref={ref}\n    className={cn(\"text-lg font-semibold text-foreground\", className)}\n    {...props}\n  />\n))\nSheetTitle.displayName = SheetPrimitive.Title.displayName\n\nconst SheetDescription = React.forwardRef<\n  React.ElementRef<typeof SheetPrimitive.Description>,\n  React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description>\n>(({ className, ...props }, ref) => (\n  <SheetPrimitive.Description\n    ref={ref}\n    className={cn(\"text-sm text-muted-foreground\", className)}\n    {...props}\n  />\n))\nSheetDescription.displayName = SheetPrimitive.Description.displayName\n\nexport {\n  Sheet,\n  SheetPortal,\n  SheetOverlay,\n  SheetTrigger,\n  SheetClose,\n  SheetContent,\n  SheetHeader,\n  SheetFooter,\n  SheetTitle,\n  SheetDescription,\n}\n"
  },
  {
    "path": "components/ui/switch.tsx",
    "content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as SwitchPrimitives from \"@radix-ui/react-switch\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst Switch = React.forwardRef<\n  React.ElementRef<typeof SwitchPrimitives.Root>,\n  React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>\n>(({ className, ...props }, ref) => (\n  <SwitchPrimitives.Root\n    className={cn(\n      \"peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-[#FDAB04] data-[state=unchecked]:bg-input\",\n      className\n    )}\n    {...props}\n    ref={ref}\n  >\n    <SwitchPrimitives.Thumb\n      className={cn(\n        \"pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0\"\n      )}\n    />\n  </SwitchPrimitives.Root>\n))\nSwitch.displayName = SwitchPrimitives.Root.displayName\n\nexport { Switch }\n"
  },
  {
    "path": "components/ui/table.tsx",
    "content": "import * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst Table = React.forwardRef<\n  HTMLTableElement,\n  React.HTMLAttributes<HTMLTableElement>\n>(({ className, ...props }, ref) => (\n  <div className=\"relative w-full overflow-auto\">\n    <table\n      ref={ref}\n      className={cn(\"w-full caption-bottom text-sm\", className)}\n      {...props}\n    />\n  </div>\n))\nTable.displayName = \"Table\"\n\nconst TableHeader = React.forwardRef<\n  HTMLTableSectionElement,\n  React.HTMLAttributes<HTMLTableSectionElement>\n>(({ className, ...props }, ref) => (\n  <thead ref={ref} className={cn(\"[&_tr]:border-b\", className)} {...props} />\n))\nTableHeader.displayName = \"TableHeader\"\n\nconst TableBody = React.forwardRef<\n  HTMLTableSectionElement,\n  React.HTMLAttributes<HTMLTableSectionElement>\n>(({ className, ...props }, ref) => (\n  <tbody\n    ref={ref}\n    className={cn(\"[&_tr:last-child]:border-0\", className)}\n    {...props}\n  />\n))\nTableBody.displayName = \"TableBody\"\n\nconst TableFooter = React.forwardRef<\n  HTMLTableSectionElement,\n  React.HTMLAttributes<HTMLTableSectionElement>\n>(({ className, ...props }, ref) => (\n  <tfoot\n    ref={ref}\n    className={cn(\n      \"border-t bg-muted/50 font-medium [&>tr]:last:border-b-0\",\n      className\n    )}\n    {...props}\n  />\n))\nTableFooter.displayName = \"TableFooter\"\n\nconst TableRow = React.forwardRef<\n  HTMLTableRowElement,\n  React.HTMLAttributes<HTMLTableRowElement>\n>(({ className, ...props }, ref) => (\n  <tr\n    ref={ref}\n    className={cn(\n      \"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted\",\n      className\n    )}\n    {...props}\n  />\n))\nTableRow.displayName = \"TableRow\"\n\nconst TableHead = React.forwardRef<\n  HTMLTableCellElement,\n  React.ThHTMLAttributes<HTMLTableCellElement>\n>(({ className, ...props }, ref) => (\n  <th\n    ref={ref}\n    className={cn(\n      \"h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0\",\n      className\n    )}\n    {...props}\n  />\n))\nTableHead.displayName = \"TableHead\"\n\nconst TableCell = React.forwardRef<\n  HTMLTableCellElement,\n  React.TdHTMLAttributes<HTMLTableCellElement>\n>(({ className, ...props }, ref) => (\n  <td\n    ref={ref}\n    className={cn(\"p-4 align-middle [&:has([role=checkbox])]:pr-0\", className)}\n    {...props}\n  />\n))\nTableCell.displayName = \"TableCell\"\n\nconst TableCaption = React.forwardRef<\n  HTMLTableCaptionElement,\n  React.HTMLAttributes<HTMLTableCaptionElement>\n>(({ className, ...props }, ref) => (\n  <caption\n    ref={ref}\n    className={cn(\"mt-4 text-sm text-muted-foreground\", className)}\n    {...props}\n  />\n))\nTableCaption.displayName = \"TableCaption\"\n\nexport {\n  Table,\n  TableHeader,\n  TableBody,\n  TableFooter,\n  TableHead,\n  TableRow,\n  TableCell,\n  TableCaption,\n}\n"
  },
  {
    "path": "components.json",
    "content": "{\n  \"$schema\": \"https://ui.shadcn.com/schema.json\",\n  \"style\": \"default\",\n  \"rsc\": true,\n  \"tsx\": true,\n  \"tailwind\": {\n    \"config\": \"tailwind.config.ts\",\n    \"css\": \"app/globals.css\",\n    \"baseColor\": \"slate\",\n    \"cssVariables\": true,\n    \"prefix\": \"\"\n  },\n  \"aliases\": {\n    \"components\": \"@/components\",\n    \"utils\": \"@/lib/utils\"\n  }\n}"
  },
  {
    "path": "lib/db.ts",
    "content": "import { PrismaClient } from \"@prisma/client\";\n\ndeclare global {\n  var prisma: PrismaClient | undefined\n}\n\nexport const db = globalThis.prisma || new PrismaClient()\n\nif (process.env.NODE_ENV !== \"production\") globalThis.prisma = db\n\n"
  },
  {
    "path": "lib/formatPrice.ts",
    "content": "export const formatPrice = (price: number) => {\n  return new Intl.NumberFormat(\"en-US\", {\n    style: \"currency\",\n    currency: \"CAD\",\n  }).format(price)\n}"
  },
  {
    "path": "lib/stripe.ts",
    "content": "import Stripe from 'stripe';\n\nexport const stripe = new Stripe(process.env.STRIPE_API_KEY!, {\n  apiVersion: \"2024-04-10\",\n  typescript: true,\n});"
  },
  {
    "path": "lib/uploadthing.ts",
    "content": "import {\n  generateUploadButton,\n  generateUploadDropzone,\n} from \"@uploadthing/react\";\n \nimport type { OurFileRouter } from \"@/app/api/uploadthing/core\";\n \nexport const UploadButton = generateUploadButton<OurFileRouter>();\nexport const UploadDropzone = generateUploadDropzone<OurFileRouter>();"
  },
  {
    "path": "lib/utils.ts",
    "content": "import { type ClassValue, clsx } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n\nexport function cn(...inputs: ClassValue[]) {\n  return twMerge(clsx(inputs))\n}\n"
  },
  {
    "path": "middleware.ts",
    "content": "import { clerkMiddleware } from \"@clerk/nextjs/server\";\n\nexport default clerkMiddleware();\n\nexport const config = {\n  matcher: [\"/((?!.*\\\\..*|_next).*)\", \"/\", \"/(api|trpc)(.*)\"],\n};"
  },
  {
    "path": "next.config.mjs",
    "content": "/** @type {import('next').NextConfig} */\nconst nextConfig = {\n  images: {\n    remotePatterns: [\n      { hostname: 'utfs.io'},\n      { hostname: 'img.clerk.com'}\n    ]\n  }\n};\n\nexport default nextConfig;\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"academy\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"scripts\": {\n    \"dev\": \"next dev\",\n    \"build\": \"next build\",\n    \"start\": \"next start\",\n    \"lint\": \"next lint\",\n    \"postinstall\": \"prisma generate\"\n  },\n  \"dependencies\": {\n    \"@clerk/nextjs\": \"^5.1.3\",\n    \"@hello-pangea/dnd\": \"^16.6.0\",\n    \"@hookform/resolvers\": \"^3.4.2\",\n    \"@mux/mux-node\": \"^8.7.1\",\n    \"@mux/mux-player-react\": \"^2.7.0\",\n    \"@prisma/client\": \"^5.14.0\",\n    \"@radix-ui/react-alert-dialog\": \"^1.0.5\",\n    \"@radix-ui/react-dialog\": \"^1.0.5\",\n    \"@radix-ui/react-label\": \"^2.0.2\",\n    \"@radix-ui/react-popover\": \"^1.0.7\",\n    \"@radix-ui/react-progress\": \"^1.0.3\",\n    \"@radix-ui/react-slot\": \"^1.0.2\",\n    \"@radix-ui/react-switch\": \"^1.0.3\",\n    \"@tanstack/react-table\": \"^8.17.3\",\n    \"@uploadthing/react\": \"^6.6.0\",\n    \"axios\": \"^1.7.2\",\n    \"class-variance-authority\": \"^0.7.0\",\n    \"clsx\": \"^2.1.1\",\n    \"cmdk\": \"^0.2.1\",\n    \"lucide-react\": \"^0.379.0\",\n    \"next\": \"14.2.3\",\n    \"react\": \"^18\",\n    \"react-dom\": \"^18\",\n    \"react-hook-form\": \"^7.51.5\",\n    \"react-hook-toast\": \"^0.0.3\",\n    \"react-hot-toast\": \"^2.4.1\",\n    \"react-quill\": \"^2.0.0\",\n    \"recharts\": \"^2.12.7\",\n    \"stripe\": \"^15.9.0\",\n    \"tailwind-merge\": \"^2.3.0\",\n    \"tailwindcss-animate\": \"^1.0.7\",\n    \"uploadthing\": \"^6.12.0\",\n    \"zod\": \"^3.23.8\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^20\",\n    \"@types/react\": \"^18\",\n    \"@types/react-dom\": \"^18\",\n    \"eslint\": \"^8\",\n    \"eslint-config-next\": \"14.2.3\",\n    \"postcss\": \"^8\",\n    \"prisma\": \"^5.14.0\",\n    \"tailwindcss\": \"^3.4.1\",\n    \"typescript\": \"^5\"\n  }\n}\n"
  },
  {
    "path": "postcss.config.mjs",
    "content": "/** @type {import('postcss-load-config').Config} */\nconst config = {\n  plugins: {\n    tailwindcss: {},\n  },\n};\n\nexport default config;\n"
  },
  {
    "path": "prisma/schema.prisma",
    "content": "// This is your Prisma schema file,\n// learn more about it in the docs: https://pris.ly/d/prisma-schema\n\n// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?\n// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init\n\ngenerator client {\n  provider = \"prisma-client-js\"\n}\n\ndatasource db {\n  provider     = \"mysql\"\n  url          = env(\"DATABASE_URL\")\n  relationMode = \"prisma\"\n}\n\nmodel Course {\n  id           String  @id @default(uuid())\n  instructorId String\n  title        String  @db.Text\n  subtitle     String? @db.Text\n  description  String? @db.Text\n  imageUrl     String? @db.Text\n  price        Float?\n  isPublished  Boolean @default(false)\n\n  categoryId String\n  category   Category @relation(fields: [categoryId], references: [id])\n\n  subCategoryId String\n  subCategory   SubCategory @relation(fields: [subCategoryId], references: [id])\n\n  levelId String?\n  level   Level?  @relation(fields: [levelId], references: [id])\n\n  sections Section[]\n\n  purchases Purchase[]\n\n  createdAt DateTime @default(now())\n  updatedAt DateTime @default(now())\n\n  @@index([categoryId])\n  @@index([subCategoryId])\n  @@index([levelId])\n}\n\nmodel Category {\n  id            String        @id @default(uuid())\n  name          String        @unique\n  subCategories SubCategory[]\n  courses       Course[]\n}\n\nmodel SubCategory {\n  id   String @id @default(uuid())\n  name String\n\n  categoryId String\n  category   Category @relation(fields: [categoryId], references: [id])\n\n  courses Course[]\n\n  @@index([categoryId])\n}\n\nmodel Level {\n  id      String   @id @default(uuid())\n  name    String   @unique\n  courses Course[]\n}\n\nmodel Section {\n  id          String  @id @default(uuid())\n  title       String\n  description String? @db.Text\n  videoUrl    String? @db.Text\n  position    Int\n  isPublished Boolean @default(false)\n  isFree      Boolean @default(false)\n\n  courseId String\n  course   Course @relation(fields: [courseId], references: [id], onDelete: Cascade)\n\n  muxData MuxData?\n\n  resources Resource[]\n\n  progress Progress[]\n\n  createdAt DateTime @default(now())\n  updatedAt DateTime @default(now())\n\n  @@index([courseId])\n}\n\nmodel MuxData {\n  id         String  @id @default(uuid())\n  assetId    String\n  playbackId String?\n\n  sectionId String  @unique\n  section   Section @relation(fields: [sectionId], references: [id], onDelete: Cascade)\n\n  @@index([sectionId])\n}\n\nmodel Resource {\n  id      String @id @default(uuid())\n  name    String\n  fileUrl String\n\n  sectionId String\n  section   Section @relation(fields: [sectionId], references: [id], onDelete: Cascade)\n\n  createdAt DateTime @default(now())\n  updatedAt DateTime @default(now())\n\n  @@index([sectionId])\n}\n\nmodel Progress {\n  id        String @id @default(uuid())\n  studentId String \n\n  sectionId String\n  section   Section @relation(fields: [sectionId], references: [id], onDelete: Cascade)\n\n  isCompleted Boolean @default(false)\n\n  createdAt DateTime @default(now())\n  updatedAt DateTime @default(now())\n\n  @@index([sectionId])\n  @@unique([studentId, sectionId])\n}\n\nmodel Purchase {\n  id         String @id @default(uuid())\n  customerId String \n\n  courseId String\n  course   Course @relation(fields: [courseId], references: [id], onDelete: Cascade)\n\n  createdAt DateTime @default(now())\n  updatedAt DateTime @default(now())\n\n  @@index([courseId])\n  @@unique([customerId, courseId])\n}\n\nmodel StripeCustomer {\n  id               String @id @default(uuid())\n  customerId       String @unique\n  stripeCustomerId String @unique\n\n  createdAt DateTime @default(now())\n  updatedAt DateTime @default(now())\n}\n"
  },
  {
    "path": "scripts/seed.ts",
    "content": "const { PrismaClient } = require(\"@prisma/client\");\nconst database = new PrismaClient();\n\nasync function main() {\n  try {\n    const categories = [\n      {\n        name: \"IT & Software\",\n        subCategories: {\n          create: [\n            { name: \"Web Development\" },\n            { name: \"Data Science\" },\n            { name: \"Cybersecurity\" },\n            { name: \"Others\" },\n          ],\n        },\n      },\n      {\n        name: \"Business\",\n        subCategories: {\n          create: [\n            { name: \"E-Commerce\" },\n            { name: \"Marketing\" },\n            { name: \"Finance\" },\n            { name: \"Others\" },\n          ],\n        },\n      },\n      {\n        name: \"Design\",\n        subCategories: {\n          create: [\n            { name: \"Graphic Design\" },\n            { name: \"3D & Animation\" },\n            { name: \"Interior Design\" },\n            { name: \"Others\" },\n          ],\n        },\n      },\n      {\n        name: \"Health\",\n        subCategories: {\n          create: [\n            { name: \"Fitness\" },\n            { name: \"Yoga\" },\n            { name: \"Nutrition\" },\n            { name: \"Others\" },\n          ],\n        },\n      },\n    ];\n\n    // Sequentially create each category with its subcategories\n    for (const category of categories) {\n      await database.category.create({\n        data: {\n          name: category.name,\n          subCategories: category.subCategories,\n        },\n        include: {\n          subCategories: true,\n        },\n      });\n    }\n\n    await database.level.createMany({\n      data: [\n        { name: \"Beginner\" },\n        { name: \"Intermediate\" },\n        { name: \"Expert\" },\n        { name: \"All levels\" },\n      ],\n    });\n\n    console.log(\"Seeding successfully\");\n  } catch (error) {\n    console.log(\"Seeding failed\", error);\n  } finally {\n    await database.$disconnect();\n  }\n}\n\nmain();\n"
  },
  {
    "path": "tailwind.config.ts",
    "content": "import type { Config } from \"tailwindcss\"\n\nconst config = {\n  darkMode: [\"class\"],\n  content: [\n    './pages/**/*.{ts,tsx}',\n    './components/**/*.{ts,tsx}',\n    './app/**/*.{ts,tsx}',\n    './src/**/*.{ts,tsx}',\n\t],\n  prefix: \"\",\n  theme: {\n    container: {\n      center: true,\n      padding: \"2rem\",\n      screens: {\n        \"2xl\": \"1400px\",\n      },\n    },\n    extend: {\n      colors: {\n        border: \"hsl(var(--border))\",\n        input: \"hsl(var(--input))\",\n        ring: \"hsl(var(--ring))\",\n        background: \"hsl(var(--background))\",\n        foreground: \"hsl(var(--foreground))\",\n        primary: {\n          DEFAULT: \"hsl(var(--primary))\",\n          foreground: \"hsl(var(--primary-foreground))\",\n        },\n        secondary: {\n          DEFAULT: \"hsl(var(--secondary))\",\n          foreground: \"hsl(var(--secondary-foreground))\",\n        },\n        destructive: {\n          DEFAULT: \"hsl(var(--destructive))\",\n          foreground: \"hsl(var(--destructive-foreground))\",\n        },\n        muted: {\n          DEFAULT: \"hsl(var(--muted))\",\n          foreground: \"hsl(var(--muted-foreground))\",\n        },\n        accent: {\n          DEFAULT: \"hsl(var(--accent))\",\n          foreground: \"hsl(var(--accent-foreground))\",\n        },\n        popover: {\n          DEFAULT: \"hsl(var(--popover))\",\n          foreground: \"hsl(var(--popover-foreground))\",\n        },\n        card: {\n          DEFAULT: \"hsl(var(--card))\",\n          foreground: \"hsl(var(--card-foreground))\",\n        },\n      },\n      borderRadius: {\n        lg: \"var(--radius)\",\n        md: \"calc(var(--radius) - 2px)\",\n        sm: \"calc(var(--radius) - 4px)\",\n      },\n      keyframes: {\n        \"accordion-down\": {\n          from: { height: \"0\" },\n          to: { height: \"var(--radix-accordion-content-height)\" },\n        },\n        \"accordion-up\": {\n          from: { height: \"var(--radix-accordion-content-height)\" },\n          to: { height: \"0\" },\n        },\n      },\n      animation: {\n        \"accordion-down\": \"accordion-down 0.2s ease-out\",\n        \"accordion-up\": \"accordion-up 0.2s ease-out\",\n      },\n    },\n  },\n  plugins: [require(\"tailwindcss-animate\")],\n} satisfies Config\n\nexport default config"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n    \"allowJs\": true,\n    \"skipLibCheck\": true,\n    \"strict\": true,\n    \"noEmit\": true,\n    \"esModuleInterop\": true,\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"bundler\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"jsx\": \"preserve\",\n    \"incremental\": true,\n    \"plugins\": [\n      {\n        \"name\": \"next\"\n      }\n    ],\n    \"paths\": {\n      \"@/*\": [\"./*\"]\n    }\n  },\n  \"include\": [\"next-env.d.ts\", \"**/*.ts\", \"**/*.tsx\", \".next/types/**/*.ts\"],\n  \"exclude\": [\"node_modules\"]\n}\n"
  }
]