├── .gitignore
├── README.md
├── components
├── Footer.jsx
├── NavBar.jsx
├── auth
│ ├── AuthBtn.jsx
│ └── FormInput.jsx
├── cart
│ ├── CartItem.jsx
│ └── NoItemCart.jsx
├── courses
│ ├── commentMsg.jsx
│ ├── detail
│ │ ├── Banner.jsx
│ │ ├── CourseContentItem.jsx
│ │ ├── CourseContentList.jsx
│ │ ├── CourseContentSec.jsx
│ │ ├── CourseDetail.jsx
│ │ ├── Description.jsx
│ │ ├── FeedBack.jsx
│ │ ├── WhatLearnt.jsx
│ │ └── WhatLearntItem.jsx
│ ├── list
│ │ └── CourseListCard.jsx
│ └── study
│ │ ├── CommentArea.jsx
│ │ ├── CommentBox.jsx
│ │ ├── WatchArea.jsx
│ │ ├── WatchList.jsx
│ │ └── WatchListItem.jsx
├── index
│ ├── CategoryCard.jsx
│ ├── CategoryList.jsx
│ ├── CompanyCard.jsx
│ ├── CourseCard.jsx
│ ├── CourseList.jsx
│ ├── CourseSuggest.jsx
│ ├── Header.jsx
│ ├── TeachUdemy.jsx
│ ├── TrustedCompanies.jsx
│ └── courseSuggestChangeBtn.jsx
├── layouts
│ ├── main.jsx
│ └── watch.jsx
└── user
│ └── courseItem.jsx
├── config
└── app.js
├── context
├── AuthContext.jsx
└── CartContext.jsx
├── next.config.js
├── package-lock.json
├── package.json
├── pages
├── _app.js
├── api
│ ├── addComment.js
│ ├── login.js
│ ├── logout.js
│ ├── payment.js
│ ├── refresh_token.js
│ ├── signup.js
│ └── user.js
├── auth
│ ├── login.js
│ └── signup.js
├── cart
│ └── index.jsx
├── course
│ ├── author
│ │ ├── [course_uuid]
│ │ │ └── manage
│ │ │ │ ├── delete.js
│ │ │ │ └── update.js
│ │ ├── course_add.js
│ │ └── course_list.js
│ ├── detail
│ │ └── [course_uuid].js
│ ├── sector
│ │ └── [sector_uuid].js
│ └── study
│ │ └── [course_uuid].js
├── index.jsx
├── search
│ └── [term].jsx
└── user.jsx
├── public
├── assets
│ ├── pexels-louis.jpg
│ ├── udemy-b1.svg
│ ├── udemy-b2.svg
│ ├── udemy-b3.svg
│ ├── udemy-b4.svg
│ ├── udemy-b5.svg
│ ├── udemy-red-white.svg
│ ├── udemy1.jpg
│ ├── udemy_logo-red.svg
│ ├── udemy_logo.svg
│ └── udemy_teacher.jpg
└── favicon.ico
├── redux
├── slices
│ └── AuthSlices.js
└── store.js
└── styles
└── style.css
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
27 | # local env files
28 | .env.local
29 | .env.development.local
30 | .env.test.local
31 | .env.production.local
32 |
33 | # vercel
34 | .vercel
35 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 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).
2 |
3 | ## Getting Started
4 |
5 | First, run the development server:
6 |
7 | ```bash
8 | npm run dev
9 | # or
10 | yarn dev
11 | ```
12 |
13 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
14 |
15 | You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file.
16 |
17 | [API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`.
18 |
19 | The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
20 |
21 | ## Learn More
22 |
23 | To learn more about Next.js, take a look at the following resources:
24 |
25 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
26 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
27 |
28 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
29 |
30 | ## Deploy on Vercel
31 |
32 | The 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.
33 |
34 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
35 |
--------------------------------------------------------------------------------
/components/Footer.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | export const Footer = () => {
4 | return (
5 | <>
6 |
45 |
46 |
47 | >
48 | )
49 | }
50 |
--------------------------------------------------------------------------------
/components/NavBar.jsx:
--------------------------------------------------------------------------------
1 | import React, { useContext,useState } from 'react'
2 | import Link from 'next/link'
3 | import { useRouter } from 'next/router'
4 | import AuthContext from '../context/AuthContext'
5 | import CartContext from '../context/CartContext'
6 |
7 |
8 | export const NavBar = () => {
9 |
10 | const router=useRouter()
11 |
12 | const pushRoute=(route)=> {
13 | router.push(route)
14 | }
15 |
16 | const {user,logout}=useContext(AuthContext)
17 |
18 | const {cart} = useContext(CartContext)
19 | const [term,setTerm]=useState('')
20 |
21 | const handleChange=e=>{
22 | setTerm(e.target.value)
23 | }
24 |
25 | const handleSubmit=e=>{
26 | e.preventDefault()
27 | router.push('/search/'+term)
28 | setTerm('')
29 | }
30 |
31 |
32 | return (
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
48 |
67 |
68 |
69 | {!user ?
70 | (<>
71 |
pushRoute('/auth/login')} className="text-sm py-1 px-2 lg:text-base lg:py-2 rounded-md md:px-5 border border-blue-500 font-semibold text-blue-500 bg-white ">Log In
72 |
pushRoute('/auth/signup')} className="text-sm py-1 px-2 lg:text-base lg:py-2 rounded-md md:px-5 border bg-blue-500 font-semibold text-white" >Sign Up
73 | >) :
74 | (<>
75 |
76 |
77 |
{user.name}
78 |
79 |
Logout
80 | >)}
81 |
82 |
83 | )
84 | }
85 |
--------------------------------------------------------------------------------
/components/auth/AuthBtn.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | function AuthBtn({action,disabled}) {
4 | return (
5 |
6 | {action}
7 |
8 | )
9 | }
10 |
11 | export default AuthBtn
12 |
--------------------------------------------------------------------------------
/components/auth/FormInput.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const FormInput = ({iconName,type,name,placeholder,setInput,inputVal}) => {
4 | return (
5 |
6 |
7 |
8 |
9 |
setInput(e.target.value)} className="w-9/12 md:w-10/12 block py-2 px-1 md:text-lg outline-none" type={type} name={name} />
10 |
11 | )
12 | }
13 |
14 | export default FormInput
15 |
--------------------------------------------------------------------------------
/components/cart/CartItem.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | function CartItem({detail,removeCart,removeCartDetail}) {
4 | // console.log("detail: ",detail)
5 |
6 | const handleRemove=()=>{
7 | removeCart(detail.course_uuid)
8 | removeCartDetail(detail.course_uuid)
9 | }
10 |
11 | return (
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | {detail.title.length > 60 ? detail.title.slice(0,59) +"..." : detail.title}
21 |
22 |
23 |
24 | By {detail.author.name}
25 |
26 |
27 |
28 |
29 |
30 |
31 | ${detail.price}
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | Remove
40 |
41 |
42 |
43 |
44 |
45 |
46 | )
47 | }
48 |
49 | export default CartItem
50 |
--------------------------------------------------------------------------------
/components/cart/NoItemCart.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Link from 'next/link'
3 |
4 | function NoItemCart() {
5 | return (
6 |
7 |
8 |
9 |
10 |
11 | Your cart is empty. Keep shopping to find a course!
12 |
13 |
14 |
15 | Keep Shopping
16 |
17 |
18 |
19 |
20 | )
21 | }
22 |
23 | export default NoItemCart
24 |
--------------------------------------------------------------------------------
/components/courses/commentMsg.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | function commentMsg({comment}) {
4 | const colors=['blue','indigo','red','yellow','green']
5 | const color=Math.floor(Math.random() * 4)
6 | return (
7 |
8 |
9 |
10 |
11 | {comment.user.name[0].toUpperCase()}
12 |
13 |
14 |
15 |
16 |
17 | {comment.user.name}
18 |
19 |
20 | {comment.message}
21 |
22 |
23 |
24 | )
25 | }
26 |
27 | export default commentMsg
28 |
--------------------------------------------------------------------------------
/components/courses/detail/Banner.jsx:
--------------------------------------------------------------------------------
1 | import { useRouter } from 'next/router'
2 | import React, { useContext } from 'react'
3 | import AuthContext from '../../../context/AuthContext'
4 | import CartContext from '../../../context/CartContext'
5 | import Link from 'next/link'
6 |
7 |
8 | function Banner({data}) {
9 |
10 | const {addCart,cart,removeCart} = useContext(CartContext)
11 | const router = useRouter()
12 |
13 | const {course_uuid} = router.query
14 |
15 |
16 |
17 | const {user}=useContext(AuthContext)
18 |
19 | const courses= user?.courses || []
20 |
21 |
22 |
23 |
24 | const handleCarting=()=>{
25 |
26 | if(cart.includes(course_uuid)){
27 |
28 | removeCart(course_uuid)
29 | } else{
30 | addCart(course_uuid)
31 |
32 | }
33 | }
34 | return (
35 |
36 |
37 | {data.title}
38 |
39 |
40 | {data.description.length > 60 ? data.description.slice(0,59) +"..." : data.description}
41 |
42 |
43 |
44 | Bestseller
45 |
46 | {data.rating} rating
47 |
48 | ({data.number_of_rating} ratings) {data.student_no} students
49 |
50 |
51 |
52 | Created by {data.author}
53 |
54 |
55 | ${data.price}
56 |
57 |
58 | {!courses.includes(course_uuid) ?
59 |
60 | {cart.includes(course_uuid) ? "Remove from cart" : "Add to cart" }
61 | :
62 |
63 |
64 |
65 | Start Learning
66 |
67 |
68 |
69 | }
70 |
71 |
72 | )
73 | }
74 |
75 | export default Banner
76 |
--------------------------------------------------------------------------------
/components/courses/detail/CourseContentItem.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | function CourseContentItem({data,setSrc}) {
4 |
5 | const handleClick=()=>{
6 | if(setSrc){
7 | setSrc(data.file)
8 | }
9 |
10 | }
11 |
12 | return (
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | {data.title}
22 |
23 |
24 |
25 |
26 |
27 | {data.length}
28 |
29 |
30 |
31 |
32 | )
33 | }
34 |
35 | export default CourseContentItem
36 |
--------------------------------------------------------------------------------
/components/courses/detail/CourseContentList.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import CourseContentItem from './CourseContentItem'
3 |
4 | function CourseContentList({hidden,data}) {
5 |
6 | return (
7 |
8 |
9 | {
10 | data.map((episode,index)=>(
11 |
12 |
13 | ))
14 | }
15 |
16 |
17 | )
18 | }
19 |
20 | export default CourseContentList
21 |
--------------------------------------------------------------------------------
/components/courses/detail/CourseContentSec.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import CourseContentList from './CourseContentList'
3 |
4 | function CourseContentSec({section}) {
5 | const [hidden,setHidden] =useState(true)
6 |
7 | const changeHidden=()=>{
8 | setHidden(!hidden)
9 | }
10 |
11 |
12 | return (
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | {hidden ? :
21 | }
22 |
23 |
24 |
25 |
26 | {section.section_title}
27 |
28 |
29 |
30 |
31 |
32 | {section.episodes.length} lecture{section.episodes.length>1 &&'s'}
33 | • {section.total_duration}
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | )
45 | }
46 |
47 | export default CourseContentSec
48 |
--------------------------------------------------------------------------------
/components/courses/detail/CourseDetail.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import CourseContentSec from './CourseContentSec'
3 |
4 | function CourseDetail({sections,total_lectures,total_length}) {
5 | return (
6 |
7 |
8 |
9 |
10 |
11 | Course Content
12 |
13 |
14 |
15 | {sections.length} sections
16 |
17 | • {total_lectures} lectures
18 | • {total_length} total length
19 |
20 |
21 | {
22 | sections.map((section,index)=>(
23 |
24 |
25 | ))
26 | }
27 |
28 |
29 |
30 |
31 |
32 |
33 | )
34 | }
35 |
36 | export default CourseDetail
37 |
--------------------------------------------------------------------------------
/components/courses/detail/Description.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | function Description({info}) {
4 | return (
5 |
6 |
7 |
8 |
9 | Description
10 |
11 |
12 |
13 | {info}
14 |
15 |
16 |
17 | )
18 | }
19 |
20 | export default Description
21 |
--------------------------------------------------------------------------------
/components/courses/detail/FeedBack.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import CommentMsg from '../commentMsg'
3 |
4 | function FeedBack({comments}) {
5 | return (
6 |
7 |
8 |
9 | Feedbacks
10 |
11 |
12 | {
13 | comments.length? comments.map(comment=>(
14 |
15 |
16 | )) :
17 |
18 | No comments
19 |
20 |
21 | }
22 |
23 |
24 |
25 |
26 |
27 | )
28 | }
29 |
30 | export default FeedBack
31 |
--------------------------------------------------------------------------------
/components/courses/detail/WhatLearnt.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import WhatLearntItem from './WhatLearntItem'
3 |
4 | function WhatLearnt() {
5 | return (
6 |
7 |
8 |
9 | What you'll learn
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | )
30 | }
31 |
32 | export default WhatLearnt
33 |
--------------------------------------------------------------------------------
/components/courses/detail/WhatLearntItem.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | function WhatLearntItem({msg}) {
4 | return (
5 |
6 |
7 |
8 |
9 |
10 |
11 | {msg}
12 |
13 |
14 | )
15 | }
16 |
17 | export default WhatLearntItem
18 |
--------------------------------------------------------------------------------
/components/courses/list/CourseListCard.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Link from 'next/link'
3 |
4 | function CourseListCard({data}) {
5 | return (
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | {data.title.length > 60 ? data.title.slice(0,59) +"..." : data.title}
17 |
18 |
19 | {data.description}
20 |
21 |
22 | {data.author.name}
23 |
24 |
25 | {data.rating} ({data.student_no})
26 |
27 |
28 | 22 total hours • {data.total_lectures} lectures
29 |
30 |
31 | Bestseller
32 |
33 |
34 |
35 |
36 |
37 | ${data.price}
38 |
39 |
40 |
41 |
42 |
43 | )
44 | }
45 |
46 | export default CourseListCard
47 |
--------------------------------------------------------------------------------
/components/courses/study/CommentArea.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import CommentMsg from '../commentMsg'
3 | import CommentBox from './CommentBox'
4 |
5 | function CommentArea({comments,setComment}) {
6 |
7 |
8 |
9 | return (
10 |
11 |
12 |
13 | Comments
14 |
15 |
16 |
17 | {
18 | comments.map((comment,index) =>( ))
19 | }
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | )
29 | }
30 |
31 | export default CommentArea
32 |
--------------------------------------------------------------------------------
/components/courses/study/CommentBox.jsx:
--------------------------------------------------------------------------------
1 | import { useRouter } from 'next/router'
2 | import React, {useState,useContext} from 'react'
3 | import { toast } from 'react-toastify'
4 | import { NEXT_BACKEND_URI } from '../../../config/app'
5 | import AuthContext from '../../../context/AuthContext'
6 |
7 |
8 | function CommentBox({setComment,comments}) {
9 |
10 | const [message,setMessage]=useState('')
11 |
12 | const router = useRouter()
13 | const { course_uuid } = router.query
14 |
15 |
16 | const handleChange=(e)=>{
17 | setMessage(e.target.value)
18 | }
19 |
20 | const {user:{name}}=useContext(AuthContext)
21 |
22 | const handleClick=async(e)=>{
23 |
24 | e.preventDefault()
25 |
26 | const new_comment={
27 | user:{
28 | name
29 | },
30 | message,
31 |
32 | }
33 | // TODO: post comment to API
34 | const res=await fetch(`${NEXT_BACKEND_URI}/addComment/`,{
35 | method:'POST',
36 | body:JSON.stringify({data:{message},course_uuid})
37 | })
38 |
39 | // console.log(res.ok,(await res).status)
40 | if (!res.ok){
41 | toast.error("could not add your comment")
42 | }else{
43 | toast.success('comment added')
44 | setComment([...comments,new_comment])
45 | setMessage('')
46 | }
47 |
48 |
49 |
50 | }
51 |
52 |
53 | return (
54 |
55 |
56 | Leave A Comment
57 |
58 |
64 |
65 | )
66 | }
67 |
68 | export default CommentBox
69 |
--------------------------------------------------------------------------------
/components/courses/study/WatchArea.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState,useEffect } from 'react'
2 | import WatchList from './WatchList'
3 |
4 | function WatchArea({sections}) {
5 | const [src,setSrc] =useState(sections[0].episodes[0].file)
6 |
7 |
8 | return (
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | Course Content
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | )
30 | }
31 |
32 | export default WatchArea
33 |
--------------------------------------------------------------------------------
/components/courses/study/WatchList.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import WatchListItem from './WatchListItem'
3 |
4 | function WatchList({sections,setSrc}) {
5 | return (
6 | <>
7 |
8 | {
9 | sections.map((section,index)=>(
10 |
11 |
12 | ))
13 | }
14 |
15 |
16 | >
17 |
18 | )
19 | }
20 |
21 | export default WatchList
22 |
--------------------------------------------------------------------------------
/components/courses/study/WatchListItem.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import CourseContentItem from '../detail/CourseContentItem'
3 |
4 |
5 |
6 | function WatchListItem({section,setSrc}) {
7 | const [hidden, setHidden] = useState()
8 | const handleHidden=()=>{
9 | setHidden(!hidden)
10 | }
11 | return (
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | {section.section_title}
21 |
22 |
23 |
24 | {hidden ? :
25 | }
26 |
27 |
28 |
29 |
30 |
31 |
32 | {
33 | section.episodes.map((data,index)=>( ))
34 | }
35 |
36 |
37 |
38 |
39 | )
40 | }
41 |
42 | export default WatchListItem
43 |
--------------------------------------------------------------------------------
/components/index/CategoryCard.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Link from 'next/link'
3 |
4 | export const CategoryCard = ({data}) => {
5 |
6 | return (
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | {data.sector_name[0].toUpperCase()+data.sector_name.slice(1,data.sector_name.length)}
16 |
17 |
18 |
19 |
20 | )
21 | }
22 |
--------------------------------------------------------------------------------
/components/index/CategoryList.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { CategoryCard } from './CategoryCard'
3 |
4 | const CategoryList = ({data}) => {
5 |
6 | const sectors=data.map(sector=>({sector_name:sector.sector_name, uuid:sector.sector_uuid,sector_image:sector.sector_image}))
7 | return (
8 |
9 |
10 | Top categories
11 |
12 |
13 |
14 |
15 | {sectors.map(sector=> )}
16 |
17 |
18 |
19 | )
20 | }
21 |
22 | export default CategoryList;
23 |
--------------------------------------------------------------------------------
/components/index/CompanyCard.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | export const CompanyCard = ({src,alt,resize}) => {
4 | return (
5 |
6 |
7 |
8 | )
9 | }
10 |
--------------------------------------------------------------------------------
/components/index/CourseCard.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Link from 'next/link'
3 |
4 | export const CourseCard = ({data}) => {
5 |
6 | return (
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | {/* Machine Learning A-Z™: Hands-On Python & R In Data... */}
18 | {data.title.length > 60 ? data.title.slice(0,59) +"..." : data.title}
19 |
20 |
21 | {/* OtchereDev */}
22 | By {data.author.name.length > 10 ? data.author.name.slice(0,9) +"..." : data.author.name}
23 |
24 |
25 | rating: {data.rating} ({data.student_no})
26 |
27 |
28 | ${data.price}
29 |
30 |
31 | Bestseller
32 |
33 |
34 |
35 |
36 | )
37 | }
38 |
--------------------------------------------------------------------------------
/components/index/CourseList.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { CourseCard } from './CourseCard'
3 |
4 | const CourseList = ({data}) => {
5 |
6 | const courses=data.map(sector=>sector.featured_courses)
7 |
8 |
9 | return (
10 |
11 |
12 | Students are viewing
13 |
14 |
15 | {courses.map(course_grp=>course_grp.map((course,index)=> ))}
16 |
17 |
18 |
19 | )
20 | }
21 |
22 | export default CourseList;
23 |
--------------------------------------------------------------------------------
/components/index/CourseSuggest.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from 'react'
2 | import {CourseCard} from './CourseCard'
3 | import { CourseSuggestChangeBtn } from './courseSuggestChangeBtn'
4 |
5 | const CourseSuggest = ({data}) => {
6 |
7 | const [suggest,setSuggest]=useState(data[0].sector_name)
8 | // console.log(suggest,data.find(sector=>sector.sector_name==suggest).featured_courses)
9 | const [suggestedCourse,setSuggestedCourse]=useState(data.find(sector=>sector.sector_name==suggest).featured_courses)
10 |
11 | useEffect(()=>{
12 | setSuggestedCourse(data.find(sector=>sector.sector_name==suggest).featured_courses)
13 | },[suggest])
14 |
15 |
16 |
17 | return (
18 |
19 |
20 | The world's largest selection of courses
21 |
22 |
23 | Choose from 155,000 online video courses with new additions published every month
24 |
25 |
26 |
27 |
28 | {data.map(sector=>(
29 |
30 |
31 | ))}
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | Lead data-driven decisions with Data Science
40 |
41 |
42 | Data science is everywhere. Better data science practices are allowing corporations to cut unnecessary costs, automate computing, and analyze markets. Essentially, data science is the key to getting ahead in a competitive global climate.
43 |
44 |
45 | Explore {suggest[0].toUpperCase()+suggest.slice(1,suggest.length)}
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 | {
57 | suggestedCourse.length? suggestedCourse.map((course,index)=>( )) :
58 |
59 | }
60 |
61 |
62 |
63 |
64 |
65 |
66 | )
67 | }
68 |
69 | export default CourseSuggest
--------------------------------------------------------------------------------
/components/index/Header.jsx:
--------------------------------------------------------------------------------
1 | import { useRouter } from 'next/router'
2 | import React, { useState } from 'react'
3 |
4 |
5 | const Header = () => {
6 | const [term,setTerm]=useState('')
7 | const router =useRouter()
8 | const handleSubmit=e=>{
9 | e.preventDefault()
10 | router.push(`/search/${term}`)
11 |
12 | }
13 | return (
14 |
15 |
16 |
17 |
18 |
19 |
20 | Dream Up
21 |
22 |
23 | Ambition accepted. Learn the latest skills to reach your professional goals.
24 |
25 |
26 | setTerm(e.target.value)} type="text" placeholder="What fo you want to learn ?" className="outline-none bg-transparent w-10/12 py-2" name="search_box"/>
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | )
35 | }
36 |
37 | export default Header
38 |
--------------------------------------------------------------------------------
/components/index/TeachUdemy.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const TeachUdemy = () => {
4 | return (
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | Become an instructor
15 |
16 |
17 | Top instructors from around the world teach millions of students on Udemy. We provide the tools and skills to teach what you love.
18 |
19 |
20 |
21 | Start teaching today
22 |
23 |
24 |
25 |
26 |
27 |
28 | )
29 | }
30 |
31 | export default TeachUdemy;
32 |
--------------------------------------------------------------------------------
/components/index/TrustedCompanies.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { CompanyCard } from './CompanyCard'
3 |
4 | const TrustedCompanies = () => {
5 | return (
6 |
7 |
8 | Trusted by companies of all sizes
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | )
26 | }
27 |
28 | export default TrustedCompanies;
29 |
--------------------------------------------------------------------------------
/components/index/courseSuggestChangeBtn.jsx:
--------------------------------------------------------------------------------
1 | export const CourseSuggestChangeBtn = ({name,setSuggest}) => {
2 | return (
3 |
4 | setSuggest(name)} className='text-sm block md:inline md:text-lg font-semibold text-gray-400 hover:text-gray-700 mr-5 focus:outline-none' href="">{name[0].toUpperCase()+name.slice(1,name.length)}
5 |
6 | )
7 | }
8 |
--------------------------------------------------------------------------------
/components/layouts/main.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Footer } from '../Footer'
3 | import { NavBar } from '../NavBar'
4 |
5 | import { ToastContainer } from 'react-toastify';
6 | import 'react-toastify/dist/ReactToastify.css';
7 |
8 | const main = ({children}) => {
9 |
10 | return (
11 |
12 |
13 |
14 |
15 |
16 | {children}
17 |
18 |
19 |
20 | )
21 | }
22 |
23 | export default main
24 |
--------------------------------------------------------------------------------
/components/layouts/watch.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Footer } from '../Footer'
3 | import Link from 'next/link'
4 |
5 | import { ToastContainer } from 'react-toastify';
6 | import 'react-toastify/dist/ReactToastify.css';
7 |
8 | const main = ({children,title}) => {
9 |
10 | return (
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | | {title}
22 |
23 |
24 |
25 |
26 |
27 |
28 | {children}
29 |
30 |
31 |
32 | )
33 | }
34 |
35 | export default main
36 |
--------------------------------------------------------------------------------
/components/user/courseItem.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Link from 'next/link'
3 |
4 | function CourseItem({course}) {
5 | return (
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | {course.title}
17 |
18 |
19 |
20 | {course.author.name}
21 |
22 |
23 |
24 |
25 |
26 |
27 | )
28 | }
29 |
30 | export default CourseItem
31 |
--------------------------------------------------------------------------------
/config/app.js:
--------------------------------------------------------------------------------
1 | // export const BACKEND_URI=process.env.BACKEND_URI
2 | // export const NEXT_BACKEND_URI=process.env.NEXT_BACKEND_URI
3 |
4 | export const BACKEND_URI="http://localhost:8000"
5 | export const NEXT_BACKEND_URI='http://localhost:3000/api'
--------------------------------------------------------------------------------
/context/AuthContext.jsx:
--------------------------------------------------------------------------------
1 | import { useRouter } from "next/router";
2 | import { useState, createContext, useEffect } from "react";
3 | import {NEXT_BACKEND_URI} from '../config/app'
4 | import { useSelector, useDispatch } from 'react-redux'
5 | import {addUser,removeUser} from '../redux/slices/AuthSlices'
6 |
7 |
8 | const AuthContext = createContext()
9 |
10 | export const AuthContextProvider=({children})=>{
11 | const user=useSelector(state=>state.auth.user)
12 | const [authError,setAuthError]=useState(null)
13 | const [authReady,setAuthReady]=useState(false)
14 |
15 | const dispatch=useDispatch()
16 |
17 |
18 |
19 |
20 |
21 |
22 | const router = useRouter()
23 |
24 | useEffect(()=>checkUserLoggedIn(),[])
25 |
26 | const login=async({email,password})=>{
27 | const res = await fetch(`${NEXT_BACKEND_URI}/login`,{
28 |
29 | method:"POST",
30 | headers:{
31 | "Content-type":"application/json"
32 | },
33 | body:JSON.stringify({email,password})
34 | })
35 |
36 | const data= await res.json()
37 |
38 |
39 | if (res.ok){
40 |
41 | // setUser(data.user)
42 |
43 | await checkUserLoggedIn()
44 |
45 | router.push('/')
46 |
47 | }else{
48 |
49 | setAuthError(data.detail)
50 |
51 | }
52 |
53 |
54 | }
55 |
56 | const signup=async({email,password,name})=>{
57 | const res = await fetch(`${NEXT_BACKEND_URI}/signup`,{
58 |
59 | method:"POST",
60 | headers:{
61 | "Content-type":"application/json"
62 | },
63 | body:JSON.stringify({email,password,name})
64 | })
65 |
66 | const data= await res.json()
67 |
68 | if (res.ok){
69 |
70 | // setUser(data)
71 |
72 | await login({email,password})
73 |
74 |
75 |
76 | }else{
77 | if (data.name){
78 |
79 | setAuthError(data.name[0])
80 | }else if(data.email){
81 | setAuthError(data.email[0])
82 | }else{
83 | setAuthError(data.password.join('\n'))
84 | }
85 |
86 | }
87 |
88 | }
89 |
90 | const logout=async()=>{
91 | const res= await fetch(`${NEXT_BACKEND_URI}/logout`,{
92 | method:"POST"
93 | })
94 |
95 | if (res.ok){
96 | // setUser(null)
97 | // push user if necessary
98 | dispatch(removeUser())
99 | }
100 | }
101 |
102 | const checkUserLoggedIn=async()=>{
103 |
104 |
105 |
106 | const res = await fetch(`${NEXT_BACKEND_URI}/user`)
107 |
108 |
109 |
110 |
111 |
112 | if (res.ok){
113 | const data= await res.json()
114 | dispatch(addUser(data.user))
115 | // setUser(data.user)
116 |
117 | }else{
118 | // setUser(null)
119 | dispatch(addUser(null))
120 |
121 |
122 | }
123 |
124 | setAuthReady(true)
125 |
126 | return
127 |
128 |
129 | }
130 |
131 | const clearUser=()=>{
132 |
133 | dispatch(removeUser())
134 | }
135 |
136 | return (
137 |
138 | {authReady && children}
139 |
140 | )
141 | }
142 |
143 |
144 |
145 | export default AuthContext
--------------------------------------------------------------------------------
/context/CartContext.jsx:
--------------------------------------------------------------------------------
1 |
2 | import { useState, createContext, useEffect,useContext } from "react";
3 | import { toast } from "react-toastify";
4 | import AuthContext from "./AuthContext";
5 |
6 |
7 | const CartContext = createContext()
8 |
9 | export const CartContextProvider=({children})=>{
10 |
11 | const {user}=useContext(AuthContext)
12 |
13 | const initCart= localStorage.getItem('cart') ? JSON.parse(localStorage.getItem('cart')):[]
14 | const [cart,setCart]=useState([...initCart.filter(item=>{
15 |
16 | if(user){
17 | return !user.courses.includes(item)
18 | }else{
19 | return true
20 | }
21 | })])
22 |
23 | localStorage.setItem('cart',JSON.stringify(cart))
24 |
25 | const addCart=(uuid)=>{
26 | if(!cart.includes(uuid)){
27 | setCart([...cart,uuid])
28 | localStorage.setItem('cart',JSON.stringify([...cart,uuid]))
29 | toast.info('1 course added to cart')
30 | }
31 | }
32 |
33 | const removeCart=(uuid)=>{
34 | const index = cart.findIndex(item=>item===uuid)
35 |
36 | if(index > -1){
37 | const currentCart=[...cart]
38 | currentCart.splice(index,1)
39 | setCart(currentCart)
40 | localStorage.setItem('cart',JSON.stringify(currentCart))
41 | toast.info('1 course removed from cart')
42 | }
43 | }
44 |
45 | const clearCart=()=>{
46 | setCart([])
47 | localStorage.setItem('cart',JSON.stringify([]))
48 | }
49 |
50 |
51 |
52 |
53 |
54 | return (
55 |
56 | {children}
57 |
58 | )
59 | }
60 |
61 |
62 |
63 | export default CartContext
--------------------------------------------------------------------------------
/next.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | reactStrictMode: true,
3 | }
4 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "udemy_frontend",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@reduxjs/toolkit": "^1.6.0",
13 | "cookie": "^0.4.1",
14 | "next": "11.0.1",
15 | "react": "17.0.2",
16 | "react-dom": "17.0.2",
17 | "react-redux": "^7.2.4",
18 | "react-toastify": "^7.0.4"
19 | },
20 | "devDependencies": {
21 | "eslint": "7.29.0",
22 | "eslint-config-next": "11.0.1"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/pages/_app.js:
--------------------------------------------------------------------------------
1 | import '../styles/style.css'
2 | import {AuthContextProvider} from '../context/AuthContext'
3 | import {useEffect} from 'react'
4 |
5 | import {CartContextProvider} from '../context/CartContext'
6 |
7 | import { store } from '../redux/store'
8 | import { Provider } from 'react-redux'
9 |
10 |
11 | function MyApp({ Component, pageProps }) {
12 |
13 | useEffect(() => {
14 | const script1 = document.createElement('script');
15 |
16 | script1.src = "https://unpkg.com/ionicons@5.5.2/dist/ionicons/ionicons.esm.js";
17 | script1.type = "module";
18 |
19 | document.body.appendChild(script1);
20 |
21 | const script = document.createElement('script');
22 |
23 | script.src = "https://unpkg.com/ionicons@5.5.2/dist/ionicons/ionicons.js";
24 | script.noModule = true;
25 |
26 | document.body.appendChild(script);
27 |
28 | return () => {
29 | document.body.removeChild(script1);
30 | document.body.removeChild(script);
31 | }
32 | }, []);
33 |
34 | return (
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | )
49 |
50 |
51 | }
52 |
53 |
54 | export default MyApp
55 |
--------------------------------------------------------------------------------
/pages/api/addComment.js:
--------------------------------------------------------------------------------
1 | import {BACKEND_URI} from '../../config/app'
2 | import cookie from 'cookie'
3 | import refreshToken from "./refresh_token"
4 |
5 | export default async(req,res)=>{
6 | if (req.method === "POST"){
7 |
8 | if (!req.headers.cookie){
9 |
10 | res.status(403).json({"message":"Not authorized"})
11 | return
12 | }
13 |
14 | let {refresh_token}=cookie.parse(req.headers.cookie)
15 |
16 | if (!refresh_token){
17 |
18 | res.status(403).json({"message":"Not authorized"})
19 | return
20 | }
21 |
22 |
23 | let {access_token}=cookie.parse(req.headers.cookie)
24 |
25 |
26 | if (!access_token){
27 | const refreshRes= await refreshToken(req,res)
28 |
29 |
30 | if(refreshRes){
31 | access_token=refreshRes
32 | }else{
33 |
34 | res.status(403).json({"message":"Not authorized"})
35 | return
36 | }
37 |
38 |
39 |
40 | }
41 |
42 | const body=JSON.parse(req.body)
43 |
44 |
45 |
46 |
47 | let resAPI = await fetch(`${BACKEND_URI}/courses/comment/${body.course_uuid}/`,{
48 | method:"POST",
49 | headers:{
50 | "Content-type":"application/json",
51 | "Authorization": `Token ${access_token}`
52 | },
53 | body:JSON.stringify(body.data)
54 |
55 | })
56 |
57 |
58 |
59 |
60 | if (resAPI.ok){
61 |
62 |
63 |
64 | res.status(200).json({})
65 |
66 |
67 | }else{
68 | // send error message
69 | res.status(403).json({})
70 | return
71 | }
72 |
73 | }else{
74 | res.setHeader("Allow",["GET"])
75 | res.status(403).json({"message":`Method ${req.method} not allowed`})
76 | return
77 | }
78 | }
--------------------------------------------------------------------------------
/pages/api/login.js:
--------------------------------------------------------------------------------
1 | import {BACKEND_URI} from '../../config/app'
2 | import cookie from 'cookie'
3 |
4 | export default async(req,res)=>{
5 | if (req.method === "POST"){
6 |
7 | const {email,password}=req.body
8 |
9 | const resAPI = await fetch(`${BACKEND_URI}/auth/jwt/create/`,{
10 |
11 | method:"POST",
12 | headers:{
13 | "Content-type":"application/json"
14 | },
15 | body:JSON.stringify({email,password})
16 | })
17 |
18 | const data= await resAPI.json()
19 |
20 |
21 | if (resAPI.ok){
22 | // set auth cookie
23 | res.setHeader("Set-Cookie",[cookie.serialize("refresh_token",data.refresh,{
24 | httpOnly:true,
25 | secure: process.env.NODE_ENV !== "development",
26 | maxAge: 60 * 60 * 24,
27 | sameSite:"strict",
28 | path: '/'
29 | }),
30 | cookie.serialize("access_token",data.access,{
31 | httpOnly:true,
32 | secure: process.env.NODE_ENV !== "development",
33 | maxAge: 60,
34 | sameSite:"strict",
35 | path: '/'
36 | })])
37 |
38 |
39 | // send success in response
40 | res.status(200).json({})
41 |
42 | }else{
43 | // send error message
44 |
45 | res.status(401).json(data)
46 | }
47 |
48 | }else{
49 | res.setHeader("Allow",["POST"])
50 | res.status(403).json({"message":`Method ${req.method} not allowed`})
51 | }
52 | }
--------------------------------------------------------------------------------
/pages/api/logout.js:
--------------------------------------------------------------------------------
1 |
2 | import cookie from 'cookie'
3 |
4 | export default async(req,res)=>{
5 | if (req.method === "POST"){
6 |
7 | // remove auth cookies
8 | res.setHeader("Set-Cookie",[cookie.serialize("refresh_token","",{
9 | httpOnly:true,
10 | secure: process.env.NODE_ENV !== "development",
11 | expires: new Date(0),
12 | sameSite:"strict",
13 | path: '/'
14 | },cookie.serialize("access_token","",{
15 | httpOnly:true,
16 | secure: process.env.NODE_ENV !== "development",
17 | expires: new Date(0),
18 | sameSite:"strict",
19 | path: '/'
20 | }))])
21 |
22 | res.status(204).json()
23 |
24 |
25 |
26 | }else{
27 | res.setHeader("Allow",["POST"])
28 | res.status(403).json({"message":`Method ${req.method} not allowed`})
29 | }
30 | }
--------------------------------------------------------------------------------
/pages/api/payment.js:
--------------------------------------------------------------------------------
1 | import {BACKEND_URI} from '../../config/app'
2 | import cookie from 'cookie'
3 | import refreshToken from "./refresh_token"
4 |
5 | export default async(req,res)=>{
6 | if (req.method === "POST"){
7 |
8 | console.log(req.body)
9 |
10 | if(!req.body||!req.body.cart||!req.body.cart.length){
11 |
12 | res.status(400).json({"message":"Cart must contain atleast one item"})
13 | return
14 | }
15 |
16 | if (!req.headers.cookie){
17 |
18 | res.status(403).json({"message":"Not authorized"})
19 | return
20 | }
21 |
22 | let {refresh_token}=cookie.parse(req.headers.cookie)
23 |
24 | if (!refresh_token){
25 |
26 | res.status(403).json({"message":"Not authorized"})
27 | return
28 | }
29 |
30 |
31 | let {access_token}=cookie.parse(req.headers.cookie)
32 |
33 |
34 | if (!access_token){
35 | const refreshRes= await refreshToken(req,res)
36 |
37 |
38 | if(refreshRes){
39 | access_token=refreshRes
40 | }else{
41 |
42 | res.status(403).json({"message":"Not authorized"})
43 | return
44 | }
45 |
46 |
47 |
48 | }
49 |
50 |
51 | const resAPI=await fetch(`${BACKEND_URI}/payments/`,{
52 | method:"POST",
53 | headers:{
54 | Authorization:`Token ${access_token}`,
55 | "Content-Type":"application/json"
56 | },
57 | body:JSON.stringify(req.body.cart)
58 | })
59 |
60 | if (resAPI.ok){
61 | const data=await resAPI.json()
62 | // console.log(data)
63 |
64 | res.status(200).json({url:data.url})
65 | }else{
66 | res.status(400).json({})
67 | }
68 |
69 |
70 |
71 |
72 |
73 | }else{
74 | res.setHeader("Allow",["POST"])
75 | res.status(403).json({"message":`Method ${req.method} not allowed`})
76 | }
77 | }
--------------------------------------------------------------------------------
/pages/api/refresh_token.js:
--------------------------------------------------------------------------------
1 | import {BACKEND_URI} from '../../config/app'
2 | import cookie from 'cookie'
3 |
4 | export default async(req,res)=>{
5 |
6 | if (req.method === "GET" || req.url=='/api/payment'|| req.url=='/api/addComment'){
7 |
8 | if (!req.headers.cookie){
9 |
10 | return
11 | }
12 |
13 | const {refresh_token}=cookie.parse(req.headers.cookie)
14 |
15 |
16 | if (!refresh_token){
17 |
18 | return
19 | }
20 |
21 | const resAPI = await fetch(`${BACKEND_URI}/auth/jwt/refresh/`,{
22 |
23 | method:"POST",
24 | headers:{
25 | "Content-type":"application/json",
26 | },
27 | body:JSON.stringify({"refresh":refresh_token})
28 |
29 | })
30 |
31 |
32 | if (resAPI.ok){
33 | const data= await resAPI.json()
34 |
35 | // set access cookie
36 | res.setHeader("Set-Cookie",cookie.serialize("access_token",data.access,{
37 | httpOnly:true,
38 | secure: process.env.NODE_ENV !== "development",
39 | maxAge: 60,
40 | sameSite:"strict",
41 | path: '/'
42 | }))
43 |
44 | return data.access
45 |
46 |
47 | }else{
48 |
49 | return
50 | }
51 |
52 | }else{
53 |
54 | return
55 | }
56 | }
--------------------------------------------------------------------------------
/pages/api/signup.js:
--------------------------------------------------------------------------------
1 | import {BACKEND_URI} from '../../config/app'
2 |
3 |
4 | export default async(req,res)=>{
5 | if (req.method === "POST"){
6 |
7 | const {email,password,name}=req.body
8 |
9 | const resAPI = await fetch(`${BACKEND_URI}/auth/users/`,{
10 |
11 | method:"POST",
12 | headers:{
13 | "Content-type":"application/json"
14 | },
15 | body:JSON.stringify({email,password,name})
16 | })
17 |
18 | const data= await resAPI.json()
19 |
20 |
21 | if (resAPI.ok){
22 |
23 | res.status(200).json(data)
24 | return
25 |
26 | }else{
27 | // send error message
28 | res.status(401).json(data)
29 | return
30 | }
31 |
32 | }else{
33 | res.setHeader("Allow",["POST"])
34 | res.status(403).json({"message":`Method ${req.method} not allowed`})
35 | }
36 | }
--------------------------------------------------------------------------------
/pages/api/user.js:
--------------------------------------------------------------------------------
1 | import {BACKEND_URI} from '../../config/app'
2 | import cookie from 'cookie'
3 | import refreshToken from "./refresh_token"
4 |
5 | export default async(req,res)=>{
6 | //console.log('running')
7 | if (req.method === "GET"){
8 | //console.log(1)
9 | if (!req.headers.cookie){
10 | //console.log(2)
11 | res.status(403).json({"message":"Not authorized"})
12 | return
13 | }
14 |
15 | let {refresh_token}=cookie.parse(req.headers.cookie)
16 |
17 | if (!refresh_token){
18 | //console.log(3)
19 | res.status(403).json({"message":"Not authorized"})
20 | return
21 | }
22 |
23 |
24 | let {access_token}=cookie.parse(req.headers.cookie)
25 |
26 |
27 | if (!access_token){
28 | const refreshRes= await refreshToken(req,res)
29 | //console.log(4)
30 |
31 | if(refreshRes){
32 | access_token=refreshRes
33 | }else{
34 |
35 | res.status(403).json({"message":"Not authorized"})
36 | return
37 | }
38 |
39 |
40 |
41 | }
42 |
43 |
44 |
45 | let resAPI = await fetch(`${BACKEND_URI}/auth/users/me/`,{
46 | method:"GET",
47 | headers:{
48 | "Content-type":"application/json",
49 | "Authorization": `Token ${access_token}`
50 | },
51 |
52 | })
53 | //console.log(5)
54 |
55 | if (resAPI.ok){
56 | //console.log(6)
57 | const user= await resAPI.json()
58 |
59 | // send user in response
60 | res.status(200).json({user})
61 | return user
62 |
63 | }else{
64 | // send error message
65 | res.status(403).json({})
66 | return
67 | }
68 |
69 | }else{
70 | res.setHeader("Allow",["GET"])
71 | res.status(403).json({"message":`Method ${req.method} not allowed`})
72 | return
73 | }
74 | }
--------------------------------------------------------------------------------
/pages/auth/login.js:
--------------------------------------------------------------------------------
1 | import React, { useContext, useEffect, useState } from 'react'
2 | import MainLayout from '../../components/layouts/main'
3 | import FormInput from '../../components/auth/FormInput'
4 | import AuthBtn from '../../components/auth/AuthBtn'
5 | import Link from 'next/link'
6 | import AuthContext from '../../context/AuthContext'
7 | import { toast } from 'react-toastify';
8 | import cookie from 'cookie'
9 |
10 | const login = () => {
11 |
12 | const [email,setEmail]= useState('')
13 | const [password,setPassword]=useState('')
14 | const [authReady,setAuthReady]=useState(false)
15 |
16 | const {login,authError,clearUser}= useContext(AuthContext)
17 |
18 | useEffect(()=>clearUser(),[])
19 |
20 | const handleSubmit=async(e)=>{
21 | e.preventDefault();
22 |
23 | setAuthReady(true)
24 | await login({email,password})
25 | setAuthReady(false)
26 |
27 |
28 | }
29 |
30 | useEffect(()=>authError && toast.error(authError),[authError])
31 |
32 | return (
33 |
34 |
35 |
36 |
37 |
38 | Log In and Start Learning!
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | Do not have an account? Sign Up
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | )
61 | }
62 |
63 | export const getServerSideProps=async({req,res})=>{
64 |
65 | if (req.headers.cookie){
66 |
67 | let cookies=cookie.parse(req.headers.cookie)
68 |
69 | if (cookies && cookies.refresh_token){
70 |
71 |
72 | return {
73 | redirect:{
74 | destination:'/',
75 | permanent:false
76 | }
77 | }
78 | }
79 | }
80 |
81 |
82 | return {
83 | props:{}
84 | }
85 |
86 | }
87 |
88 | export default login
89 |
--------------------------------------------------------------------------------
/pages/auth/signup.js:
--------------------------------------------------------------------------------
1 | import React, { useContext, useEffect, useState } from 'react'
2 | import MainLayout from '../../components/layouts/main'
3 | import FormInput from '../../components/auth/FormInput'
4 | import AuthBtn from '../../components/auth/AuthBtn'
5 | import Link from 'next/link'
6 | import AuthContext from '../../context/AuthContext'
7 | import { toast } from 'react-toastify'
8 | import cookie from 'cookie'
9 |
10 | const signup = () => {
11 |
12 | const {signup,authError,clearUser}= useContext(AuthContext)
13 |
14 | useEffect(()=>clearUser(),[])
15 |
16 | const [name,setName]=useState('')
17 | const [email,setEmail]=useState('')
18 | const [password,setPassword]=useState('')
19 | const [authReady,setAuthReady]=useState(false)
20 |
21 | useEffect(()=>authError && toast.error(authError),[authError])
22 |
23 |
24 | const handleSubmit=async(e)=>{
25 | e.preventDefault()
26 | setAuthReady(true)
27 | await signup({name,email,password})
28 | setAuthReady(false)
29 | }
30 |
31 | return (
32 |
33 |
34 |
35 |
36 | Sign Up and Start Learning!
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | By signing up, you agree to our Terms of Use and Privacy Policy.
50 |
51 |
52 |
53 | Already have an account? Log In
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | )
62 | }
63 |
64 | export const getServerSideProps=async({req,res})=>{
65 |
66 | if(req.headers.cookie){
67 |
68 | let cookies=cookie.parse(req.headers.cookie)
69 |
70 | if (cookies && cookies.refresh_token){
71 |
72 |
73 | return {
74 | redirect:{
75 | destination:'/',
76 | permanent:false
77 | }
78 | }
79 | }
80 | }
81 |
82 |
83 | return {
84 | props:{}
85 | }
86 |
87 |
88 |
89 | }
90 |
91 |
92 | export default signup
93 |
--------------------------------------------------------------------------------
/pages/cart/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { useContext, useEffect, useState } from 'react'
2 | import MainLayout from '../../components/layouts/main'
3 | import CartItem from '../../components/cart/CartItem'
4 | import NoItemCart from '../../components/cart/NoItemCart'
5 | import CartContext from '../../context/CartContext'
6 | import { BACKEND_URI, NEXT_BACKEND_URI } from '../../config/app'
7 | import { useRouter } from 'next/router'
8 | import { toast } from 'react-toastify'
9 |
10 | function CartIndex() {
11 | // const [cartSize,setCartSize]=useState(0)
12 |
13 | const {cart,removeCart,clearCart} = useContext(CartContext)
14 | const [cartTotal,setCartTotal]=useState()
15 | const [cartReady,setCartReady] = useState(false)
16 | const [requestingPayment, setRequestingPayment] = useState(false)
17 |
18 | const [cartDetails,setCartDetails]= useState([])
19 |
20 | const router = useRouter()
21 |
22 | const handleCheckout=async()=>{
23 | if(cart.length){
24 |
25 | setRequestingPayment(true)
26 | const res= await fetch(`${NEXT_BACKEND_URI}/payment/`,{
27 | method:'POST',
28 | headers:{
29 | "Content-Type":"application/json",
30 |
31 | },
32 | body:JSON.stringify({cart})
33 |
34 | })
35 | if (res.ok){
36 |
37 | const data = await res.json()
38 | toast.success("Redirecting you to payment page...")
39 | window.location=data.url
40 |
41 |
42 | }
43 | if(res.status==403){
44 | setRequestingPayment(false)
45 | toast.error("Please login to proceed!!")
46 | await router.push('/auth/login')
47 | }
48 | if(!res.ok){
49 | setRequestingPayment(false)
50 | toast.error("Sorry there was an error!!")
51 | }
52 | }
53 | }
54 |
55 | useEffect(async()=>{
56 | if(cart.length >0){
57 |
58 | const res = await fetch(`${BACKEND_URI}/courses/cart/`,{
59 | method:'POST',
60 | headers:{
61 | 'Content-type':'application/json'
62 | },
63 | body:JSON.stringify({cart}),
64 |
65 |
66 | })
67 |
68 |
69 |
70 | if(res.ok){
71 |
72 | const details = await res.json()
73 | setCartTotal(details.cart_total)
74 | setCartDetails([...details.cart_detail])
75 | setCartReady(true)
76 |
77 | }
78 |
79 |
80 | }
81 |
82 | },[])
83 |
84 | const removeCartDetail=(uuid)=>{
85 |
86 | const index = cartDetails.findIndex(detail=>detail.course_uuid===uuid)
87 |
88 | if(index > -1){
89 |
90 | const currentCart=[...cartDetails]
91 |
92 |
93 |
94 | currentCart.splice(index,1)
95 |
96 |
97 |
98 | setCartDetails([...currentCart])
99 |
100 |
101 | }
102 |
103 | }
104 |
105 | return (
106 |
107 |
108 |
109 | Shopping Cart
110 |
111 |
112 |
113 |
114 |
115 |
116 | {cart.length} Course in Cart
117 |
118 |
119 | {cart.length > 0 && cartReady ? (
120 | <>
121 | {
122 | cartDetails.length ? cart.map(item=>{
123 |
124 | const courseItem=cartDetails.find(detail=>item==detail.course_uuid)
125 |
126 | return ( )
127 | }):''
128 | }
129 |
130 | >
131 | ) :
132 |
133 |
134 | }
135 |
136 |
137 |
138 |
139 |
140 | {cart.length > 0 &&
141 |
142 | Total:
143 |
144 |
145 | ${cartTotal}
146 |
147 |
148 | { requestingPayment? "Please wait ...": "Checkout"}
149 |
150 |
151 | Clear Cart
152 |
153 | }
154 |
155 |
156 |
157 |
158 |
159 | )
160 | }
161 |
162 | export default CartIndex
163 |
--------------------------------------------------------------------------------
/pages/course/author/[course_uuid]/manage/delete.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const deleteCourse = () => {
4 | return (
5 |
6 |
7 |
8 | )
9 | }
10 |
11 | export default deleteCourse
12 |
--------------------------------------------------------------------------------
/pages/course/author/[course_uuid]/manage/update.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const update = () => {
4 | return (
5 |
6 |
7 |
8 | )
9 | }
10 |
11 | export default update
12 |
--------------------------------------------------------------------------------
/pages/course/author/course_add.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const course_add = () => {
4 | return (
5 |
6 |
7 |
8 | )
9 | }
10 |
11 | export default course_add
12 |
--------------------------------------------------------------------------------
/pages/course/author/course_list.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const course_list = () => {
4 | return (
5 |
6 |
7 |
8 | )
9 | }
10 |
11 | export default course_list
12 |
--------------------------------------------------------------------------------
/pages/course/detail/[course_uuid].js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import MainLayout from '../../../components/layouts/main'
3 | import Banner from '../../../components/courses/detail/Banner'
4 | import WhatLearnt from '../../../components/courses/detail/WhatLearnt'
5 | import CourseDetail from '../../../components/courses/detail/CourseDetail'
6 | import Description from '../../../components/courses/detail/Description'
7 | import FeedBack from '../../../components/courses/detail/FeedBack'
8 | import { BACKEND_URI } from '../../../config/app'
9 |
10 |
11 | const course_uuid = ({data}) => {
12 |
13 | const banner_data={title:data.title,
14 | description:data.description,
15 | rating:data.student_rating,
16 | number_of_rating:data.student_rating_no,
17 | author:data.author.name,
18 | price:data.price,
19 | student_no:data.student_no
20 | }
21 | return (
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | )
32 | }
33 |
34 | export const getServerSideProps=async({req,res,query:{course_uuid}})=>{
35 | const resAPI=await fetch(`${BACKEND_URI}/courses/detail/${course_uuid}/`)
36 |
37 |
38 | if (!resAPI.ok){
39 | return {
40 | redirect:{
41 | destination:"/",
42 | permanent:false
43 | }
44 | }
45 | }
46 |
47 | const data=await resAPI.json()
48 |
49 |
50 | return {
51 | props:{data}
52 | }
53 |
54 | }
55 |
56 | export default course_uuid
57 |
--------------------------------------------------------------------------------
/pages/course/sector/[sector_uuid].js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import MainLayout from '../../../components/layouts/main'
3 | import CourseListCard from '../../../components/courses/list/CourseListCard'
4 | import { BACKEND_URI } from '../../../config/app'
5 |
6 | const sector_uuid = ({data:{sector_name,data:courses,total_students}}) => {
7 |
8 |
9 | return (
10 |
11 |
12 |
13 | {sector_name[0].toUpperCase()+sector_name.slice(1,sector_name.length)} Courses
14 |
15 |
16 |
17 |
18 |
19 | {total_students} learners
20 |
21 |
22 |
23 |
24 |
25 |
26 | All {sector_name[0].toUpperCase()+sector_name.slice(1,sector_name.length)} Courses
27 |
28 |
29 | {sector_name[0].toUpperCase()+sector_name.slice(1,sector_name.length)} instructors on Udemy specialize in everything from software development to data analysis, and are known for their effective, friendly instruction for students of all levels.
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | Not sure? All courses have a 30-day money-back guarantee
39 |
40 |
41 |
42 |
43 |
44 |
45 | {
46 | courses.map(course=>
47 |
48 | ( )
49 | )
50 | }
51 | {/*
52 | */}
53 |
54 |
55 |
56 | )
57 | }
58 |
59 | export const getServerSideProps=async({req,res,query:{sector_uuid}})=>{
60 | const resAPI=await fetch(`${BACKEND_URI}/courses/${sector_uuid}/`)
61 |
62 |
63 | if (!resAPI.ok){
64 | return {
65 | redirect:{
66 | destination:'/',
67 | permanent:false
68 | }
69 | }
70 | }
71 |
72 | const data=await resAPI.json()
73 |
74 | // console.log(data)
75 |
76 |
77 | return {
78 | props:{data}
79 | }
80 |
81 | }
82 |
83 | export default sector_uuid
84 |
--------------------------------------------------------------------------------
/pages/course/study/[course_uuid].js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import WatchLayout from "../../../components/layouts/watch"
3 | import WatchArea from '../../../components/courses/study/WatchArea'
4 | import CommentArea from '../../../components/courses/study/CommentArea'
5 | import cookie from 'cookie'
6 | import refreshToken from "../../api/refresh_token"
7 | import { BACKEND_URI } from '../../../config/app'
8 |
9 |
10 | const course_uuid = ({data}) => {
11 | const [comments,setComments]=useState(data.comment)
12 | const [title,setTitle]=useState(data.title)
13 | return (
14 |
15 |
16 |
17 |
18 |
19 | )
20 | }
21 |
22 | export const getServerSideProps=async({req,res,query:{course_uuid}})=>{
23 |
24 |
25 | if (!req.headers.cookie){
26 |
27 |
28 | return {
29 | redirect:{
30 | destination:'/auth/login',
31 | permanent:false
32 | }
33 | }
34 |
35 | }
36 |
37 | let {refresh_token}=cookie.parse(req.headers.cookie)
38 |
39 | if (!refresh_token){
40 |
41 |
42 | return {
43 | redirect:{
44 | destination:'/auth/login',
45 | permanent:false
46 | }
47 | }
48 | }
49 |
50 |
51 |
52 | let {access_token}=cookie.parse(req.headers.cookie)
53 |
54 |
55 | if (!access_token){
56 | const refreshRes= await refreshToken(req,res)
57 |
58 |
59 | if(refreshRes){
60 | access_token=refreshRes
61 | }else{
62 |
63 |
64 | return {
65 | redirect:{
66 | destination:'/auth/login',
67 | permanent:false
68 | }
69 | }
70 | }
71 |
72 |
73 |
74 | }
75 |
76 | let resAPI = await fetch(`${BACKEND_URI}/courses/study/${course_uuid}/`,{
77 | method:"GET",
78 | headers:{
79 | "Content-type":"application/json",
80 | "Authorization": `Token ${access_token}`
81 | }
82 |
83 | })
84 |
85 |
86 | if (resAPI.ok){
87 | const data= await resAPI.json()
88 |
89 | // console.log(data)
90 |
91 | // send data in response
92 |
93 | return {
94 | props:{data}
95 | }
96 |
97 | }else{
98 | // send error message
99 |
100 |
101 | return {
102 | redirect:{
103 | destination:'/',
104 | permanent:false
105 | }
106 | }
107 | }
108 |
109 |
110 |
111 | }
112 |
113 | export default course_uuid
114 |
--------------------------------------------------------------------------------
/pages/index.jsx:
--------------------------------------------------------------------------------
1 | import Head from 'next/head'
2 | import Image from 'next/image'
3 | import MainLayout from '../components/layouts/main'
4 | import Header from '../components/index/Header'
5 | import CourseSuggest from '../components/index/CourseSuggest'
6 | import CourseList from '../components/index/CourseList'
7 | import CategoryList from '../components/index/CategoryList'
8 | import TeachUdemy from '../components/index/TeachUdemy'
9 | import TrustedCompanies from '../components/index/TrustedCompanies'
10 | import { BACKEND_URI } from '../config/app'
11 |
12 |
13 |
14 |
15 | export default function Home({data}) {
16 |
17 |
18 | return (
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | )
34 | }
35 |
36 | export const getServerSideProps=async({req,res})=>{
37 | const resAPI=await fetch(`${BACKEND_URI}/courses/`)
38 |
39 | if (!resAPI.ok){
40 | return {
41 | props:{}
42 | }
43 | }
44 |
45 | const data=await resAPI.json()
46 |
47 |
48 |
49 | return {
50 | props:{data}
51 | }
52 |
53 | }
54 |
55 |
--------------------------------------------------------------------------------
/pages/search/[term].jsx:
--------------------------------------------------------------------------------
1 | import Head from 'next/head'
2 | import Image from 'next/image'
3 | import { useRouter } from 'next/router'
4 | import MainLayout from '../../components/layouts/main'
5 | import CourseListCard from '../../components/courses/list/CourseListCard'
6 | import { BACKEND_URI } from '../../config/app'
7 |
8 |
9 | export default function Search({data}) {
10 | const router=useRouter()
11 | const {query:{term}}=router
12 |
13 |
14 |
15 | return (
16 |
17 |
18 |
19 |
20 | Courses on terms "{
21 | term
22 | }"
23 |
24 |
25 |
26 | {data && data.length ?
27 |
28 | {
29 | data.map(course=>
30 |
31 | ( )
32 | )
33 | }
34 |
35 |
36 | :
37 |
38 |
39 |
40 | No Course found on the provided term "{term}"
41 |
42 |
43 | }
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | )
55 | }
56 |
57 | export const getServerSideProps=async({req,res,query:{term}})=>{
58 | const resAPI=await fetch(`${BACKEND_URI}/courses/search/${term}/`)
59 |
60 |
61 | // if (!resAPI.ok){
62 | // return {
63 | // redirect:{
64 | // destination:'/',
65 | // permanent:false
66 | // }
67 | // }
68 | // }
69 |
70 | const data=await resAPI.json()
71 |
72 | // console.log(data)
73 |
74 |
75 | return {
76 | props:{data}
77 | }
78 |
79 | }
--------------------------------------------------------------------------------
/pages/user.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState,useEffect, useContext } from 'react'
2 | import MainLayout from '../components/layouts/main'
3 | import CourseItem from '../components/user/courseItem'
4 | import AuthContext from '../context/AuthContext'
5 | import { BACKEND_URI } from '../config/app'
6 | import cookie from 'cookie'
7 | import refreshToken from './api/refresh_token'
8 |
9 | function user() {
10 |
11 | const [courseDetail,setCourseDetail]=useState([])
12 | const [courseReady,setCourseReady]=useState(false)
13 |
14 | const {user:{courses}}=useContext(AuthContext)
15 |
16 | useEffect(async()=>{
17 | if(courses.length >0){
18 |
19 | const res = await fetch(`${BACKEND_URI}/courses/cart/`,{
20 | method:'POST',
21 | headers:{
22 | 'Content-type':'application/json'
23 | },
24 | body:JSON.stringify({cart:courses}),
25 |
26 |
27 | })
28 |
29 |
30 |
31 | if(res.ok){
32 |
33 | const details = await res.json()
34 |
35 | setCourseDetail([...details.cart_detail])
36 |
37 | setCourseReady(true)
38 |
39 | }
40 |
41 |
42 | }
43 |
44 | },[])
45 |
46 | return (
47 |
48 |
49 |
50 | Your Courses
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | {courses.length} Course
59 |
60 |
61 | {courseDetail.length > 0 && courseReady ? (
62 | <>
63 | {
64 | courseDetail.length ? courses.map((item,index)=>{
65 |
66 |
67 | const obj=courseDetail.find(detail=>item==detail.course_uuid)
68 |
69 |
70 | return ( )
71 | }):''
72 | }
73 |
74 | >
75 | ) :
76 |
77 | You have no courses yet
78 | }
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 | )
88 | }
89 |
90 | export const getServerSideProps=async({req,res,query:{course_uuid}})=>{
91 |
92 |
93 | if (!req.headers.cookie){
94 |
95 |
96 | return {
97 | redirect:{
98 | destination:'/auth/login',
99 | permanent:false
100 | }
101 | }
102 |
103 | }
104 |
105 | let {refresh_token}=cookie.parse(req.headers.cookie)
106 |
107 | if (!refresh_token){
108 |
109 |
110 | return {
111 | redirect:{
112 | destination:'/auth/login',
113 | permanent:false
114 | }
115 | }
116 | }
117 |
118 |
119 |
120 | let {access_token}=cookie.parse(req.headers.cookie)
121 |
122 |
123 | if (!access_token){
124 | const refreshRes= await refreshToken(req,res)
125 |
126 |
127 | if(refreshRes){
128 | access_token=refreshRes
129 | }else{
130 |
131 |
132 | return {
133 | redirect:{
134 | destination:'/auth/login',
135 | permanent:false
136 | }
137 | }
138 | }
139 |
140 |
141 |
142 | }
143 |
144 |
145 |
146 |
147 | return {
148 | props:{}
149 | }
150 |
151 |
152 |
153 |
154 | }
155 |
156 |
157 | export default user
158 |
--------------------------------------------------------------------------------
/public/assets/pexels-louis.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OtchereDev/Udemy_Nextjs/8dd386999b444b0f7a175b68163146406a9c4e47/public/assets/pexels-louis.jpg
--------------------------------------------------------------------------------
/public/assets/udemy-b1.svg:
--------------------------------------------------------------------------------
1 | booking
--------------------------------------------------------------------------------
/public/assets/udemy-b2.svg:
--------------------------------------------------------------------------------
1 | volkswagen
--------------------------------------------------------------------------------
/public/assets/udemy-b3.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
8 |
52 |
53 |
--------------------------------------------------------------------------------
/public/assets/udemy-b4.svg:
--------------------------------------------------------------------------------
1 | adidas
--------------------------------------------------------------------------------
/public/assets/udemy-b5.svg:
--------------------------------------------------------------------------------
1 | eventbrite
--------------------------------------------------------------------------------
/public/assets/udemy-red-white.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 | Logo
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/public/assets/udemy1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OtchereDev/Udemy_Nextjs/8dd386999b444b0f7a175b68163146406a9c4e47/public/assets/udemy1.jpg
--------------------------------------------------------------------------------
/public/assets/udemy_logo-red.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
9 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/public/assets/udemy_logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/public/assets/udemy_teacher.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OtchereDev/Udemy_Nextjs/8dd386999b444b0f7a175b68163146406a9c4e47/public/assets/udemy_teacher.jpg
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OtchereDev/Udemy_Nextjs/8dd386999b444b0f7a175b68163146406a9c4e47/public/favicon.ico
--------------------------------------------------------------------------------
/redux/slices/AuthSlices.js:
--------------------------------------------------------------------------------
1 | import { createSlice } from '@reduxjs/toolkit'
2 |
3 | const initialState = {
4 | user:null
5 | }
6 |
7 | export const authSlice = createSlice({
8 | name: 'auth',
9 | initialState,
10 | reducers: {
11 | addUser: (state,action) => {
12 | // console.log(action.payload)
13 | // if(!action.payload===undefined){
14 | // console.log('i happen')
15 | // }
16 | state.user = action.payload
17 | },
18 | removeUser: (state) => {
19 | state.user = null
20 | }
21 | },
22 | })
23 |
24 | // Action creators are generated for each case reducer function
25 | export const { addUser,removeUser } = authSlice.actions
26 |
27 | export default authSlice.reducer
--------------------------------------------------------------------------------
/redux/store.js:
--------------------------------------------------------------------------------
1 | import { configureStore } from '@reduxjs/toolkit'
2 | import AuthReducer from "./slices/AuthSlices"
3 |
4 | export const store = configureStore({
5 | reducer: {
6 | auth:AuthReducer
7 | },
8 | })
--------------------------------------------------------------------------------