diff --git a/.env b/.env index a9650f6..ae77092 100644 --- a/.env +++ b/.env @@ -17,4 +17,13 @@ NEXTAUTH_SECRET=123l;k12j3lk12j3kl12j3;l12kj3;l12j3 #STRIPE STRIPE_PUBLISH_KEY=pk_test_51MUo50EhwvoKb8N0k2Q2Adcuxd7ncoDj7Va1qsWmcxbXjuJ1U6xyqHj5xLRkeuedfNNAi08bUNR4n08W0mNvclxs00j1zLJXRN STRIPE_SECRET_KEY=sk_test_51MUo50EhwvoKb8N0BQGlnofyht55sT6HNfjoZf2ZTcRt0K7PPoJNGMdmALVD8zIzaOzbT7pbVpw5SLk7bGXfkyWg00WDeAeJPe -STRIPE_CREATE_INSTRUCTOR_REDIRECT_URL="http://localhost:3000/instructor/create-callback" \ No newline at end of file +STRIPE_CREATE_INSTRUCTOR_REDIRECT_URL="http://localhost:3000/instructor/create-callback" + + + +#AWS +AWS_SECRET_KEY=UuXHG9zp8Fz6tb8d0bPPG/+ormGh7M0Adh9ENJ5Y +AWS_ACCESS_KEY=AKIAR5X24KRRRJENNTLD +AWS_ARN=arn:aws:iam::132596388963:user/salleh_worker +AWS_LOGIN_PASSWORD=asdzxcASDZXC123123@ +AWS_BUCKET_NAME=edumy \ No newline at end of file diff --git a/data/linksData.ts b/data/linksData.ts new file mode 100644 index 0000000..d60b5fd --- /dev/null +++ b/data/linksData.ts @@ -0,0 +1,16 @@ +interface ILinks { + text: string, + link: string, +} + + +export const instructorLinksData = [ + { + link: "/instructor/course/create", + text: "Create Course" + }, + { + link: "/instructor", + text: "Dashboard" + } +] as ILinks[] \ No newline at end of file diff --git a/layouts/InstructorLayout/InstructorLayout.tsx b/layouts/InstructorLayout/InstructorLayout.tsx new file mode 100644 index 0000000..96f2881 --- /dev/null +++ b/layouts/InstructorLayout/InstructorLayout.tsx @@ -0,0 +1,28 @@ +import { instructorLinksData } from '@/data/linksData' +import { useRouter } from 'next/router' +import React from 'react' +interface InstructorLayoutProps { + children: React.ReactElement +} + + +export const InstructorLayout = ({ children }: InstructorLayoutProps) => { + const router = useRouter() + return ( +
+ { + instructorLinksData.map((link, i) => ( + router.push(link.link)} + > + {link.text} + + )) + } + {children} +
+ ) +} + + diff --git a/layouts/InstructorLayout/index.ts b/layouts/InstructorLayout/index.ts new file mode 100644 index 0000000..51a99ee --- /dev/null +++ b/layouts/InstructorLayout/index.ts @@ -0,0 +1 @@ +export * from './InstructorLayout' \ No newline at end of file diff --git a/layouts/UserPages/UserPages.tsx b/layouts/UserPages/UserPages.tsx index 690ecae..ea33057 100644 --- a/layouts/UserPages/UserPages.tsx +++ b/layouts/UserPages/UserPages.tsx @@ -1,13 +1,24 @@ import { makeInstructorApi } from '@/services/api/instructor' import { Button } from '@/ui' import { handleFrontEndResponse } from '@/utils/apiresponses' -import React from 'react' - +import clsx from 'clsx' +import { useSession } from 'next-auth/react' +import Link from 'next/link' +import React, { useState } from 'react' +import { AiOutlineUser } from 'react-icons/ai' +import { FiSettings } from 'react-icons/fi' interface UserPagesProps { children: React.ReactElement } +const instructorRoutes = [{ + title: "Create Courses", + to: "/instructor" +}] as { title: string, to: string }[] + const UserPagesLayout = ({ children }: UserPagesProps) => { + const [showMenu, setshowMenu] = useState(false) + const { data: session } = useSession() const handleBecomeInstructor = async () => { try { const data = await makeInstructorApi() @@ -18,12 +29,53 @@ const UserPagesLayout = ({ children }: UserPagesProps) => { } } return ( -
-
-
+
+ { + session?.user && +
+ + { + session?.user?.name && +

{session?.user?.name}

+ } +
+ } + { + session?.user ? +
{children} - + ) } diff --git a/layouts/index.ts b/layouts/index.ts index 0acb712..f94aa5a 100644 --- a/layouts/index.ts +++ b/layouts/index.ts @@ -1 +1,2 @@ -export * from './UserPages' \ No newline at end of file +export * from './UserPages' +export * from './InstructorLayout' \ No newline at end of file diff --git a/lib/auth.ts b/lib/auth.ts index 7aee111..2663141 100644 --- a/lib/auth.ts +++ b/lib/auth.ts @@ -42,8 +42,18 @@ export const authOptions: AuthOptions = { }, secret: process.env.NEXTAUTH_SECRET as string, callbacks: { - jwt: ({ token, user }) => { - if (user) { + jwt: async ({ token, user }) => { + const dbUser = await db.user.findFirst({ + where: { + email: token.email + } + }) + if (dbUser) { + token.id = dbUser.id + token.name = dbUser.name + token.email = dbUser.email + token.roles = dbUser.roles + } else { token.id = user.id token.name = user.name token.email = user.email diff --git a/lib/aws.ts b/lib/aws.ts new file mode 100644 index 0000000..1b37a55 --- /dev/null +++ b/lib/aws.ts @@ -0,0 +1,12 @@ +import AWS from 'aws-sdk' + +AWS.config.update({ + apiVersion: "us-east-1", + credentials: { + accessKeyId: process.env.AWS_ACCESS_KEY as string, + secretAccessKey: process.env.AWS_SECRET_KEY as string + } +}) + + +export const S3 = new AWS.S3() diff --git a/middleware.ts b/middleware.ts index d9b2e93..c7efa2a 100644 --- a/middleware.ts +++ b/middleware.ts @@ -6,7 +6,6 @@ export default withAuth( async function middleware(req) { const token = await getToken({ req }) const isAuth = !!token - console.log("isauth===>",isAuth) const isAuthPage = req.nextUrl.pathname.startsWith("/auth/login") || req.nextUrl.pathname.startsWith("/auth/register") @@ -18,16 +17,26 @@ export default withAuth( return null } + if (!isAuth) { let from = req.nextUrl.pathname; if (req.nextUrl.search) { from += req.nextUrl.search; } - return NextResponse.redirect( new URL(`/auth/login?from=${encodeURIComponent(from)}`, req.url) ); } + + const isInstructorPage = + req.nextUrl.pathname.startsWith("/instructor") + + if (isInstructorPage) { + if (isAuth && !token.roles.includes("instructor")) { + return NextResponse.redirect(new URL("/", req.url)) + } + return null + } }, { callbacks: { @@ -35,6 +44,7 @@ export default withAuth( // This is a work-around for handling redirect on auth pages. // We return true here so that the middleware function above // is always called. + return true }, }, @@ -42,5 +52,5 @@ export default withAuth( ) export const config = { - matcher: ["/:path*", "/auth/login", "/auth/register"], + matcher: ["/", "/auth/login", "/auth/register", "/instructor"], } \ No newline at end of file diff --git a/molecules/CourseLessonsList/CourseLessonsList.tsx b/molecules/CourseLessonsList/CourseLessonsList.tsx new file mode 100644 index 0000000..d76007c --- /dev/null +++ b/molecules/CourseLessonsList/CourseLessonsList.tsx @@ -0,0 +1,27 @@ +import { Lesson } from '@prisma/client' +import React from 'react' +import { SingleLessonItem } from '../SingleLessonItem' +interface CourseLessonsListProps { + lessons: Lesson[] + onPreviewClicked?: (video: string) => void +} +export const CourseLessonsList = ({ lessons, onPreviewClicked }: CourseLessonsListProps) => { + const onClickItem = (video: string) => { + onPreviewClicked && onPreviewClicked(video) + } + return ( +
+

{lessons.length} Lessons

+ { + lessons.map((l, li) => ( + { + if(l.free_preview){ + onClickItem(l.video) + } + } + } index={li} lesson={l} key={li} /> + )) + } +
+ ) +} \ No newline at end of file diff --git a/molecules/CourseLessonsList/index.ts b/molecules/CourseLessonsList/index.ts new file mode 100644 index 0000000..2973850 --- /dev/null +++ b/molecules/CourseLessonsList/index.ts @@ -0,0 +1 @@ +export * from './CourseLessonsList' \ No newline at end of file diff --git a/molecules/InstructorCourseCard/InstructorCourseCard.tsx b/molecules/InstructorCourseCard/InstructorCourseCard.tsx new file mode 100644 index 0000000..1744a0d --- /dev/null +++ b/molecules/InstructorCourseCard/InstructorCourseCard.tsx @@ -0,0 +1,24 @@ +import React from 'react' +import { Course } from '@prisma/client' +import Image from 'next/image' +import { useRouter } from 'next/router' +interface CourseCardProps extends Course { + +} +export const CourseCard = ({ category, description, id, imagePreview, name, paid, price, userId }: CourseCardProps) => { + const router = useRouter() + return ( +
{ router.push(`/instructor/course/${id}`) }} className='cursor-pointer relative border p-2 shadow-md rounded-[16px] flex items-center gap-x-4'> +
+ +
+
+

{name}

+

{description}

+

type:{paid}

+

price: {price}

+

category:{category}

+
+
+ ) +} \ No newline at end of file diff --git a/molecules/InstructorCourseCard/index.ts b/molecules/InstructorCourseCard/index.ts new file mode 100644 index 0000000..bf05aae --- /dev/null +++ b/molecules/InstructorCourseCard/index.ts @@ -0,0 +1 @@ +export * from './InstructorCourseCard' \ No newline at end of file diff --git a/molecules/SingleCourseJumboTron/SingleCourseJumboTron.tsx b/molecules/SingleCourseJumboTron/SingleCourseJumboTron.tsx new file mode 100644 index 0000000..80379e1 --- /dev/null +++ b/molecules/SingleCourseJumboTron/SingleCourseJumboTron.tsx @@ -0,0 +1,74 @@ +import { Course } from '@prisma/client' +import Image from 'next/image' +import React, { useState } from 'react' +import { MdOutlineLanguage, MdSubtitles } from 'react-icons/md' +import ReactPlayer from 'react-player' +interface SingleCourseJumboTronProps { + course: (Course & { + Lesson: { + video: string; + }[]; + user: { + id: string; + name: string | null; + }; + }) + + onClickPreview?: (video: string) => void +} +export const SingleCourseJumboTron = ({ + course: { + category, + description, + id, + imagePreview, + name, + paid, + price, + userId, + user, + Lesson + }, + onClickPreview +}: SingleCourseJumboTronProps) => { + const [allowPlayer, setallowPlayer] = React.useState(false) + React.useEffect(() => { + if (typeof window !== "undefined") { + setallowPlayer(true) + } + }, []) + return ( +
+
+

{name}

+

{description}

+
Created by {user.name}
+
+
+ English +
+
+ English [Auto] +
+
+
+
+ { + (Lesson.length > 0 && allowPlayer) && + < div className='w-[100%] aspect-video cursor-pointer' onClick={() => { + onClickPreview && onClickPreview(Lesson[0].video) + }}> + +
+ }{ + (Lesson.length === 0) && +
+ +
+ } +
+ + ) +} \ No newline at end of file diff --git a/molecules/SingleCourseJumboTron/index.ts b/molecules/SingleCourseJumboTron/index.ts new file mode 100644 index 0000000..c7095bf --- /dev/null +++ b/molecules/SingleCourseJumboTron/index.ts @@ -0,0 +1 @@ +export * from './SingleCourseJumboTron' \ No newline at end of file diff --git a/molecules/SingleLessonItem/SingleLessonItem.tsx b/molecules/SingleLessonItem/SingleLessonItem.tsx new file mode 100644 index 0000000..99f6dbe --- /dev/null +++ b/molecules/SingleLessonItem/SingleLessonItem.tsx @@ -0,0 +1,40 @@ +import { Button } from '@/ui' +import { Lesson } from '@prisma/client' +import clsx from 'clsx' +import React from 'react' +interface SingleLessonItemProps { + lesson: Lesson + onClick?: () => void + index: number +} +export const SingleLessonItem = ({ + lesson: { + courseId, description, free_preview, id, name, video + }, + onClick, + index +}: SingleLessonItemProps) => { + return ( +
+
+
+ {index + 1} + +
+

{name}

+
+ { + free_preview ? + Preview + : + null + } +
+ ) +} \ No newline at end of file diff --git a/molecules/SingleLessonItem/index.ts b/molecules/SingleLessonItem/index.ts new file mode 100644 index 0000000..3c09f6d --- /dev/null +++ b/molecules/SingleLessonItem/index.ts @@ -0,0 +1 @@ +export * from './SingleLessonItem' \ No newline at end of file diff --git a/molecules/UserCourseCard/UserCourseCard.tsx b/molecules/UserCourseCard/UserCourseCard.tsx new file mode 100644 index 0000000..07133df --- /dev/null +++ b/molecules/UserCourseCard/UserCourseCard.tsx @@ -0,0 +1,29 @@ +import React from 'react' +import { Course } from '@prisma/client' +import Image from 'next/image' +import { currencyFormaterClient } from '@/utils/currencyFormaterClient' +import { Badge } from '@/ui' +import Link from 'next/link' +import { useRouter } from 'next/router' +interface UserCourseCardProps extends Course { + +} +export const UserCourseCard = ({ + category, description, id, imagePreview, name, paid, price, userId +}: UserCourseCardProps) => { + const router = useRouter() + return ( +
{ router.push(`/course/${id}`) }} className='border shadow-md cursor-pointer'> +
+ +
+
+

{name}

+

Price: {currencyFormaterClient({ amount: price, currency: "usd" })}

+

{description}

+ +
+
+ ) +} + diff --git a/molecules/UserCourseCard/index.ts b/molecules/UserCourseCard/index.ts new file mode 100644 index 0000000..e7e86cc --- /dev/null +++ b/molecules/UserCourseCard/index.ts @@ -0,0 +1 @@ +export * from './UserCourseCard' \ No newline at end of file diff --git a/molecules/index.ts b/molecules/index.ts new file mode 100644 index 0000000..ea88df0 --- /dev/null +++ b/molecules/index.ts @@ -0,0 +1,5 @@ +export * from './InstructorCourseCard' +export * from './UserCourseCard' +export * from './SingleCourseJumboTron' +export * from './SingleLessonItem' +export * from './CourseLessonsList' \ No newline at end of file diff --git a/next.config.js b/next.config.js index a843cbe..bcd38cd 100644 --- a/next.config.js +++ b/next.config.js @@ -1,6 +1,9 @@ /** @type {import('next').NextConfig} */ const nextConfig = { - reactStrictMode: true, + reactStrictMode: false, + images: { + domains: ['edumy.s3.amazonaws.com'], + }, } module.exports = nextConfig diff --git a/package-lock.json b/package-lock.json index 6b54fe3..df06d06 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,35 +1,51 @@ { - "name": "marketplace", + "name": "edumy", "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "marketplace", + "name": "edumy", "version": "0.1.0", "dependencies": { "@next-auth/prisma-adapter": "^0.4.4-canary.81", "@prisma/client": "^4.12.0", + "@radix-ui/colors": "^0.1.8", + "@radix-ui/react-dialog": "^1.0.3", + "@tailwindcss/line-clamp": "^0.4.4", "@types/node": "18.15.11", "@types/react": "18.0.35", "@types/react-dom": "18.0.11", "autoprefixer": "10.4.14", + "aws-sdk": "^2.1365.0", "axios": "^1.3.6", + "clsx": "^1.2.1", "eslint": "8.38.0", "eslint-config-next": "13.3.0", + "express-formidable": "^1.2.0", "formik": "^2.2.9", + "multer": "^1.4.5-lts.1", "next": "13.3.0", "next-auth": "^4.22.0", + "next-connect": "^0.13.0", "postcss": "8.4.21", "query-string": "^8.1.0", "react": "18.2.0", "react-dom": "18.2.0", + "react-icons": "^4.8.0", + "react-player": "^2.12.0", + "react-query": "^3.39.3", "stripe": "^12.2.0", "tailwindcss": "3.3.1", "typescript": "5.0.4", + "uuid": "^9.0.0", "yup": "^1.1.1" }, "devDependencies": { + "@types/express-formidable": "^1.2.0", + "@types/formidable": "^2.0.5", + "@types/multer": "^1.4.7", + "@types/uuid": "^9.0.1", "prisma": "^4.12.0" } }, @@ -422,6 +438,220 @@ "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-4.12.0-67.659ef412370fa3b41cd7bf6e94587c1dfb7f67e7.tgz", "integrity": "sha512-JIHNj5jlXb9mcaJwakM0vpgRYJIAurxTUqM0iX0tfEQA5XLZ9ONkIckkhuAKdAzocZ+80GYg7QSsfpjg7OxbOA==" }, + "node_modules/@radix-ui/colors": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/@radix-ui/colors/-/colors-0.1.8.tgz", + "integrity": "sha512-jwRMXYwC0hUo0mv6wGpuw254Pd9p/R6Td5xsRpOmaWkUHlooNWqVcadgyzlRumMq3xfOTXwJReU0Jv+EIy4Jbw==" + }, + "node_modules/@radix-ui/primitive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.0.tgz", + "integrity": "sha512-3e7rn8FDMin4CgeL7Z/49smCA3rFYY3Ha2rUQ7HRWFadS5iCRw08ZgVT1LaNTCNqgvrUiyczLflrVrF0SRQtNA==", + "dependencies": { + "@babel/runtime": "^7.13.10" + } + }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.0.tgz", + "integrity": "sha512-0KaSv6sx787/hK3eF53iOkiSLwAGlFMx5lotrqD2pTjB18KbybKoEIgkNZTKC60YECDQTKGTRcDBILwZVqVKvA==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-context": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.0.0.tgz", + "integrity": "sha512-1pVM9RfOQ+n/N5PJK33kRSKsr1glNxomxONs5c49MliinBY6Yw2Q995qfBUUo0/Mbg05B/sGA0gkgPI7kmSHBg==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-dialog": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.0.3.tgz", + "integrity": "sha512-owNhq36kNPqC2/a+zJRioPg6HHnTn5B/sh/NjTY8r4W9g1L5VJlrzZIVcBr7R9Mg8iLjVmh6MGgMlfoVf/WO/A==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.0", + "@radix-ui/react-compose-refs": "1.0.0", + "@radix-ui/react-context": "1.0.0", + "@radix-ui/react-dismissable-layer": "1.0.3", + "@radix-ui/react-focus-guards": "1.0.0", + "@radix-ui/react-focus-scope": "1.0.2", + "@radix-ui/react-id": "1.0.0", + "@radix-ui/react-portal": "1.0.2", + "@radix-ui/react-presence": "1.0.0", + "@radix-ui/react-primitive": "1.0.2", + "@radix-ui/react-slot": "1.0.1", + "@radix-ui/react-use-controllable-state": "1.0.0", + "aria-hidden": "^1.1.1", + "react-remove-scroll": "2.5.5" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.3.tgz", + "integrity": "sha512-nXZOvFjOuHS1ovumntGV7NNoLaEp9JEvTht3MBjP44NSW5hUKj/8OnfN3+8WmB+CEhN44XaGhpHoSsUIEl5P7Q==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.0", + "@radix-ui/react-compose-refs": "1.0.0", + "@radix-ui/react-primitive": "1.0.2", + "@radix-ui/react-use-callback-ref": "1.0.0", + "@radix-ui/react-use-escape-keydown": "1.0.2" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.0.tgz", + "integrity": "sha512-UagjDk4ijOAnGu4WMUPj9ahi7/zJJqNZ9ZAiGPp7waUWJO0O1aWXi/udPphI0IUjvrhBsZJGSN66dR2dsueLWQ==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-focus-scope": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.2.tgz", + "integrity": "sha512-spwXlNTfeIprt+kaEWE/qYuYT3ZAqJiAGjN/JgdvgVDTu8yc+HuX+WOWXrKliKnLnwck0F6JDkqIERncnih+4A==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.0", + "@radix-ui/react-primitive": "1.0.2", + "@radix-ui/react-use-callback-ref": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-id": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.0.tgz", + "integrity": "sha512-Q6iAB/U7Tq3NTolBBQbHTgclPmGWE3OlktGGqrClPozSw4vkQ1DfQAOtzgRPecKsMdJINE05iaoDUG8tRzCBjw==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-layout-effect": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-portal": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.2.tgz", + "integrity": "sha512-swu32idoCW7KA2VEiUZGBSu9nB6qwGdV6k6HYhUoOo3M1FFpD+VgLzUqtt3mwL1ssz7r2x8MggpLSQach2Xy/Q==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.2" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-presence": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.0.0.tgz", + "integrity": "sha512-A+6XEvN01NfVWiKu38ybawfHsBjWum42MRPnEuqPsBZ4eV7e/7K321B5VgYMPv3Xx5An6o1/l9ZuDBgmcmWK3w==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.0", + "@radix-ui/react-use-layout-effect": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.2.tgz", + "integrity": "sha512-zY6G5Qq4R8diFPNwtyoLRZBxzu1Z+SXMlfYpChN7Dv8gvmx9X3qhDqiLWvKseKVJMuedFeU/Sa0Sy/Ia+t06Dw==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-slot": "1.0.1" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-slot": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.1.tgz", + "integrity": "sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.0.tgz", + "integrity": "sha512-GZtyzoHz95Rhs6S63D2t/eqvdFCm7I+yHMLVQheKM7nBD8mbZIt+ct1jz4536MDnaOGKIxynJ8eHTkVGVVkoTg==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.0.tgz", + "integrity": "sha512-FohDoZvk3mEXh9AWAVyRTYR4Sq7/gavuofglmiXB2g1aKyboUD4YtgWxKj8O5n+Uak52gXQ4wKz5IFST4vtJHg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-callback-ref": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.2.tgz", + "integrity": "sha512-DXGim3x74WgUv+iMNCF+cAo8xUHHeqvjx8zs7trKf+FkQKPQXLk2sX7Gx1ysH7Q76xCpZuxIJE7HLPxRE+Q+GA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-callback-ref": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.0.tgz", + "integrity": "sha512-6Tpkq+R6LOlmQb1R5NNETLG0B4YP0wc+klfXafpUCj6JGyaUc8il7/kUZ7m59rGbXGczE9Bs+iz2qloqsZBduQ==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, "node_modules/@rushstack/eslint-patch": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz", @@ -435,11 +665,96 @@ "tslib": "^2.4.0" } }, + "node_modules/@tailwindcss/line-clamp": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/line-clamp/-/line-clamp-0.4.4.tgz", + "integrity": "sha512-5U6SY5z8N42VtrCrKlsTAA35gy2VSyYtHWCsg1H87NU1SXnEfekTVlrga9fzUDrrHcGi2Lb5KenUWb4lRQT5/g==", + "peerDependencies": { + "tailwindcss": ">=2.0.0 || >=3.0.0 || >=3.0.0-alpha.1" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "4.17.17", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz", + "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==", + "dev": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-formidable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/express-formidable/-/express-formidable-1.2.0.tgz", + "integrity": "sha512-nmqjkWllce71jVB0dWilJHyv676GmYESDtY12gcxgUwwl6J55FaOEAfn9bcShnydxj4f+AWIN1gEF4ItXyubUA==", + "dev": true, + "dependencies": { + "@types/express": "*", + "@types/formidable": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.17.34", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.34.tgz", + "integrity": "sha512-fvr49XlCGoUj2Pp730AItckfjat4WNb0lb3kfrLWffd+RLeoGAMsq7UOy04PAPtoL01uKwcp6u8nhzpgpDYr3w==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/formidable": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/formidable/-/formidable-2.0.5.tgz", + "integrity": "sha512-uvMcdn/KK3maPOaVUAc3HEYbCEhjaGFwww4EsX6IJfWIJ1tzHtDHczuImH3GKdusPnAAmzB07St90uabZeCKPA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" }, + "node_modules/@types/mime": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", + "dev": true + }, + "node_modules/@types/multer": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.7.tgz", + "integrity": "sha512-/SNsDidUFCvqqcWDwxv2feww/yqhNeTRL5CVoL3jU4Goc4kKEL10T7Eye65ZqPNi4HRx8sAEX59pV1aEH7drNA==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, "node_modules/@types/node": { "version": "18.15.11", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz", @@ -450,6 +765,18 @@ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" }, + "node_modules/@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", + "dev": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", + "dev": true + }, "node_modules/@types/react": { "version": "18.0.35", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.35.tgz", @@ -473,6 +800,32 @@ "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==" }, + "node_modules/@types/send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.1.tgz", + "integrity": "sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==", + "dev": true, + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.1.tgz", + "integrity": "sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==", + "dev": true, + "dependencies": { + "@types/mime": "*", + "@types/node": "*" + } + }, + "node_modules/@types/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-rFT3ak0/2trgvp4yYZo5iKFEPsET7vKydKF+VRCxlQ9bpheehyAJH89dAkaLEq/j/RZXJIqcgsmPJKUP1Z28HA==", + "dev": true + }, "node_modules/@typescript-eslint/parser": { "version": "5.58.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.58.0.tgz", @@ -642,6 +995,11 @@ "node": ">= 8" } }, + "node_modules/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==" + }, "node_modules/arg": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", @@ -652,6 +1010,17 @@ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, + "node_modules/aria-hidden": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.3.tgz", + "integrity": "sha512-xcLxITLe2HYa1cnYnwCjkOO1PqUHQpozB8x9AR0OgWN2woOBi5kSDVxKfd0b7sb1hw5qFeJhXm9H1nu3xSfLeQ==", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/aria-query": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", @@ -797,6 +1166,34 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/aws-sdk": { + "version": "2.1365.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1365.0.tgz", + "integrity": "sha512-GRwHfzYufi7BhBtgyzeHvqS5yCMRC5ZCqmDU/TBMnr8IaH6sabSG2iAhVn1Kkpjv3tDnWHwDr5s8wNMTzJLPmg==", + "dependencies": { + "buffer": "4.9.2", + "events": "1.1.1", + "ieee754": "1.1.13", + "jmespath": "0.16.0", + "querystring": "0.2.0", + "sax": "1.2.1", + "url": "0.10.3", + "util": "^0.12.4", + "uuid": "8.0.0", + "xml2js": "0.5.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/aws-sdk/node_modules/uuid": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", + "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/axe-core": { "version": "4.6.3", "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.6.3.tgz", @@ -828,6 +1225,33 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/big-integer": { + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", + "engines": { + "node": ">=0.6" + } + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -856,6 +1280,21 @@ "node": ">=8" } }, + "node_modules/broadcast-channel": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/broadcast-channel/-/broadcast-channel-3.7.0.tgz", + "integrity": "sha512-cIAKJXAxGJceNZGTZSBzMxzyOn72cVgPnKx4dc6LRjQgbaJUQqhy5rzL3zbMxkMWsGKkv2hSFkPRMEXfoMZ2Mg==", + "dependencies": { + "@babel/runtime": "^7.7.2", + "detect-node": "^2.1.0", + "js-sha3": "0.8.0", + "microseconds": "0.2.0", + "nano-time": "1.0.0", + "oblivious-set": "1.0.0", + "rimraf": "3.0.2", + "unload": "2.2.0" + } + }, "node_modules/browserslist": { "version": "4.21.5", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", @@ -883,6 +1322,26 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/buffer/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, "node_modules/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -998,6 +1457,14 @@ "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" }, + "node_modules/clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "engines": { + "node": ">=6" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1038,6 +1505,20 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "engines": [ + "node >= 0.8" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, "node_modules/cookie": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", @@ -1046,6 +1527,11 @@ "node": ">= 0.6" } }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -1175,6 +1661,16 @@ "node": ">=0.4.0" } }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==" + }, + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==" + }, "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", @@ -1774,6 +2270,25 @@ "node": ">=0.10.0" } }, + "node_modules/events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/express-formidable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/express-formidable/-/express-formidable-1.2.0.tgz", + "integrity": "sha512-w1vXjF3gb50UKTNkFaW8/4rqY4dUrKfZ1sAZzwAF9YxCAgj/29QZsycf71di0GkskrZOAkubk9pvGYfxyAMYiw==", + "dependencies": { + "formidable": "^1.0.17" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -1928,6 +2443,15 @@ "node": ">= 6" } }, + "node_modules/formidable": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.6.tgz", + "integrity": "sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ==", + "deprecated": "Please upgrade to latest, formidable@v2 or formidable@v3! Check these notes: https://bit.ly/2ZEqIau", + "funding": { + "url": "https://ko-fi.com/tunnckoCore/commissions" + } + }, "node_modules/formik": { "version": "2.2.9", "resolved": "https://registry.npmjs.org/formik/-/formik-2.2.9.tgz", @@ -2029,6 +2553,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "engines": { + "node": ">=6" + } + }, "node_modules/get-symbol-description": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", @@ -2242,6 +2774,11 @@ "react-is": "^16.7.0" } }, + "node_modules/ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + }, "node_modules/ignore": { "version": "5.2.4", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", @@ -2300,6 +2837,14 @@ "node": ">= 0.4" } }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/is-arguments": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", @@ -2423,6 +2968,20 @@ "node": ">=0.10.0" } }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -2623,6 +3182,14 @@ "jiti": "bin/jiti.js" } }, + "node_modules/jmespath": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", + "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/jose": { "version": "4.14.0", "resolved": "https://registry.npmjs.org/jose/-/jose-4.14.0.tgz", @@ -2640,6 +3207,11 @@ "url": "https://opencollective.com/js-sdsl" } }, + "node_modules/js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -2727,6 +3299,11 @@ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, + "node_modules/load-script": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/load-script/-/load-script-1.0.0.tgz", + "integrity": "sha512-kPEjMFtZvwL9TaZo0uZ2ml+Ye9HUMmPwbYRJ324qF9tqMejwykJ5ggTyvzmrbBeapCAbk98BSbTeovHEEP1uCA==" + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -2778,6 +3355,28 @@ "node": ">=10" } }, + "node_modules/match-sorter": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/match-sorter/-/match-sorter-6.3.1.tgz", + "integrity": "sha512-mxybbo3pPNuA+ZuCUhm5bwNkXrJTbsk5VWbR5wiwz/GC6LIiegBGn2w3O08UG/jdbYLinw51fSQ5xNU1U3MgBw==", + "dependencies": { + "@babel/runtime": "^7.12.5", + "remove-accents": "0.4.2" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memoize-one": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", + "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==" + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -2798,6 +3397,11 @@ "node": ">=8.6" } }, + "node_modules/microseconds": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/microseconds/-/microseconds-0.2.0.tgz", + "integrity": "sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA==" + }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -2836,11 +3440,39 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "node_modules/multer": { + "version": "1.4.5-lts.1", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.1.tgz", + "integrity": "sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^1.0.0", + "concat-stream": "^1.5.2", + "mkdirp": "^0.5.4", + "object-assign": "^4.1.1", + "type-is": "^1.6.4", + "xtend": "^4.0.0" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/mz": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", @@ -2851,6 +3483,14 @@ "thenify-all": "^1.0.0" } }, + "node_modules/nano-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/nano-time/-/nano-time-1.0.0.tgz", + "integrity": "sha512-flnngywOoQ0lLQOTRNexn2gGSNuM9bKj9RZAWSzhQ+UJYaAFG9bac4DW9VHjUAzrOaIcajHybCTHe/bkvozQqA==", + "dependencies": { + "big-integer": "^1.6.16" + } + }, "node_modules/nanoid": { "version": "3.3.6", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", @@ -2952,6 +3592,22 @@ } } }, + "node_modules/next-auth/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/next-connect": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/next-connect/-/next-connect-0.13.0.tgz", + "integrity": "sha512-f2G4edY01XomjCECSrgOpb/zzQinJO6Whd8Zds0+rLUYhj5cLwkh6FVvZsQCSSbxSc4k9nCwNuk5NLIhvO1gUA==", + "dependencies": { + "trouter": "^3.2.0" + } + }, "node_modules/next/node_modules/postcss": { "version": "8.4.14", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", @@ -3122,6 +3778,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/oblivious-set": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/oblivious-set/-/oblivious-set-1.0.0.tgz", + "integrity": "sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw==" + }, "node_modules/oidc-token-hash": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.2.tgz", @@ -3442,6 +4103,11 @@ "node": ">=14.17" } }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -3500,6 +4166,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", + "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "engines": { + "node": ">=0.4.x" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -3558,11 +4233,139 @@ "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz", "integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==" }, + "node_modules/react-icons": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.8.0.tgz", + "integrity": "sha512-N6+kOLcihDiAnj5Czu637waJqSnwlMNROzVZMhfX68V/9bu9qHaMIJC4UdozWoOk57gahFCNHwVvWzm0MTzRjg==", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/react-player": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/react-player/-/react-player-2.12.0.tgz", + "integrity": "sha512-rymLRz/2GJJD+Wc01S7S+i9pGMFYnNmQibR2gVE3KmHJCBNN8BhPAlOPTGZtn1uKpJ6p4RPLlzPQ1OLreXd8gw==", + "dependencies": { + "deepmerge": "^4.0.0", + "load-script": "^1.0.0", + "memoize-one": "^5.1.1", + "prop-types": "^15.7.2", + "react-fast-compare": "^3.0.1" + }, + "peerDependencies": { + "react": ">=16.6.0" + } + }, + "node_modules/react-player/node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-player/node_modules/react-fast-compare": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.1.tgz", + "integrity": "sha512-xTYf9zFim2pEif/Fw16dBiXpe0hoy5PxcD8+OwBnTtNLfIm3g6WxhKNurY+6OmdH1u6Ta/W/Vl6vjbYP1MFnDg==" + }, + "node_modules/react-query": { + "version": "3.39.3", + "resolved": "https://registry.npmjs.org/react-query/-/react-query-3.39.3.tgz", + "integrity": "sha512-nLfLz7GiohKTJDuT4us4X3h/8unOh+00MLb2yJoGTPjxKs2bc1iDhkNx2bd5MKklXnOD3NrVZ+J2UXujA5In4g==", + "dependencies": { + "@babel/runtime": "^7.5.5", + "broadcast-channel": "^3.4.1", + "match-sorter": "^6.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz", + "integrity": "sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==", + "dependencies": { + "react-remove-scroll-bar": "^2.3.3", + "react-style-singleton": "^2.2.1", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.0", + "use-sidecar": "^1.1.2" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll-bar": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.4.tgz", + "integrity": "sha512-63C4YQBUt0m6ALadE9XV56hV8BgJWDmmTPY758iIJjfQKt2nYwoUrPk0LXRXcB/yIj82T1/Ixfdpdk68LwIB0A==", + "dependencies": { + "react-style-singleton": "^2.2.1", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-style-singleton": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz", + "integrity": "sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==", + "dependencies": { + "get-nonce": "^1.0.0", + "invariant": "^2.2.4", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -3571,6 +4374,25 @@ "pify": "^2.3.0" } }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -3603,6 +4425,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/regexparam": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/regexparam/-/regexparam-1.3.0.tgz", + "integrity": "sha512-6IQpFBv6e5vz1QAqI+V4k8P2e/3gRrqfCJ9FI+O1FLQTO+Uz6RXZEZOPmTJ6hlGj7gkERzY5BRCv09whKP96/g==", + "engines": { + "node": ">=6" + } + }, + "node_modules/remove-accents": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.4.2.tgz", + "integrity": "sha512-7pXIJqJOq5tFgG1A2Zxti3Ht8jJF337m4sowbuHsW30ZnkQFnDzy9qBNhgzX8ZLW4+UBcXiiR7SwR6pokHsxiA==" + }, "node_modules/resolve": { "version": "1.22.3", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.3.tgz", @@ -3672,6 +4507,11 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, "node_modules/safe-regex-test": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", @@ -3685,6 +4525,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/sax": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==" + }, "node_modules/scheduler": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", @@ -3785,6 +4630,14 @@ "node": ">=10.0.0" } }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/string.prototype.matchall": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz", @@ -4122,6 +4975,17 @@ "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==" }, + "node_modules/trouter": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/trouter/-/trouter-3.2.1.tgz", + "integrity": "sha512-oY3CmIiEYOe1YMEzh++I67lrNOUldtCeuLL0vRPydvQLHZpSJ03B5dgDFlpFsiriMq6e//NDjjopjUzXOztHow==", + "dependencies": { + "regexparam": "^1.3.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", @@ -4184,6 +5048,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/typed-array-length": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", @@ -4197,6 +5073,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" + }, "node_modules/typescript": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", @@ -4223,6 +5104,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/unload": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unload/-/unload-2.2.0.tgz", + "integrity": "sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA==", + "dependencies": { + "@babel/runtime": "^7.6.2", + "detect-node": "^2.0.4" + } + }, "node_modules/update-browserslist-db": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", @@ -4256,15 +5146,82 @@ "punycode": "^2.1.0" } }, + "node_modules/url": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", + "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==", + "dependencies": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "node_modules/url/node_modules/punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==" + }, + "node_modules/use-callback-ref": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.0.tgz", + "integrity": "sha512-3FT9PRuRdbB9HfXhEq35u4oZkvpJ5kuYbpqhCfmiZyReuRgpnhDlbr2ZEnnuS0RrJAPn6l23xjFg9kpDM+Ms7w==", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sidecar": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz", + "integrity": "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==", + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.9.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", "bin": { "uuid": "dist/bin/uuid" } @@ -4344,6 +5301,34 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, + "node_modules/xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", diff --git a/package.json b/package.json index 2f0837f..5029d89 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "marketplace", + "name": "edumy", "version": "0.1.0", "private": true, "scripts": { @@ -11,26 +11,42 @@ "dependencies": { "@next-auth/prisma-adapter": "^0.4.4-canary.81", "@prisma/client": "^4.12.0", + "@radix-ui/colors": "^0.1.8", + "@radix-ui/react-dialog": "^1.0.3", + "@tailwindcss/line-clamp": "^0.4.4", "@types/node": "18.15.11", "@types/react": "18.0.35", "@types/react-dom": "18.0.11", "autoprefixer": "10.4.14", + "aws-sdk": "^2.1365.0", "axios": "^1.3.6", + "clsx": "^1.2.1", "eslint": "8.38.0", "eslint-config-next": "13.3.0", + "express-formidable": "^1.2.0", "formik": "^2.2.9", + "multer": "^1.4.5-lts.1", "next": "13.3.0", "next-auth": "^4.22.0", + "next-connect": "^0.13.0", "postcss": "8.4.21", "query-string": "^8.1.0", "react": "18.2.0", "react-dom": "18.2.0", + "react-icons": "^4.8.0", + "react-player": "^2.12.0", + "react-query": "^3.39.3", "stripe": "^12.2.0", "tailwindcss": "3.3.1", "typescript": "5.0.4", + "uuid": "^9.0.0", "yup": "^1.1.1" }, "devDependencies": { + "@types/express-formidable": "^1.2.0", + "@types/formidable": "^2.0.5", + "@types/multer": "^1.4.7", + "@types/uuid": "^9.0.1", "prisma": "^4.12.0" } } diff --git a/pages/_app.tsx b/pages/_app.tsx index 8552747..6fdc91a 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -1,12 +1,14 @@ import '@/styles/globals.css' import { SessionProvider } from 'next-auth/react' - +import { QueryClient, QueryClientProvider } from 'react-query' import type { AppProps } from 'next/app' - +export const queryClient = new QueryClient() export default function App({ Component, pageProps: { session, ...pageProps }, }: AppProps) { return ( - + + + ) } diff --git a/pages/api/auth/[...nextauth].ts b/pages/api/auth/[...nextauth].ts index 954eaea..0952273 100644 --- a/pages/api/auth/[...nextauth].ts +++ b/pages/api/auth/[...nextauth].ts @@ -1,8 +1,4 @@ import NextAuth, { AuthOptions } from "next-auth" -import CredentialsProvider from "next-auth/providers/credentials" -import { PrismaAdapter } from "@next-auth/prisma-adapter" -import { db } from "@/lib/db" -import { Session } from "next-auth" import { authOptions } from "@/lib/auth" diff --git a/pages/api/instructor/createcourse.tsx b/pages/api/instructor/createcourse.tsx new file mode 100644 index 0000000..70c20e4 --- /dev/null +++ b/pages/api/instructor/createcourse.tsx @@ -0,0 +1,91 @@ + +import { NextApiRequest, NextApiResponse, NextConfig } from "next"; +import nc from "next-connect"; +import multer from 'multer' +import { CreateCourseDto } from "@/services/api"; +import { S3 } from "@/lib/aws"; +import { v4 as uuid } from 'uuid' +import { ManagedUpload } from "aws-sdk/clients/s3"; +import { db } from "@/lib/db"; +import { ErrorResponse, SuccessResponse } from "@/utils/apiresponses"; +import { getServerSession } from "next-auth"; +import { authOptions } from "@/lib/auth"; +const storage = multer.memoryStorage(); +const upload = multer({ storage }); +const handler = nc({ + onError: (err, req: NextApiRequest, res: NextApiResponse, next) => { + console.error(err.stack); + res.status(500).end("Something broke!"); + }, + onNoMatch: (req, res) => { + res.status(404).end("Page is not found"); + }, +}) + .use(upload.single("imagePreview")) + + .post(async (req, res) => { + const session = await getServerSession(req, res, authOptions) + if (!session) { + return ErrorResponse({ + error: "kindly login", + res, + status: 400 + }) + } + const { category, description, name, paid, price } = req.body as CreateCourseDto + // @ts-ignore + const imagePreview = req.file + // image/png + const imageType = imagePreview.mimetype as string + let uploadedImage: string = ""; + await new Promise((resolve, reject) => { + S3.upload({ + Bucket: process.env.AWS_BUCKET_NAME as string, + Body: imagePreview.buffer, + Key: `${uuid().toString()}.${imageType.split("/")[1]}`, + ContentType: imageType + }, + (err: Error, data: ManagedUpload.SendData) => { + if (err) { + reject(err) + } + if (data) { + uploadedImage = data.Location + resolve(data) + } + } + ) + }) + if (!uploadedImage) { + return ErrorResponse({ + error: "Image not uploaded successfully.", + res, + status: 400 + }) + } + await db.course.create({ + data: { + category, + description, + imagePreview: uploadedImage, + name, + // @ts-ignore + paid: paid === "true" ? true : false, + price: Number(price), + userId: session.user.id + } + }) + return SuccessResponse({ + res, + data: {}, + msg: "okay" + }) + }) + +export default handler; + +export const config = { + api: { + bodyParser: false, + }, +} \ No newline at end of file diff --git a/pages/api/instructor/get-my-created-courses.ts b/pages/api/instructor/get-my-created-courses.ts new file mode 100644 index 0000000..ca63d36 --- /dev/null +++ b/pages/api/instructor/get-my-created-courses.ts @@ -0,0 +1,36 @@ +import { authOptions } from "@/lib/auth" +import { db } from "@/lib/db" +import { ErrorResponse, SuccessResponse } from "@/utils/apiresponses" +import { NextApiRequest, NextApiResponse } from "next" +import { getServerSession } from "next-auth" + +const handler = async (req: NextApiRequest, res: NextApiResponse) => { + try { + const session = await getServerSession(req, res, authOptions) + console.log("session",session) + if (!session) { + return ErrorResponse({ + error: "UnAuthorized", + res, + status: 400 + }) + } + const courses = await db.course.findMany({ + where: { + userId: session.user.id + } + }) + return SuccessResponse({ + res, + data: courses, + msg: "okay" + }) + } catch (error: any) { + return ErrorResponse({ + error: error.message, + res, + status: 500 + }) + } +} +export default handler \ No newline at end of file diff --git a/pages/api/instructor/lesson/create.ts b/pages/api/instructor/lesson/create.ts new file mode 100644 index 0000000..1c36ee3 --- /dev/null +++ b/pages/api/instructor/lesson/create.ts @@ -0,0 +1,69 @@ +import { NextApiRequest, NextApiResponse } from "next"; +import nc from "next-connect"; +import formidable from 'express-formidable' +import { S3 } from "@/lib/aws"; +import { v4 as uuid } from 'uuid' +import { readFileSync } from 'fs' +import { ManagedUpload } from "aws-sdk/clients/s3"; +import { ErrorResponse, SuccessResponse } from "@/utils/apiresponses"; +import { db } from "@/lib/db"; +import { Lesson } from "@prisma/client"; +const handler = nc({ + onError: (err, req: NextApiRequest, res: NextApiResponse, next) => { + console.error(err.stack); + res.status(500).end("Something broke!"); + }, + onNoMatch: (req, res) => { + res.status(404).end("Page is not found"); + }, +}) + .use(formidable()) + .post(async (req, res) => { + // @ts-ignore + const { description, name, courseId } = req.fields as Pick + // @ts-ignore + const { video, } = req.files + + const data: null | ManagedUpload.SendData = await new Promise((resolve, reject) => { + S3.upload({ + Bucket: process.env.AWS_BUCKET_NAME as string, + Key: `${uuid().toString()}.${video.type.split("/")[1]}`, + Body: readFileSync(video.path), + ACL: "public-read", + ContentType: video.type + }, (err: Error, data: ManagedUpload.SendData) => { + if (err) { + resolve(null) + } + if (data) { + resolve(data) + } + }) + }) + if (!data) { + return ErrorResponse({ + error: "Failed to upload", + res, + status: 400 + }) + } + await db.lesson.create({ + data: { + description, + name, + video: data.Location, + courseId, + } + }) + return SuccessResponse({ + res, + data: {}, + msg: "okay" + }) + }) +export default handler; +export const config = { + api: { + bodyParser: false, + }, +} \ No newline at end of file diff --git a/pages/api/instructor/lesson/togglepreview.ts b/pages/api/instructor/lesson/togglepreview.ts new file mode 100644 index 0000000..a712318 --- /dev/null +++ b/pages/api/instructor/lesson/togglepreview.ts @@ -0,0 +1,34 @@ +import { ErrorResponse, SuccessResponse } from "@/utils/apiresponses"; +import { NextApiRequest, NextApiResponse } from "next"; +import { Lesson } from '@prisma/client' +import { TogglePreviewLessonDTO } from "@/pages/instructor/course/[cid]"; +import { db } from "@/lib/db"; + +const handler = async (req: NextApiRequest, res: NextApiResponse) => { + + try { + const { lessongId } = req.body as TogglePreviewLessonDTO + const lesson = await db.lesson.findUnique({ where: { id: lessongId } }) + await db.lesson.update({ + where: { + id: lessongId + }, + data: { + free_preview: !lesson?.free_preview + } + }) + return SuccessResponse({ + res, + data: {}, + msg: "okay" + }) + } catch (error: any) { + return ErrorResponse({ + error: error.message, + res, + status: 500 + }) + } +} + +export default handler; \ No newline at end of file diff --git a/pages/api/instructor/make-instructor.ts b/pages/api/stripe/make-instructor.ts similarity index 100% rename from pages/api/instructor/make-instructor.ts rename to pages/api/stripe/make-instructor.ts diff --git a/pages/api/instructor/verify-instructor.ts b/pages/api/stripe/verify-instructor.ts similarity index 72% rename from pages/api/instructor/verify-instructor.ts rename to pages/api/stripe/verify-instructor.ts index e36f3a1..a4100ba 100644 --- a/pages/api/instructor/verify-instructor.ts +++ b/pages/api/stripe/verify-instructor.ts @@ -3,7 +3,8 @@ import { db } from "@/lib/db" import { stripe } from "@/lib/stripe" import { ErrorResponse, SuccessResponse } from "@/utils/apiresponses" import { NextApiRequest, NextApiResponse } from "next" -import { getServerSession } from "next-auth" +import { Session, getServerSession } from "next-auth" +import type { UserRoles } from '@/types/next-auth.d.ts' const hanlder = async (req: NextApiRequest, res: NextApiResponse) => { try { @@ -19,6 +20,16 @@ const hanlder = async (req: NextApiRequest, res: NextApiResponse) => { if (!account.charges_enabled) { return ErrorResponse({ error: "UnAuthorized", res, status: 400 }) } + if (!user.roles.includes("instructor" as UserRoles)) { + await db.user.update({ + where: { id: session.user.id }, + data: { + roles: { + push: "instructor" as UserRoles + } + } + }) + } return SuccessResponse({ res, data: {}, msg: "okay." }) } catch (error: any) { return ErrorResponse({ error: error.message, res, status: 500 }) diff --git a/pages/course/[cid].tsx b/pages/course/[cid].tsx new file mode 100644 index 0000000..1225377 --- /dev/null +++ b/pages/course/[cid].tsx @@ -0,0 +1,80 @@ +import { db } from '@/lib/db' +import { CourseLessonsList, SingleCourseJumboTron } from '@/molecules' +import { Button, Dialog } from '@/ui' +import { Course, Lesson } from '@prisma/client' +import { GetServerSideProps } from 'next' +import React, { useState } from 'react' +import ReactPlayer from 'react-player' +interface SingleCourseProp { + course: (Course & { + Lesson: Lesson[]; + user: { + id: string; + name: string | null; + }; + }) | null +} +const SingleCourse = ({ course }: SingleCourseProp) => { + const [hasWindow, sethasWindow] = useState(false) + const [open, setopen] = useState(false) + const [videoUrl, setvideoUrl] = useState("") + React.useEffect(() => { + if (typeof window !== "undefined") { + sethasWindow(true) + } + }, []) + return ( +
+ { + course && + { + setvideoUrl(video) + setopen(true) + }} + course={course} /> + } + { + (course?.Lesson !== undefined && course.Lesson.length > 0) && + { + setvideoUrl(video) + setopen(true) + } + } + lessons={course.Lesson} + /> + } + { setopen(open) }} className='max-w-[50%]'> +
+ +
+
+
+ ) +} + +export default SingleCourse + +export const getServerSideProps: GetServerSideProps = async (ctx) => { + const cid = ctx?.params?.cid as string + const course = await db.course.findFirst({ + where: { + id: cid + }, + include: { + user: { + select: { + id: true, + name: true + } + }, + Lesson: {} + } + }) + return { + props: { + course + } + } +} \ No newline at end of file diff --git a/pages/index.tsx b/pages/index.tsx index cb8f9c2..d0f146b 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -1,12 +1,36 @@ import { UserPagesLayout } from "@/layouts" +import { db } from "@/lib/db" +import { UserCourseCard } from "@/molecules" +import { Course } from "@prisma/client" +import { GetServerSideProps } from "next" import { useSession } from "next-auth/react" - -export default function Home() { +interface HomeProps { + courses: Course[] +} +export default function Home({ courses }: HomeProps) { const { data: session } = useSession() console.log({ session }) return ( -
user layou
+
+ + { + courses.map((c, ci) => ( + + )) + } +
) } + + +export const getServerSideProps: GetServerSideProps = async (ctx) => { + const courses = await db.course.findMany() + + return { + props: { + courses + } + } +} diff --git a/pages/instructor/course/[cid].tsx b/pages/instructor/course/[cid].tsx new file mode 100644 index 0000000..d5734c2 --- /dev/null +++ b/pages/instructor/course/[cid].tsx @@ -0,0 +1,104 @@ +import { InstructorLayout } from '@/layouts' +import { db } from '@/lib/db' +import { axios } from '@/services/api/axios' +import { Button } from '@/ui' +import { Course, Lesson } from '@prisma/client' +import { GetServerSideProps } from 'next' +import Image from 'next/image' +import { useRouter } from 'next/router' +import React from 'react' +import { useMutation } from 'react-query' +interface SingleCoursePageProp { + course: (Course & { + Lesson: Lesson[]; + }) | null +} +export interface TogglePreviewLessonDTO { + lessongId: string +} +const SingleCoursePage = ({ course }: SingleCoursePageProp) => { + const toggleLessonPreviewMutation = useMutation({ + mutationFn: (data: TogglePreviewLessonDTO) => { + return axios.post('/instructor/lesson/togglepreview', data) + }, + onSuccess: () => { + alert("updated successfully!") + } + }) + console.log(course) + const router = useRouter() + return ( + + <> + { + !course &&
No course found...
+ } + { + course && +
+
+
+
+ +
+
+

{course.name}

+

{course.description}

+
+
+

{course.Lesson.length} Lessons

+ { + toggleLessonPreviewMutation.isLoading ?
Updating
+ : + course?.Lesson?.map((l, li) => ( +
+
+
{li + 1}
+

{l.name}

+
+
+
+ Preview: {l.free_preview ? True : False} +
+ { + l.free_preview ? +
+
+ )) + } +
+
+ } + +
+ ) +} + +export default SingleCoursePage + + +export const getServerSideProps: GetServerSideProps = async (ctx) => { + const query = ctx.query.cid as string + const course = await db.course.findUnique({ + where: { + id: query + }, + include: { + Lesson: {} + } + }) + return { + props: { + course + } + } +} \ No newline at end of file diff --git a/pages/instructor/course/create.tsx b/pages/instructor/course/create.tsx new file mode 100644 index 0000000..39c141f --- /dev/null +++ b/pages/instructor/course/create.tsx @@ -0,0 +1,115 @@ +import React from 'react' +import { Course } from '@prisma/client' +import { ErrorMessage, Formik } from 'formik' +import { Button, Input } from '@/ui' +import * as Yup from 'yup' +import { createCourseApi } from '@/services/api' +import { handleFrontEndResponse } from '@/utils/apiresponses' +import { InstructorLayout } from '@/layouts' +const prices = [ + "9.99", + "19.99", + "29.99", + "39.99", + "49.99", +] +interface IInitialValues { + category: string, + description: string, + name: string, + paid: boolean, + price: string + file: File | null +} + +const initialValues: IInitialValues = { + category: "", + description: "", + name: "", + paid: true, + price: "9.99", + file: null +} + +const CreateCourseSchema = Yup.object().shape({ + name: Yup.string().required(), + category: Yup.string().required(), + description: Yup.string().required(), + paid: Yup.boolean().required(), + price: Yup.string().required(), + +}); +const CreateCourses = () => { + return ( + + { + try { + await createCourseApi({ + category: values.category, + description: values.description, + imagePreview: values.file!, + name: values.name, + paid: values.paid, + price: Number(values.price) + }) + alert("created successfully!") + } catch (error: any) { + const err = handleFrontEndResponse(error) + alert(err) + } + }} + > + { + ({ + handleChange, + values, + setValues, + handleSubmit, + setFieldValue + }) => ( +
+ + + + + + + +
+

paid

+ { setFieldValue("paid", true) }} type="radio" checked={values.paid} /> +

free

+ { setFieldValue("paid", false) }} type="radio" checked={!values.paid} /> +
+ + + + { + if (!e.target.files) { + return; + } + const file = e.target.files[0] + + setFieldValue("file", file) + }} /> + + + + ) } diff --git a/ui/Dialog/Dialog.tsx b/ui/Dialog/Dialog.tsx new file mode 100644 index 0000000..f84da8e --- /dev/null +++ b/ui/Dialog/Dialog.tsx @@ -0,0 +1,35 @@ +import React from 'react' +import * as RadiDialog from '@radix-ui/react-dialog' +import clsx from 'clsx' +interface DialogProps { + open: boolean, + onChange: (open: boolean) => void, + title?: string + children: React.ReactElement + className?: string +} +export const Dialog = ({ onChange, open, children, title, className }: DialogProps) => { + return ( + { onChange(open) }}> + + + + { + title ? + + {title} + + : + null + } + {children} +
+
+
+
+
+ ) +} \ No newline at end of file diff --git a/ui/Dialog/index.ts b/ui/Dialog/index.ts new file mode 100644 index 0000000..aca62ab --- /dev/null +++ b/ui/Dialog/index.ts @@ -0,0 +1 @@ +export * from './Dialog' \ No newline at end of file diff --git a/ui/Select/Select.tsx b/ui/Select/Select.tsx new file mode 100644 index 0000000..0aee003 --- /dev/null +++ b/ui/Select/Select.tsx @@ -0,0 +1,7 @@ +import React from 'react' + +export const Select = () => { + return ( +
Select
+ ) +} \ No newline at end of file diff --git a/ui/Select/index.ts b/ui/Select/index.ts new file mode 100644 index 0000000..ec5ec83 --- /dev/null +++ b/ui/Select/index.ts @@ -0,0 +1 @@ +export * from './Select' \ No newline at end of file diff --git a/ui/index.ts b/ui/index.ts index fda236b..7c3a7c6 100644 --- a/ui/index.ts +++ b/ui/index.ts @@ -1,2 +1,5 @@ export * from './Input' -export * from './Button' \ No newline at end of file +export * from './Button' +export * from './Select' +export * from './Badge' +export * from './Dialog' \ No newline at end of file diff --git a/utils/currencyFormaterClient.ts b/utils/currencyFormaterClient.ts new file mode 100644 index 0000000..b0bfdcb --- /dev/null +++ b/utils/currencyFormaterClient.ts @@ -0,0 +1,11 @@ +interface CurrencyFormaterClientProps { + amount: number, + currency?: string +} +export const currencyFormaterClient = ({ + amount, + currency = "USD" +}: CurrencyFormaterClientProps) => { + let toreturn=`$${amount}` + return `${toreturn}`; +} \ No newline at end of file