├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ ├── documentation.yml │ ├── feature_request.yml │ ├── other.yml │ ├── refactor.yml │ └── task-request.yml ├── pull_request_template.md └── workflows │ ├── autocomment-iss-close.yml │ ├── close-old-issue.yml │ └── close-old-pr.yml ├── .gitignore ├── .vscode └── settings.json ├── CODE_OF_CONDUCT.md ├── Create Contributing.md ├── LICENSE ├── README.md ├── images ├── architecture.png ├── mainpage.png └── schema.png ├── package-lock.json ├── package.json ├── public ├── favicon.png ├── index.html └── robots.txt ├── server ├── .gitignore ├── config │ ├── cloudinary.js │ ├── database.js │ └── razorpay.js ├── controllers │ ├── Auth.js │ ├── Category.js │ ├── ContactUs.js │ ├── Course.js │ ├── Payments.js │ ├── Profile.js │ ├── RatingAndReview.js │ ├── ResetPassword.js │ ├── Section.js │ ├── Subsection.js │ └── courseProgress.js ├── index.js ├── mail │ └── templates │ │ ├── contactFormRes.js │ │ ├── courseEnrollmentEmail.js │ │ ├── emailVerificationTemplate.js │ │ ├── passwordUpdate.js │ │ └── paymentSuccessEmail.js ├── middlewares │ └── auth.js ├── models │ ├── Category.js │ ├── Course.js │ ├── CourseProgress.js │ ├── OTP.js │ ├── Profile.js │ ├── RatingAndRaview.js │ ├── Section.js │ ├── SubSection.js │ └── User.js ├── package-lock.json ├── package.json ├── routes │ ├── Contact.js │ ├── Course.js │ ├── Payments.js │ ├── Profile.js │ └── User.js └── utils │ ├── imageUploader.js │ ├── mailSender.js │ └── secToDuration.js ├── src ├── .DS_Store ├── App.css ├── App.js ├── assets │ ├── Images │ │ ├── 404.png │ │ ├── Compare_with_others.png │ │ ├── Compare_with_others.svg │ │ ├── FoundingStory.png │ │ ├── Instructor.png │ │ ├── Know_your_progress.png │ │ ├── Know_your_progress.svg │ │ ├── Plan_your_lessons.png │ │ ├── Plan_your_lessons.svg │ │ ├── TimelineImage.png │ │ ├── aboutus1.webp │ │ ├── aboutus2.webp │ │ ├── aboutus3.webp │ │ ├── ads_click_24dp_FFFFFF_FILL0_wght400_GRAD0_opsz24 (2).svg │ │ ├── banner.mp4 │ │ ├── bghome.svg │ │ ├── boxoffice.png │ │ ├── frame.png │ │ ├── image1.png │ │ ├── image2.jpg │ │ ├── image3.webp │ │ ├── image4.jpg │ │ ├── image5.jpg │ │ ├── image6.jpg │ │ ├── image7.jpeg │ │ ├── image8.webp │ │ ├── image9.webp │ │ ├── loaderbg.jpg │ │ ├── login.webp │ │ ├── logo.png │ │ ├── preloader.gif │ │ └── signup.webp │ ├── Logo │ │ ├── Logo-Full-Dark.png │ │ ├── Logo-Full-Light.png │ │ ├── Logo-Small-Dark.png │ │ ├── Logo-Small-Light.png │ │ └── rzp_logo.png │ └── TimeLineLogo │ │ ├── Logo1.svg │ │ ├── Logo2.svg │ │ ├── Logo3.svg │ │ └── Logo4.svg ├── components │ ├── ContactPage │ │ ├── ContactDetails.jsx │ │ ├── ContactForm.jsx │ │ └── ContactUsForm.jsx │ ├── common │ │ ├── BackToTop.jsx │ │ ├── ConfirmationModal.jsx │ │ ├── FAQ.jsx │ │ ├── Footer.jsx │ │ ├── IconBtn.jsx │ │ ├── Loading.jsx │ │ ├── Navbar.jsx │ │ ├── RatingStars.jsx │ │ ├── ReviewSlider.jsx │ │ ├── Tab.jsx │ │ ├── progess.css │ │ └── progressbar.jsx │ └── core │ │ ├── AboutPage │ │ ├── ContactFormSection.jsx │ │ ├── LearningGrid.jsx │ │ ├── Quote.jsx │ │ ├── Stats.jsx │ │ └── useIntersectionObserver.js │ │ ├── Auth │ │ ├── LoginForm.jsx │ │ ├── OpenRoute.jsx │ │ ├── PrivateRoute.jsx │ │ ├── ProfileDropDown.jsx │ │ ├── SignupForm.jsx │ │ └── Template.jsx │ │ ├── Catalog │ │ ├── CourseSlider.jsx │ │ └── Course_Card.jsx │ │ ├── Course │ │ ├── CourseAccordionBar.jsx │ │ ├── CourseDetailsCard.js │ │ └── CourseSubSectionAccordion.jsx │ │ ├── Dashboard │ │ ├── AddCourse │ │ │ ├── CourseBuilder │ │ │ │ ├── CourseBuilderForm.jsx │ │ │ │ ├── NestedView.jsx │ │ │ │ └── SubSectionModal.jsx │ │ │ ├── CourseInformation │ │ │ │ ├── ChipInput.jsx │ │ │ │ ├── CourseInformationForm.jsx │ │ │ │ └── RequirementField.jsx │ │ │ ├── PublishCourse │ │ │ │ └── index.jsx │ │ │ ├── RenderSteps.jsx │ │ │ ├── Upload.jsx │ │ │ └── index.jsx │ │ ├── Cart │ │ │ ├── RenderCartCourses.jsx │ │ │ ├── RenderTotalAmount.jsx │ │ │ └── index.jsx │ │ ├── EditCourse │ │ │ └── index.js │ │ ├── EnrolledCourses.jsx │ │ ├── InstructorCourses │ │ │ └── CoursesTable.jsx │ │ ├── InstructorDashboard │ │ │ ├── Instructor.jsx │ │ │ └── InstructorChart.jsx │ │ ├── MyCourses.jsx │ │ ├── MyProfile.jsx │ │ ├── Settings │ │ │ ├── ChangeProfilePicture.jsx │ │ │ ├── DeleteAccount.jsx │ │ │ ├── EditProfile.jsx │ │ │ ├── UpdatePassword.jsx │ │ │ └── index.jsx │ │ ├── Sidebar.jsx │ │ └── SidebarLink.jsx │ │ ├── HomePage │ │ ├── Button.jsx │ │ ├── CodeBlocks.jsx │ │ ├── CourseCard.jsx │ │ ├── ExploreMore.jsx │ │ ├── HighlightText.jsx │ │ ├── InstructorSection.jsx │ │ ├── LearningLanguageSection.jsx │ │ └── TimelineSection.jsx │ │ └── ViewCourse │ │ ├── CourseReviewModal.jsx │ │ ├── VideoDetails.jsx │ │ └── VideoDetailsSidebar.jsx ├── data │ ├── countrycode.json │ ├── dashboard-links.js │ ├── footer-links.js │ ├── homepage-explore.js │ └── navbar-links.js ├── hooks │ ├── useOnClickOutside.js │ └── useRouteMatch.js ├── index.css ├── index.js ├── pages │ ├── About.jsx │ ├── Catalog.jsx │ ├── Chatbot.jsx │ ├── Contact.jsx │ ├── CourseDetails.jsx │ ├── Dashboard.jsx │ ├── Error.jsx │ ├── ForgotPassword.jsx │ ├── Home.jsx │ ├── Login.jsx │ ├── Project.jsx │ ├── Rateus.jsx │ ├── Rating.css │ ├── Signup.jsx │ ├── TermsAndConditions.jsx │ ├── UpdatePassword.jsx │ ├── VerifyEmail.jsx │ ├── ViewCourse.jsx │ └── privacypolicy.jsx ├── reducer │ └── index.js ├── services │ ├── apiconnector.js │ ├── apis.js │ ├── formatDate.js │ └── operations │ │ ├── SettingsAPI.js │ │ ├── authAPI.js │ │ ├── courseDetailsAPI.js │ │ ├── pageAndComponentData.js │ │ ├── profileAPI.js │ │ └── studentFeaturesAPI.js ├── slices │ ├── authSlice.js │ ├── cartSlice.js │ ├── courseSlice.js │ ├── profileSlice.js │ └── viewCourseSlice.js └── utils │ ├── avgRating.js │ ├── constants.js │ └── dateFormatter.js ├── tailwind.config.js └── vercel.json /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: ​🐞 Bug 2 | description: Report an issue to help us improve the project. 3 | title: "BUG REPORT" 4 | labels: ["bug", "goal: fix"] 5 | body: 6 | - type: textarea 7 | attributes: 8 | label: Description 9 | description: A brief description of the issue or bug you are facing, include what you tried and what didn't work 10 | validations: 11 | required: false 12 | - type: textarea 13 | attributes: 14 | label: Screenshots 15 | description: Add screenshots or videos if applicable 16 | validations: 17 | required: false 18 | - type: textarea 19 | attributes: 20 | label: Additional information 21 | description: Any additional information or anything we should know about this bug? 22 | validations: 23 | required: false 24 | - type: dropdown 25 | attributes: 26 | label: What browser are you seeing this bug on? 27 | multiple: true 28 | options: 29 | - Firefox 30 | - Chrome 31 | - Safari 32 | - Microsoft Edge 33 | - type: checkboxes 34 | id: no-duplicate-issues 35 | attributes: 36 | label: "Checklist" 37 | options: 38 | - label: "I have checked the existing issues" 39 | required: true 40 | 41 | - label: "I have read the Contributing Guidelines" 42 | required: true 43 | 44 | - label: "I have read the Code Of Conduct" 45 | required: true 46 | 47 | - label: "The changes don't break the code" 48 | required: true 49 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/documentation.yml: -------------------------------------------------------------------------------- 1 | name: "📝 Documentation" 2 | description: "Use this format to suggest improvements in our docs" 3 | title: "📝[DOCS] <suggestion title>" 4 | labels: ["documentation", "goal: enhancement"] 5 | 6 | body: 7 | - type: textarea 8 | id: docs_description 9 | attributes: 10 | label: "Issue Description" 11 | description: "A brief summary of the documentation issue you would like to address." 12 | validations: 13 | required: true 14 | 15 | - type: textarea 16 | id: screenshots_examples_docs 17 | attributes: 18 | label: "Screenshots or Examples (if applicable)" 19 | description: "Please add relevant screenshots or examples to illustrate the problem." 20 | 21 | - type: checkboxes 22 | id: terms_checklist_docs 23 | attributes: 24 | label: "Checklist" 25 | description: "By submitting this issue, you agree to follow our Code of Conduct" 26 | options: 27 | - label: "I have checked the existing issues." 28 | required: true 29 | 30 | - label: "I have read the Contributing Guidelines." 31 | required: true 32 | 33 | - label: "I have read the Code Of Conduct." 34 | required: true 35 | 36 | - label: "The changes doesn't break the code" 37 | required: true 38 | 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: 💡 Feature Request 2 | description: Suggest an interesting feature idea for this project 3 | title: "💡[FEATURE] <title>" 4 | labels: ["enhancement", "goal: addition"] 5 | body: 6 | - type: textarea 7 | id: description 8 | attributes: 9 | label: Description 10 | description: A clear and concise description of features you want to add. 11 | validations: 12 | required: true 13 | - type: textarea 14 | id: screenshots 15 | attributes: 16 | label: Screenshots 17 | description: Include relevant screenshots if applicable 18 | validations: 19 | required: false 20 | - type: checkboxes 21 | id: no-duplicate-issues 22 | attributes: 23 | label: "Checklist" 24 | options: 25 | - label: "I have checked the existing issues." 26 | required: true 27 | 28 | - label: "I have read the Contributing Guidelines" 29 | required: true 30 | 31 | - label: "I have read the Code Of Conduct" 32 | required: true 33 | 34 | - label: "The changes don't break the code" 35 | required: true 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/other.yml: -------------------------------------------------------------------------------- 1 | name: Other 2 | description: Use this for any other question or issue. Do not create blank issues 3 | title: "[OTHER]<title>" 4 | labels: ["status: awaiting triage"] 5 | body: 6 | - type: textarea 7 | id: issue-description 8 | attributes: 9 | label: What is your issue or question? 10 | description: A clear and concise explanation of the issue. 11 | validations: 12 | required: true 13 | - type: checkboxes 14 | id: no-duplicate-issues 15 | attributes: 16 | label: "Checklist" 17 | options: 18 | - label: "I have checked the existing issues." 19 | required: true 20 | 21 | - label: "I have read the Contribution Guidelines." 22 | required: true 23 | 24 | - label: "I have read the Code Of Conduct." 25 | required: true 26 | 27 | - label: "The changes don't break the code." 28 | required: true 29 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/refactor.yml: -------------------------------------------------------------------------------- 1 | name: 🔧 Refactor Code 2 | description: Use this format for code refactoring tasks. 3 | title: "🔧 [Refactor] <title>" 4 | labels: ["goal: refactor"] 5 | body: 6 | - type: input 7 | attributes: 8 | label: File Name 9 | description: "Enter the file that you want to refactor in the codebase." 10 | placeholder: "For example - component/Cards/Card.tsx" 11 | validations: 12 | required: true 13 | - type: textarea 14 | id: refactor_description 15 | attributes: 16 | label: "Reason for Refactoring the Code" 17 | description: "Describe the improvements that can be made in the codebase without introducing breaking changes." 18 | validations: 19 | required: true 20 | - type: checkboxes 21 | id: no-duplicate-issues 22 | 23 | 24 | attributes: 25 | label: "Checklist" 26 | options: 27 | 28 | - label: "I have checked the existing issues." 29 | required: true 30 | 31 | - label: "I have read the Contributing Guidelines" 32 | required: true 33 | 34 | - label: "I have read the Code Of Conduct" 35 | required: true 36 | 37 | - label: "The changes don't break the code" 38 | required: true 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/task-request.yml: -------------------------------------------------------------------------------- 1 | name: ​Task Request 2 | description: This is Only For Project Admins and Mentors. 3 | title: " <write a small description here>" 4 | labels: ["priority: high"] 5 | body: 6 | - type: textarea 7 | attributes: 8 | label: Description 9 | description: A brief description of the issue or bug you are facing, also include what you tried and what didn't work. 10 | validations: 11 | required: false 12 | - type: textarea 13 | attributes: 14 | label: Screenshots 15 | description: Please include screenshots if required 16 | validations: 17 | required: false 18 | - type: textarea 19 | attributes: 20 | label: Any additional Details? 21 | description: Please Provide Key Details, if required (Goals and objectives,Deliverables,Dependencies) 22 | validations: 23 | required: false 24 | - type: textarea 25 | attributes: 26 | label: Deadline 27 | description: Specify the task deadline 28 | validations: 29 | required: true 30 | 31 | - type: markdown 32 | attributes: 33 | value: "Stay you can start working on it by taking latest pull from main branch 34 | 35 | Please read the contributors guidelines Contributing Guidelines. 36 | 37 | Please follow the code of conduct Code Of Conduct." 38 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Related Issue 2 | [Cite any related issue(s) this pull request addresses. If none, simply state “None”] 3 | 4 | ## Description 5 | [Please include a brief description of the changes or features added] 6 | 7 | ## Type of PR 8 | 9 | - [ ] Bug fix 10 | - [ ] Feature enhancement 11 | - [ ] Documentation update 12 | - [ ] Other (specify): _______________ 13 | 14 | ## Screenshots / videos (if applicable) 15 | [Attach any relevant screenshots or videos demonstrating the changes] 16 | 17 | ## Checklist: 18 | - [ ] I have performed a self-review of my code 19 | - [ ] I have read and followed the Contribution Guidelines. 20 | - [ ] I have tested the changes thoroughly before submitting this pull request. 21 | - [ ] I have provided relevant issue numbers, screenshots, and videos after making the changes. 22 | - [ ] I have commented my code, particularly in hard-to-understand areas. 23 | <!-- [X] - put a cross/X inside [] to check the box --> 24 | 25 | ## Additional context: 26 | [Include any additional information or context that might be helpful for reviewers.] 27 | -------------------------------------------------------------------------------- /.github/workflows/autocomment-iss-close.yml: -------------------------------------------------------------------------------- 1 | name: Comment on Issue Close 2 | 3 | on: 4 | issues: 5 | types: [closed] 6 | 7 | jobs: 8 | greet-on-close: 9 | runs-on: ubuntu-latest 10 | permissions: 11 | issues: write 12 | steps: 13 | - name: Greet User 14 | uses: actions/github-script@v5 15 | with: 16 | github-token: ${{ secrets.GITHUB_TOKEN }} 17 | script: | 18 | const issue = context.payload.issue; 19 | const issueCreator = issue.user.login; 20 | const issueNumber = issue.number; 21 | 22 | const greetingMessage = `Hello @${issueCreator}! Your issue #${issueNumber} has been closed. Thank you for your contribution!`; 23 | 24 | github.rest.issues.createComment({ 25 | owner: context.repo.owner, 26 | repo: context.repo.repo, 27 | issue_number: issueNumber, 28 | body: greetingMessage 29 | }); 30 | -------------------------------------------------------------------------------- /.github/workflows/close-old-issue.yml: -------------------------------------------------------------------------------- 1 | name: Close Old Issues 2 | on: 3 | schedule: 4 | - cron: "0 0 * * *" 5 | 6 | jobs: 7 | close-issues: 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - name: Checkout Repository 12 | uses: actions/checkout@v4 13 | 14 | - name: Close Old Issues 15 | run: | 16 | open_issues=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ 17 | "https://api.github.com/repos/${{ github.repository }}/issues?state=open" \ 18 | | jq -r '.[] | .number') 19 | for issue in $open_issues; do 20 | # Get the last updated timestamp of the issue 21 | last_updated=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ 22 | "https://api.github.com/repos/${{ github.repository }}/issues/$issue" \ 23 | | jq -r '.updated_at') 24 | days_since_update=$(( ( $(date +%s) - $(date -d "$last_updated" +%s) ) / 86400 )) 25 | if [ $days_since_update -gt 30 ]; then 26 | curl -s -X PATCH -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ 27 | -H "Accept: application/vnd.github.v3+json" \ 28 | -d '{"state":"closed"}' \ 29 | "https://api.github.com/repos/${{ github.repository }}/issues/$issue" 30 | 31 | # Add a comment explaining when the issue will be closed 32 | curl -s -X POST -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ 33 | -H "Accept: application/vnd.github.v3+json" \ 34 | -d '{"body":"This issue has been automatically closed because it has been inactive for more than 30 days. If you believe this is still relevant, feel free to reopen it or create a new one. Thank you!"}' \ 35 | "https://api.github.com/repos/${{ github.repository }}/issues/$issue/comments" 36 | fi 37 | done 38 | -------------------------------------------------------------------------------- /.github/workflows/close-old-pr.yml: -------------------------------------------------------------------------------- 1 | name: Close Stale PRs 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * *' # Runs daily at midnight 6 | pull_request: 7 | types: 8 | - opened 9 | - reopened 10 | - synchronize 11 | 12 | permissions: 13 | pull-requests: write 14 | issues: write 15 | 16 | jobs: 17 | close_stale_prs: 18 | runs-on: ubuntu-latest 19 | permissions: 20 | pull-requests: write 21 | 22 | steps: 23 | - uses: actions/stale@v7 24 | with: 25 | repo-token: ${{ secrets.GITHUB_TOKEN }} 26 | stale-pr-message: 'This PR has been automatically closed due to inactivity from the owner for 15 days.' 27 | days-before-pr-stale: 15 28 | days-before-pr-close: 0 29 | exempt-pr-author: false 30 | exempt-pr-labels: '' 31 | only-labels: '' 32 | operations-per-run: 30 33 | remove-stale-when-updated: true 34 | debug-only: false 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /server/node_modules 3 | 4 | .env 5 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "python.analysis.extraPaths": [ 3 | "./src/pages/import " 4 | ] 5 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Muskan Sahu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /images/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Muskansahuincredible/StudyNotion-An-Online-Education-Platform/510af9c37a48b8eec913c5f6e98018665903ac3c/images/architecture.png -------------------------------------------------------------------------------- /images/mainpage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Muskansahuincredible/StudyNotion-An-Online-Education-Platform/510af9c37a48b8eec913c5f6e98018665903ac3c/images/mainpage.png -------------------------------------------------------------------------------- /images/schema.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Muskansahuincredible/StudyNotion-An-Online-Education-Platform/510af9c37a48b8eec913c5f6e98018665903ac3c/images/schema.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-tailwind-css-starter-pack", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@emotion/react": "^11.13.0", 7 | "@emotion/styled": "^11.13.0", 8 | "@mui/icons-material": "^5.16.4", 9 | "@mui/material": "^5.16.4", 10 | "@ramonak/react-progress-bar": "^5.0.3", 11 | "@reduxjs/toolkit": "^1.9.5", 12 | "axios": "^1.4.0", 13 | "bcrypt": "^5.1.1", 14 | "chart.js": "^4.3.0", 15 | "concurrently": "^8.0.1", 16 | "copy-to-clipboard": "^3.3.3", 17 | "dotenv": "^16.4.5", 18 | "framer-motion": "^11.3.19", 19 | "json-server": "^1.0.0-beta.0", 20 | "react": "^18.2.0", 21 | "react-chartjs-2": "^5.2.0", 22 | "react-dom": "^18.2.0", 23 | "react-dropzone": "^14.2.3", 24 | "react-hook-form": "^7.44.2", 25 | "react-hot-toast": "^2.4.1", 26 | "react-icons": "^4.12.0", 27 | "react-markdown": "^8.0.7", 28 | "react-otp-input": "^3.0.2", 29 | "react-rating-stars-component": "^2.2.0", 30 | "react-redux": "^8.0.5", 31 | "react-router-dom": "^6.11.2", 32 | "react-scripts": "^5.0.1", 33 | "react-spinners": "^0.13.8", 34 | "react-super-responsive-table": "^5.2.1", 35 | "react-type-animation": "^3.0.1", 36 | "redux-toolkit": "^1.1.2", 37 | "styled-components": "^6.1.11", 38 | "swiper": "^11.1.4", 39 | "video-react": "^0.16.0", 40 | "web-vitals": "^2.1.4" 41 | }, 42 | "repository": { 43 | "type": "git", 44 | "url": "git+https://github.com/thepranaygupta/react-tailwind-css-starter-pack.git" 45 | }, 46 | "author": "Pranay Gupta", 47 | "bugs": { 48 | "url": "https://github.com/thepranaygupta/react-tailwind-css-starter-pack/issues" 49 | }, 50 | "scripts": { 51 | "start": "react-scripts start", 52 | "build": "react-scripts build", 53 | "eject": "react-scripts eject", 54 | "server": "cd server && npm run dev", 55 | "dev": "concurrently -n \"client,server\" -c \"bgBlue,bgYellow\" \"npm start\" \"npm run server\"" 56 | }, 57 | "eslintConfig": { 58 | "extends": [ 59 | "react-app", 60 | "react-app/jest" 61 | ] 62 | }, 63 | "browserslist": { 64 | "production": [ 65 | ">0.2%", 66 | "not dead", 67 | "not op_mini all" 68 | ], 69 | "development": [ 70 | "last 1 chrome version", 71 | "last 1 firefox version", 72 | "last 1 safari version" 73 | ] 74 | }, 75 | "devDependencies": { 76 | "@babel/plugin-proposal-private-property-in-object": "^7.21.11", 77 | "autoprefixer": "^10.4.19", 78 | "postcss": "^8.4.38", 79 | "tailwindcss": "^3.4.4" 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Muskansahuincredible/StudyNotion-An-Online-Education-Platform/510af9c37a48b8eec913c5f6e98018665903ac3c/public/favicon.png -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | <!DOCTYPE html> 2 | <html lang="en"> 3 | 4 | <head> 5 | <meta charset="utf-8" /> 6 | <meta name="viewport" content="width=device-width, initial-scale=1" /> 7 | <link rel="icon" type="image/x-icon" href="favicon.png"> 8 | <title>StudyNotion 9 | 18 | 22 | 23 | 24 | 25 | 26 |
27 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /server/.gitignore: -------------------------------------------------------------------------------- 1 | .env -------------------------------------------------------------------------------- /server/config/cloudinary.js: -------------------------------------------------------------------------------- 1 | const cloudinary = require("cloudinary").v2; //! Cloudinary is being required 2 | 3 | exports.cloudinaryConnect = () => { 4 | try { 5 | cloudinary.config({ 6 | //! ######## Configuring the Cloudinary to Upload MEDIA ######## 7 | cloud_name: process.env.CLOUD_NAME, 8 | api_key: process.env.API_KEY, 9 | api_secret: process.env.API_SECRET, 10 | }); 11 | } catch (error) { 12 | console.log(error); 13 | } 14 | }; -------------------------------------------------------------------------------- /server/config/database.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | require("dotenv").config(); 3 | 4 | exports.connect = () => { 5 | mongoose.connect(process.env.MONGODB_URL, { 6 | useNewUrlParser: true, 7 | useUnifiedTopology:true, 8 | }) 9 | .then(() => console.log("DB Connected Successfully")) 10 | .catch( (error) => { 11 | console.log("DB Connection Failed"); 12 | console.error(error); 13 | process.exit(1); 14 | } ) 15 | }; -------------------------------------------------------------------------------- /server/config/razorpay.js: -------------------------------------------------------------------------------- 1 | const Razorpay = require("razorpay"); 2 | 3 | 4 | exports.instance = new Razorpay({ 5 | key_id: process.env.RAZORPAY_KEY, 6 | key_secret: process.env.RAZORPAY_SECRET, 7 | }); -------------------------------------------------------------------------------- /server/controllers/ContactUs.js: -------------------------------------------------------------------------------- 1 | const { contactUsEmail } = require("../mail/templates/contactFormRes") 2 | const mailSender = require("../utils/mailSender") 3 | 4 | exports.contactUsController = async (req, res) => { 5 | const { email, firstname, lastname, message, phoneNo, countrycode } = req.body 6 | console.log(req.body) 7 | try { 8 | const emailRes = await mailSender( 9 | email, 10 | "Your Data send successfully", 11 | contactUsEmail(email, firstname, lastname, message, phoneNo, countrycode) 12 | ) 13 | console.log("Email Res ", emailRes) 14 | return res.json({ 15 | success: true, 16 | message: "Email send successfully", 17 | }) 18 | } catch (error) { 19 | console.log("Error", error) 20 | console.log("Error message :", error.message) 21 | return res.json({ 22 | success: false, 23 | message: "Something went wrong...", 24 | }) 25 | } 26 | } -------------------------------------------------------------------------------- /server/controllers/ResetPassword.js: -------------------------------------------------------------------------------- 1 | const User = require("../models/User"); 2 | const mailSender = require("../utils/mailSender"); 3 | const bcrypt = require("bcryptjs"); 4 | const crypto = require("crypto"); 5 | 6 | exports.resetPasswordToken = async (req, res) => { 7 | try { 8 | const email = req.body.email; 9 | const user = await User.findOne({ email: email }); 10 | if (!user) { 11 | return res.json({ 12 | success: false, 13 | message: `This Email: ${email} is not Registered With Us Enter a Valid Email `, 14 | }); 15 | } 16 | const token = crypto.randomBytes(20).toString("hex"); 17 | 18 | const updatedDetails = await User.findOneAndUpdate( 19 | { email: email }, 20 | { 21 | token: token, 22 | resetPasswordExpires: Date.now() + 3600000, 23 | }, 24 | { new: true } 25 | ); 26 | console.log("DETAILS", updatedDetails); 27 | 28 | const url = `http://localhost:3000/update-password/${token}`; 29 | 30 | await mailSender( 31 | email, 32 | "Password Reset", 33 | `Your Link for email verification is ${url}. Please click this url to reset your password.` 34 | ); 35 | 36 | res.json({ 37 | success: true, 38 | message: 39 | "Email Sent Successfully, Please Check Your Email to Continue Further", 40 | }); 41 | } catch (error) { 42 | return res.json({ 43 | error: error.message, 44 | success: false, 45 | message: `Some Error in Sending the Reset Message`, 46 | }); 47 | } 48 | }; 49 | 50 | exports.resetPassword = async (req, res) => { 51 | try { 52 | const { password, confirmPassword, token } = req.body; 53 | 54 | if (confirmPassword !== password) { 55 | return res.json({ 56 | success: false, 57 | message: "Password and Confirm Password Does not Match", 58 | }); 59 | } 60 | const userDetails = await User.findOne({ token: token }); 61 | if (!userDetails) { 62 | return res.json({ 63 | success: false, 64 | message: "Token is Invalid", 65 | }); 66 | } 67 | if (!(userDetails.resetPasswordExpires > Date.now())) { 68 | return res.status(403).json({ 69 | success: false, 70 | message: `Token is Expired, Please Regenerate Your Token`, 71 | }); 72 | } 73 | const encryptedPassword = await bcrypt.hash(password, 10); 74 | await User.findOneAndUpdate( 75 | { token: token }, 76 | { password: encryptedPassword }, 77 | { new: true } 78 | ); 79 | res.json({ 80 | success: true, 81 | message: `Password Reset Successful`, 82 | }); 83 | } catch (error) { 84 | return res.json({ 85 | error: error.message, 86 | success: false, 87 | message: `Some Error in Updating the Password`, 88 | }); 89 | } 90 | }; -------------------------------------------------------------------------------- /server/controllers/Section.js: -------------------------------------------------------------------------------- 1 | const Section = require("../models/Section"); 2 | const Course = require("../models/Course"); 3 | const SubSection = require("../models/SubSection"); 4 | // CREATE a new section 5 | exports.createSection = async (req, res) => { 6 | try { 7 | // Extract the required properties from the request body 8 | const { sectionName, courseId } = req.body; 9 | 10 | // Validate the input 11 | if (!sectionName || !courseId) { 12 | return res.status(400).json({ 13 | success: false, 14 | message: "Missing required properties", 15 | }); 16 | } 17 | 18 | // Create a new section with the given name 19 | const newSection = await Section.create({ sectionName }); 20 | 21 | // Add the new section to the course's content array 22 | const updatedCourse = await Course.findByIdAndUpdate( 23 | courseId, 24 | { 25 | $push: { 26 | courseContent: newSection._id, 27 | }, 28 | }, 29 | { new: true } 30 | ) 31 | .populate({ 32 | path: "courseContent", 33 | populate: { 34 | path: "subSection", 35 | }, 36 | }) 37 | .exec(); 38 | 39 | // Return the updated course object in the response 40 | res.status(200).json({ 41 | success: true, 42 | message: "Section created successfully", 43 | updatedCourse, 44 | }); 45 | } catch (error) { 46 | // Handle errors 47 | res.status(500).json({ 48 | success: false, 49 | message: "Internal server error", 50 | error: error.message, 51 | }); 52 | } 53 | }; 54 | 55 | // UPDATE a section 56 | exports.updateSection = async (req, res) => { 57 | try { 58 | const { sectionName, sectionId,courseId } = req.body; 59 | const section = await Section.findByIdAndUpdate( 60 | sectionId, 61 | { sectionName }, 62 | { new: true } 63 | ); 64 | 65 | const course = await Course.findById(courseId) 66 | .populate({ 67 | path:"courseContent", 68 | populate:{ 69 | path:"subSection", 70 | }, 71 | }) 72 | .exec(); 73 | 74 | res.status(200).json({ 75 | success: true, 76 | message: section, 77 | data:course, 78 | }); 79 | } catch (error) { 80 | console.error("Error updating section:", error); 81 | res.status(500).json({ 82 | success: false, 83 | message: "Internal server error", 84 | }); 85 | } 86 | }; 87 | 88 | // DELETE a section 89 | exports.deleteSection = async (req, res) => { 90 | try { 91 | 92 | const { sectionId, courseId } = req.body; 93 | await Course.findByIdAndUpdate(courseId, { 94 | $pull: { 95 | courseContent: sectionId, 96 | } 97 | }) 98 | const section = await Section.findById(sectionId); 99 | console.log(sectionId, courseId); 100 | if(!section) { 101 | return res.status(404).json({ 102 | success:false, 103 | message:"Section not Found", 104 | }) 105 | } 106 | 107 | //delete sub section 108 | await SubSection.deleteMany({_id: {$in: section.subSection}}); 109 | 110 | await Section.findByIdAndDelete(sectionId); 111 | 112 | //find the updated course and return 113 | const course = await Course.findById(courseId).populate({ 114 | path:"courseContent", 115 | populate: { 116 | path: "subSection" 117 | } 118 | }) 119 | .exec(); 120 | 121 | res.status(200).json({ 122 | success:true, 123 | message:"Section deleted", 124 | data:course 125 | }); 126 | } catch (error) { 127 | console.error("Error deleting section:", error); 128 | res.status(500).json({ 129 | success: false, 130 | message: "Internal server error", 131 | }); 132 | } 133 | }; -------------------------------------------------------------------------------- /server/controllers/courseProgress.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose") 2 | const Section = require("../models/Section") 3 | const SubSection = require("../models/SubSection") 4 | const CourseProgress = require("../models/CourseProgress") 5 | const Course = require("../models/Course") 6 | 7 | exports.updateCourseProgress = async (req, res) => { 8 | const { courseId, subsectionId } = req.body 9 | const userId = req.user.id 10 | 11 | try { 12 | // Check if the subsection is valid 13 | const subsection = await SubSection.findById(subsectionId) 14 | if (!subsection) { 15 | return res.status(404).json({ error: "Invalid subsection" }) 16 | } 17 | 18 | // Find the course progress document for the user and course 19 | let courseProgress = await CourseProgress.findOne({ 20 | courseID: courseId, 21 | userId: userId, 22 | }) 23 | 24 | if (!courseProgress) { 25 | // If course progress doesn't exist, create a new one 26 | return res.status(404).json({ 27 | success: false, 28 | message: "Course progress Does Not Exist", 29 | }) 30 | } else { 31 | // If course progress exists, check if the subsection is already completed 32 | if (courseProgress.completedVideos.includes(subsectionId)) { 33 | return res.status(400).json({ error: "Subsection already completed" }) 34 | } 35 | 36 | // Push the subsection into the completedVideos array 37 | courseProgress.completedVideos.push(subsectionId) 38 | } 39 | 40 | // Save the updated course progress 41 | await courseProgress.save() 42 | 43 | return res.status(200).json({ message: "Course progress updated" }) 44 | } catch (error) { 45 | console.error(error) 46 | return res.status(500).json({ error: "Internal server error" }) 47 | } 48 | } 49 | 50 | // exports.getProgressPercentage = async (req, res) => { 51 | // const { courseId } = req.body 52 | // const userId = req.user.id 53 | 54 | // if (!courseId) { 55 | // return res.status(400).json({ error: "Course ID not provided." }) 56 | // } 57 | 58 | // try { 59 | // // Find the course progress document for the user and course 60 | // let courseProgress = await CourseProgress.findOne({ 61 | // courseID: courseId, 62 | // userId: userId, 63 | // }) 64 | // .populate({ 65 | // path: "courseID", 66 | // populate: { 67 | // path: "courseContent", 68 | // }, 69 | // }) 70 | // .exec() 71 | 72 | // if (!courseProgress) { 73 | // return res 74 | // .status(400) 75 | // .json({ error: "Can not find Course Progress with these IDs." }) 76 | // } 77 | // console.log(courseProgress, userId) 78 | // let lectures = 0 79 | // courseProgress.courseID.courseContent?.forEach((sec) => { 80 | // lectures += sec.subSection.length || 0 81 | // }) 82 | 83 | // let progressPercentage = 84 | // (courseProgress.completedVideos.length / lectures) * 100 85 | 86 | // // To make it up to 2 decimal point 87 | // const multiplier = Math.pow(10, 2) 88 | // progressPercentage = 89 | // Math.round(progressPercentage * multiplier) / multiplier 90 | 91 | // return res.status(200).json({ 92 | // data: progressPercentage, 93 | // message: "Succesfully fetched Course progress", 94 | // }) 95 | // } catch (error) { 96 | // console.error(error) 97 | // return res.status(500).json({ error: "Internal server error" }) 98 | // } 99 | // } -------------------------------------------------------------------------------- /server/index.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const app = express(); 3 | 4 | const userRoutes = require("./routes/User"); 5 | const profileRoutes = require("./routes/Profile"); 6 | const paymentRoutes = require("./routes/Payments"); 7 | const courseRoutes = require("./routes/Course"); 8 | const contactUsRoute = require("./routes/Contact"); 9 | const database = require("./config/database"); 10 | const cookieParser = require("cookie-parser"); 11 | const cors = require("cors"); 12 | const {cloudinaryConnect } = require("./config/cloudinary"); 13 | const fileUpload = require("express-fileupload"); 14 | const dotenv = require("dotenv"); 15 | 16 | dotenv.config(); 17 | const PORT = process.env.PORT || 4000; 18 | 19 | //database connect 20 | database.connect(); 21 | //middlewares 22 | app.use(express.json()); 23 | app.use(express.urlencoded({extended: true}))//for using postman 24 | app.use(cookieParser()); 25 | app.use( 26 | cors({ 27 | origin:"http://localhost:3000", 28 | credentials:true, 29 | }) 30 | ) 31 | 32 | app.use( 33 | fileUpload({ 34 | useTempFiles:true, 35 | tempFileDir:"/tmp", 36 | }) 37 | ) 38 | //cloudinary connection 39 | cloudinaryConnect(); 40 | 41 | //routes 42 | app.use("/api/v1/auth", userRoutes); 43 | app.use("/api/v1/profile", profileRoutes); 44 | app.use("/api/v1/course", courseRoutes); 45 | app.use("/api/v1/payment", paymentRoutes); 46 | app.use("/api/v1/reach", contactUsRoute); 47 | 48 | //def route 49 | 50 | app.get("/", (req, res) => { 51 | return res.json({ 52 | success:true, 53 | message:'Your server is up and running....' 54 | }); 55 | }); 56 | 57 | app.listen(PORT, () => { 58 | console.log(`App is running at ${PORT}`) 59 | }) 60 | 61 | -------------------------------------------------------------------------------- /server/mail/templates/contactFormRes.js: -------------------------------------------------------------------------------- 1 | exports.contactUsEmail = ( 2 | email, 3 | firstname, 4 | lastname, 5 | message, 6 | phoneNo, 7 | countrycode 8 | ) => { 9 | return ` 10 | 11 | 12 | 13 | 14 | Contact Form Confirmation 15 | 72 | 73 | 74 | 75 | 76 |
77 | 79 |
Contact Form Confirmation
80 |
81 |

Dear ${firstname} ${lastname},

82 |

Thank you for contacting us. We have received your message and will respond to you as soon as possible. 83 |

84 |

Here are the details you provided:

85 |

Name: ${firstname} ${lastname}

86 |

Email: ${email}

87 |

Phone Number: ${phoneNo}

88 |

Message: ${message}

89 |

We appreciate your interest and will get back to you shortly.

90 |
91 |
If you have any further questions or need immediate assistance, please feel free to reach 92 | out to us at info@studynotion.com. We are here to help!
93 |
94 | 95 | 96 | ` 97 | } -------------------------------------------------------------------------------- /server/mail/templates/courseEnrollmentEmail.js: -------------------------------------------------------------------------------- 1 | exports.courseEnrollmentEmail = (courseName, name) => { 2 | return ` 3 | 4 | 5 | 6 | 7 | Course Registration Confirmation 8 | 65 | 66 | 67 | 68 | 69 |
70 | 72 |
Course Registration Confirmation
73 |
74 |

Dear ${name},

75 |

You have successfully registered for the course "${courseName}". We 76 | are excited to have you as a participant!

77 |

Please log in to your learning dashboard to access the course materials and start your learning journey. 78 |

79 | Go to Dashboard 80 |
81 |
If you have any questions or need assistance, please feel free to reach out to us at info@studynotion.com. We are here to help!
83 |
84 | 85 | 86 | `; 87 | }; -------------------------------------------------------------------------------- /server/mail/templates/emailVerificationTemplate.js: -------------------------------------------------------------------------------- 1 | const otpTemplate = (otp) => { 2 | return ` 3 | 4 | 5 | 6 | 7 | OTP Verification Email 8 | 64 | 65 | 66 | 67 | 68 |
69 | 71 |
OTP Verification Email
72 |
73 |

Dear User,

74 |

Thank you for registering with StudyNotion. To complete your registration, please use the following OTP 75 | (One-Time Password) to verify your account:

76 |

${otp}

77 |

This OTP is valid for 5 minutes. If you did not request this verification, please disregard this email. 78 | Once your account is verified, you will have access to our platform and its features.

79 |
80 |
If you have any questions or need assistance, please feel free to reach out to us at info@studynotion.com. We are here to help!
82 |
83 | 84 | 85 | `; 86 | }; 87 | module.exports = otpTemplate; -------------------------------------------------------------------------------- /server/mail/templates/passwordUpdate.js: -------------------------------------------------------------------------------- 1 | exports.passwordUpdated = (email, name) => { 2 | return ` 3 | 4 | 5 | 6 | 7 | Password Update Confirmation 8 | 53 | 54 | 55 | 56 | 57 |
58 | 60 |
Password Update Confirmation
61 |
62 |

Hey ${name},

63 |

Your password has been successfully updated for the email ${email}. 64 |

65 |

If you did not request this password change, please contact us immediately to secure your account.

66 |
67 |
If you have any questions or need further assistance, please feel free to reach out to us 68 | at 69 | info@studynotion.com. We are here to help! 70 |
71 |
72 | 73 | 74 | `; 75 | }; -------------------------------------------------------------------------------- /server/mail/templates/paymentSuccessEmail.js: -------------------------------------------------------------------------------- 1 | exports.paymentSuccessEmail = (name, amount, orderId, paymentId) => { 2 | return ` 3 | 4 | 5 | 6 | 7 | Payment Confirmation 8 | 65 | 66 | 67 | 68 | 69 |
70 | 72 |
Course Payment Confirmation
73 |
74 |

Dear ${name},

75 |

We have received a payment of ₹${amount}

. 76 |

Your Payment ID is ${paymentId}

77 |

Your Order ID is ${orderId}

78 |
79 |
If you have any questions or need assistance, please feel free to reach out to us at info@studynotion.com. We are here to help!
81 |
82 | 83 | 84 | ` 85 | } -------------------------------------------------------------------------------- /server/middlewares/auth.js: -------------------------------------------------------------------------------- 1 | // Importing required modules 2 | const jwt = require("jsonwebtoken"); 3 | const dotenv = require("dotenv"); 4 | const User = require("../models/User"); 5 | // Configuring dotenv to load environment variables from .env file 6 | dotenv.config(); 7 | 8 | // This function is used as middleware to authenticate user requests 9 | exports.auth = async (req, res, next) => { 10 | try { 11 | // Extracting JWT from request cookies, body or header 12 | const token = 13 | req.cookies.token || 14 | req.body.token || 15 | req.header("Authorization").replace("Bearer ", ""); 16 | 17 | // If JWT is missing, return 401 Unauthorized response 18 | if (!token) { 19 | return res.status(401).json({ success: false, message: `Token Missing` }); 20 | } 21 | 22 | try { 23 | // Verifying the JWT using the secret key stored in environment variables 24 | const decode = await jwt.verify(token, process.env.JWT_SECRET); 25 | console.log(decode); 26 | // Storing the decoded JWT payload in the request object for further use 27 | req.user = decode; 28 | } catch (error) { 29 | // If JWT verification fails, return 401 Unauthorized response 30 | return res 31 | .status(401) 32 | .json({ success: false, message: "token is invalid" }); 33 | } 34 | 35 | // If JWT is valid, move on to the next middleware or request handler 36 | next(); 37 | } catch (error) { 38 | // If there is an error during the authentication process, return 401 Unauthorized response 39 | return res.status(401).json({ 40 | success: false, 41 | message: `Something Went Wrong While Validating the Token`, 42 | }); 43 | } 44 | }; 45 | exports.isStudent = async (req, res, next) => { 46 | try { 47 | const userDetails = await User.findOne({ email: req.user.email }); 48 | 49 | if (userDetails.accountType !== "Student") { 50 | return res.status(401).json({ 51 | success: false, 52 | message: "This is a Protected Route for Students", 53 | }); 54 | } 55 | next(); 56 | } catch (error) { 57 | return res 58 | .status(500) 59 | .json({ success: false, message: `User Role Can't be Verified` }); 60 | } 61 | }; 62 | exports.isAdmin = async (req, res, next) => { 63 | try { 64 | const userDetails = await User.findOne({ email: req.user.email }); 65 | 66 | if (userDetails.accountType !== "Admin") { 67 | return res.status(401).json({ 68 | success: false, 69 | message: "This is a Protected Route for Admin", 70 | }); 71 | } 72 | next(); 73 | } catch (error) { 74 | return res 75 | .status(500) 76 | .json({ success: false, message: `User Role Can't be Verified` }); 77 | } 78 | }; 79 | exports.isInstructor = async (req, res, next) => { 80 | try { 81 | const userDetails = await User.findOne({ email: req.user.email }); 82 | console.log(userDetails); 83 | 84 | console.log(userDetails.accountType); 85 | 86 | if (userDetails.accountType !== "Instructor") { 87 | return res.status(401).json({ 88 | success: false, 89 | message: "This is a Protected Route for Instructor", 90 | }); 91 | } 92 | next(); 93 | } catch (error) { 94 | return res 95 | .status(500) 96 | .json({ success: false, message: `User Role Can't be Verified` }); 97 | } 98 | }; -------------------------------------------------------------------------------- /server/models/Category.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | // Define the Tags schema 4 | const categorySchema = new mongoose.Schema({ 5 | name: { 6 | type: String, 7 | required: true, 8 | }, 9 | description: { type: String }, 10 | courses: [ 11 | { 12 | type: mongoose.Schema.Types.ObjectId, 13 | ref: "Course", 14 | }, 15 | ], 16 | }); 17 | 18 | // Export the Tags model 19 | module.exports = mongoose.model("Category", categorySchema); -------------------------------------------------------------------------------- /server/models/Course.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | // Define the Courses schema 4 | const coursesSchema = new mongoose.Schema({ 5 | courseName: { type: String }, 6 | courseDescription: { type: String }, 7 | instructor: { 8 | type: mongoose.Schema.Types.ObjectId, 9 | required: true, 10 | ref: "user", 11 | }, 12 | whatYouWillLearn: { 13 | type: String, 14 | }, 15 | courseContent: [ 16 | { 17 | type: mongoose.Schema.Types.ObjectId, 18 | ref: "Section", 19 | }, 20 | ], 21 | ratingAndReviews: [ 22 | { 23 | type: mongoose.Schema.Types.ObjectId, 24 | ref: "RatingAndReview", 25 | }, 26 | ], 27 | price: { 28 | type: Number, 29 | }, 30 | thumbnail: { 31 | type: String, 32 | }, 33 | tag: { 34 | type: [String], 35 | required: true, 36 | }, 37 | category: { 38 | type: mongoose.Schema.Types.ObjectId, 39 | // required: true, 40 | ref: "Category", 41 | }, 42 | studentsEnrolled: [ 43 | { 44 | type: mongoose.Schema.Types.ObjectId, 45 | required: true, 46 | ref: "user", 47 | }, 48 | ], 49 | instructions: { 50 | type: [String], 51 | }, 52 | status: { 53 | type: String, 54 | enum: ["Draft", "Published"], 55 | }, 56 | createdAt: { 57 | type:Date, 58 | default:Date.now 59 | }, 60 | }); 61 | 62 | // Export the Courses model 63 | module.exports = mongoose.model("Course", coursesSchema); -------------------------------------------------------------------------------- /server/models/CourseProgress.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose") 2 | 3 | const courseProgress = new mongoose.Schema({ 4 | courseID: { 5 | type: mongoose.Schema.Types.ObjectId, 6 | ref: "Course", 7 | }, 8 | userId: { 9 | type: mongoose.Schema.Types.ObjectId, 10 | ref: "user", 11 | }, 12 | completedVideos: [ 13 | { 14 | type: mongoose.Schema.Types.ObjectId, 15 | ref: "SubSection", 16 | }, 17 | ], 18 | }) 19 | 20 | module.exports = mongoose.model("courseProgress", courseProgress) -------------------------------------------------------------------------------- /server/models/OTP.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const mailSender = require("../utils/mailSender"); 3 | const emailTemplate = require("../mail/templates/emailVerificationTemplate"); 4 | const OTPSchema = new mongoose.Schema({ 5 | email: { 6 | type: String, 7 | required: true, 8 | }, 9 | otp: { 10 | type: String, 11 | required: true, 12 | }, 13 | createdAt: { 14 | type: Date, 15 | default: Date.now, 16 | expires: 60 * 5, // The document will be automatically deleted after 5 minutes of its creation time 17 | }, 18 | }); 19 | 20 | // Define a function to send emails 21 | async function sendVerificationEmail(email, otp) { 22 | // Create a transporter to send emails 23 | 24 | // Define the email options 25 | 26 | // Send the email 27 | try { 28 | const mailResponse = await mailSender( 29 | email, 30 | "Verification Email", 31 | emailTemplate(otp) 32 | ); 33 | console.log("Email sent successfully: ", mailResponse.response); 34 | } catch (error) { 35 | console.log("Error occurred while sending email: ", error); 36 | throw error; 37 | } 38 | } 39 | 40 | // Define a post-save hook to send email after the document has been saved 41 | OTPSchema.pre("save", async function (next) { 42 | console.log("New document saved to database"); 43 | 44 | // Only send an email when a new document is created 45 | if (this.isNew) { 46 | await sendVerificationEmail(this.email, this.otp); 47 | } 48 | next(); 49 | }); 50 | 51 | const OTP = mongoose.model("OTP", OTPSchema); 52 | 53 | module.exports = OTP; -------------------------------------------------------------------------------- /server/models/Profile.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | // Define the Profile schema 4 | const profileSchema = new mongoose.Schema({ 5 | gender: { 6 | type: String, 7 | }, 8 | dateOfBirth: { 9 | type: String, 10 | }, 11 | about: { 12 | type: String, 13 | trim: true, 14 | }, 15 | contactNumber: { 16 | type: Number, 17 | trim: true, 18 | }, 19 | }); 20 | 21 | // Export the Profile model 22 | module.exports = mongoose.model("Profile", profileSchema); 23 | -------------------------------------------------------------------------------- /server/models/RatingAndRaview.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | // Define the RatingAndReview schema 4 | const ratingAndReviewSchema = new mongoose.Schema({ 5 | user: { 6 | type: mongoose.Schema.Types.ObjectId, 7 | required: true, 8 | ref: "user", 9 | }, 10 | rating: { 11 | type: Number, 12 | required: true, 13 | }, 14 | review: { 15 | type: String, 16 | required: true, 17 | }, 18 | course: { 19 | type: mongoose.Schema.Types.ObjectId, 20 | required: true, 21 | ref: "Course", 22 | index: true, 23 | }, 24 | }); 25 | 26 | // Export the RatingAndReview model 27 | module.exports = mongoose.model("RatingAndReview", ratingAndReviewSchema); -------------------------------------------------------------------------------- /server/models/Section.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | // Define the Section schema 4 | const sectionSchema = new mongoose.Schema({ 5 | sectionName: { 6 | type: String, 7 | }, 8 | subSection: [ 9 | { 10 | type: mongoose.Schema.Types.ObjectId, 11 | required: true, 12 | ref: "SubSection", 13 | }, 14 | ], 15 | }); 16 | 17 | // Export the Section model 18 | module.exports = mongoose.model("Section", sectionSchema); -------------------------------------------------------------------------------- /server/models/SubSection.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const SubSectionSchema = new mongoose.Schema({ 4 | title: { type: String }, 5 | timeDuration: { type: String }, 6 | description: { type: String }, 7 | videoUrl: { type: String }, 8 | }); 9 | 10 | module.exports = mongoose.model("SubSection", SubSectionSchema); -------------------------------------------------------------------------------- /server/models/User.js: -------------------------------------------------------------------------------- 1 | // Import the Mongoose library 2 | const mongoose = require("mongoose"); 3 | 4 | // Define the user schema using the Mongoose Schema constructor 5 | const userSchema = new mongoose.Schema( 6 | { 7 | // Define the name field with type String, required, and trimmed 8 | firstName: { 9 | type: String, 10 | required: true, 11 | trim: true, 12 | }, 13 | lastName: { 14 | type: String, 15 | required: true, 16 | trim: true, 17 | }, 18 | // Define the email field with type String, required, and trimmed 19 | email: { 20 | type: String, 21 | required: true, 22 | trim: true, 23 | }, 24 | 25 | // Define the password field with type String and required 26 | password: { 27 | type: String, 28 | required: true, 29 | }, 30 | // Define the role field with type String and enum values of "Admin", "Student", or "Visitor" 31 | accountType: { 32 | type: String, 33 | enum: ["Admin", "Student", "Instructor"], 34 | required: true, 35 | }, 36 | active: { 37 | type: Boolean, 38 | default: true, 39 | }, 40 | approved: { 41 | type: Boolean, 42 | default: true, 43 | }, 44 | additionalDetails: { 45 | type: mongoose.Schema.Types.ObjectId, 46 | required: true, 47 | ref: "Profile", 48 | }, 49 | courses: [ 50 | { 51 | type: mongoose.Schema.Types.ObjectId, 52 | ref: "Course", 53 | }, 54 | ], 55 | token: { 56 | type: String, 57 | }, 58 | resetPasswordExpires: { 59 | type: Date, 60 | }, 61 | image: { 62 | type: String, 63 | //at registration image cannot be uploaded 64 | // required: true, 65 | }, 66 | courseProgress: [ 67 | { 68 | type: mongoose.Schema.Types.ObjectId, 69 | ref: "courseProgress", 70 | }, 71 | ], 72 | 73 | // Add timestamps for when the document is created and last modified 74 | }, 75 | { timestamps: true } 76 | ); 77 | 78 | // Export the Mongoose model for the user schema, using the name "user" 79 | module.exports = mongoose.model("user", userSchema); -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node index.js", 8 | "dev": "nodemon index.js" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "bcrypt": "^5.1.0", 15 | "bcryptjs": "^2.4.3", 16 | "cloudinary": "^1.36.4", 17 | "cookie-parser": "^1.4.6", 18 | "cors": "^2.8.5", 19 | "crypto-random-string": "^5.0.0", 20 | "dotenv": "^16.0.3", 21 | "express": "^4.18.2", 22 | "express-fileupload": "^1.4.0", 23 | "jsonwebtoken": "^9.0.0", 24 | "mongoose": "^7.0.3", 25 | "node-schedule": "^2.1.1", 26 | "nodemailer": "^6.9.1", 27 | "nodemon": "^3.1.3", 28 | "otp-generator": "^4.0.1", 29 | "razorpay": "^2.8.6", 30 | "@babel/plugin-proposal-private-property-in-object": "7.21.11" 31 | } 32 | } 33 | 34 | 35 | -------------------------------------------------------------------------------- /server/routes/Contact.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express.Router() 3 | const { contactUsController } = require("../controllers/ContactUs") 4 | 5 | router.post("/contact", contactUsController) 6 | 7 | module.exports = router -------------------------------------------------------------------------------- /server/routes/Payments.js: -------------------------------------------------------------------------------- 1 | // Import the required modules 2 | const express = require("express") 3 | const router = express.Router() 4 | 5 | const { capturePayment, verifyPayment, sendPaymentSuccessEmail } = require("../controllers/Payments") 6 | const { auth, isInstructor, isStudent, isAdmin } = require("../middlewares/auth") 7 | router.post("/capturePayment", auth, isStudent, capturePayment) 8 | router.post("/verifyPayment",auth, isStudent, verifyPayment) 9 | router.post("/sendPaymentSuccessEmail", auth, isStudent, sendPaymentSuccessEmail); 10 | 11 | module.exports = router -------------------------------------------------------------------------------- /server/routes/Profile.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express.Router() 3 | const { auth, isInstructor } = require("../middlewares/auth") 4 | const { 5 | deleteAccount, 6 | updateProfile, 7 | getAllUserDetails, 8 | updateDisplayPicture, 9 | getEnrolledCourses, 10 | instructorDashboard, 11 | } = require("../controllers/Profile") 12 | 13 | // ******************************************************************************************************** 14 | // Profile routes 15 | // ******************************************************************************************************** 16 | // Delet User Account 17 | router.delete("/deleteProfile", auth, deleteAccount) 18 | router.put("/updateProfile", auth, updateProfile) 19 | router.get("/getUserDetails", auth, getAllUserDetails) 20 | // Get Enrolled Courses 21 | router.get("/getEnrolledCourses", auth, getEnrolledCourses) 22 | router.put("/updateDisplayPicture", auth, updateDisplayPicture) 23 | router.get("/instructorDashboard", auth, isInstructor, instructorDashboard) 24 | 25 | module.exports = router -------------------------------------------------------------------------------- /server/routes/User.js: -------------------------------------------------------------------------------- 1 | // Import the required modules 2 | const express = require("express") 3 | const router = express.Router() 4 | 5 | // Import the required controllers and middleware functions 6 | const { 7 | login, 8 | signup, 9 | sendotp, 10 | changePassword, 11 | } = require("../controllers/Auth") 12 | const { 13 | resetPasswordToken, 14 | resetPassword, 15 | } = require("../controllers/ResetPassword") 16 | 17 | const { auth } = require("../middlewares/auth") 18 | 19 | // Routes for Login, Signup, and Authentication 20 | 21 | // ******************************************************************************************************** 22 | // Authentication routes 23 | // ******************************************************************************************************** 24 | 25 | // Route for user login 26 | router.post("/login", login) 27 | 28 | // Route for user signup 29 | router.post("/signup", signup) 30 | 31 | // Route for sending OTP to the user's email 32 | router.post("/sendotp", sendotp) 33 | 34 | // Route for Changing the password 35 | router.post("/changepassword", auth, changePassword) 36 | 37 | // ******************************************************************************************************** 38 | // Reset Password 39 | // ******************************************************************************************************** 40 | 41 | // Route for generating a reset password token 42 | router.post("/reset-password-token", resetPasswordToken) 43 | 44 | // Route for resetting user's password after verification 45 | router.post("/reset-password", resetPassword) 46 | 47 | // Export the router for use in the main application 48 | module.exports = router -------------------------------------------------------------------------------- /server/utils/imageUploader.js: -------------------------------------------------------------------------------- 1 | const cloudinary = require('cloudinary').v2 2 | 3 | 4 | exports.uploadImageToCloudinary = async (file, folder, height, quality) => { 5 | const options = {folder}; 6 | if(height) { 7 | options.height = height; 8 | } 9 | if(quality) { 10 | options.quality = quality; 11 | } 12 | options.resource_type = "auto"; 13 | 14 | return await cloudinary.uploader.upload(file.tempFilePath, options); 15 | } -------------------------------------------------------------------------------- /server/utils/mailSender.js: -------------------------------------------------------------------------------- 1 | const nodemailer = require("nodemailer"); 2 | 3 | const mailSender = async (email, title, body) => { 4 | try{ 5 | let transporter = nodemailer.createTransport({ 6 | host:process.env.MAIL_HOST, 7 | //add certification 8 | port: 465, 9 | secure: true, 10 | auth:{ 11 | user: process.env.MAIL_USER, 12 | pass: process.env.MAIL_PASS, 13 | } 14 | }) 15 | 16 | 17 | let info = await transporter.sendMail({ 18 | from: 'StudyNotion || CodeHelp - by Babbar', 19 | to:`${email}`, 20 | subject: `${title}`, 21 | html: `${body}`, 22 | }) 23 | console.log(info); 24 | return info; 25 | } 26 | catch(error) { 27 | console.log(error); 28 | //throw right error 29 | throw error; 30 | } 31 | } 32 | 33 | 34 | module.exports = mailSender; -------------------------------------------------------------------------------- /server/utils/secToDuration.js: -------------------------------------------------------------------------------- 1 | // Helper function to convert total seconds to the duration format 2 | function convertSecondsToDuration(totalSeconds) { 3 | const hours = Math.floor(totalSeconds / 3600) 4 | const minutes = Math.floor((totalSeconds % 3600) / 60) 5 | const seconds = Math.floor((totalSeconds % 3600) % 60) 6 | 7 | if (hours > 0) { 8 | return `${hours}h ${minutes}m` 9 | } else if (minutes > 0) { 10 | return `${minutes}m ${seconds}s` 11 | } else { 12 | return `${seconds}s` 13 | } 14 | } 15 | 16 | module.exports = { 17 | convertSecondsToDuration, 18 | } -------------------------------------------------------------------------------- /src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Muskansahuincredible/StudyNotion-An-Online-Education-Platform/510af9c37a48b8eec913c5f6e98018665903ac3c/src/.DS_Store -------------------------------------------------------------------------------- /src/assets/Images/404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Muskansahuincredible/StudyNotion-An-Online-Education-Platform/510af9c37a48b8eec913c5f6e98018665903ac3c/src/assets/Images/404.png -------------------------------------------------------------------------------- /src/assets/Images/Compare_with_others.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Muskansahuincredible/StudyNotion-An-Online-Education-Platform/510af9c37a48b8eec913c5f6e98018665903ac3c/src/assets/Images/Compare_with_others.png -------------------------------------------------------------------------------- /src/assets/Images/FoundingStory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Muskansahuincredible/StudyNotion-An-Online-Education-Platform/510af9c37a48b8eec913c5f6e98018665903ac3c/src/assets/Images/FoundingStory.png -------------------------------------------------------------------------------- /src/assets/Images/Instructor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Muskansahuincredible/StudyNotion-An-Online-Education-Platform/510af9c37a48b8eec913c5f6e98018665903ac3c/src/assets/Images/Instructor.png -------------------------------------------------------------------------------- /src/assets/Images/Know_your_progress.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Muskansahuincredible/StudyNotion-An-Online-Education-Platform/510af9c37a48b8eec913c5f6e98018665903ac3c/src/assets/Images/Know_your_progress.png -------------------------------------------------------------------------------- /src/assets/Images/Plan_your_lessons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Muskansahuincredible/StudyNotion-An-Online-Education-Platform/510af9c37a48b8eec913c5f6e98018665903ac3c/src/assets/Images/Plan_your_lessons.png -------------------------------------------------------------------------------- /src/assets/Images/TimelineImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Muskansahuincredible/StudyNotion-An-Online-Education-Platform/510af9c37a48b8eec913c5f6e98018665903ac3c/src/assets/Images/TimelineImage.png -------------------------------------------------------------------------------- /src/assets/Images/aboutus1.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Muskansahuincredible/StudyNotion-An-Online-Education-Platform/510af9c37a48b8eec913c5f6e98018665903ac3c/src/assets/Images/aboutus1.webp -------------------------------------------------------------------------------- /src/assets/Images/aboutus2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Muskansahuincredible/StudyNotion-An-Online-Education-Platform/510af9c37a48b8eec913c5f6e98018665903ac3c/src/assets/Images/aboutus2.webp -------------------------------------------------------------------------------- /src/assets/Images/aboutus3.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Muskansahuincredible/StudyNotion-An-Online-Education-Platform/510af9c37a48b8eec913c5f6e98018665903ac3c/src/assets/Images/aboutus3.webp -------------------------------------------------------------------------------- /src/assets/Images/ads_click_24dp_FFFFFF_FILL0_wght400_GRAD0_opsz24 (2).svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/Images/banner.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Muskansahuincredible/StudyNotion-An-Online-Education-Platform/510af9c37a48b8eec913c5f6e98018665903ac3c/src/assets/Images/banner.mp4 -------------------------------------------------------------------------------- /src/assets/Images/boxoffice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Muskansahuincredible/StudyNotion-An-Online-Education-Platform/510af9c37a48b8eec913c5f6e98018665903ac3c/src/assets/Images/boxoffice.png -------------------------------------------------------------------------------- /src/assets/Images/frame.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Muskansahuincredible/StudyNotion-An-Online-Education-Platform/510af9c37a48b8eec913c5f6e98018665903ac3c/src/assets/Images/frame.png -------------------------------------------------------------------------------- /src/assets/Images/image1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Muskansahuincredible/StudyNotion-An-Online-Education-Platform/510af9c37a48b8eec913c5f6e98018665903ac3c/src/assets/Images/image1.png -------------------------------------------------------------------------------- /src/assets/Images/image2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Muskansahuincredible/StudyNotion-An-Online-Education-Platform/510af9c37a48b8eec913c5f6e98018665903ac3c/src/assets/Images/image2.jpg -------------------------------------------------------------------------------- /src/assets/Images/image3.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Muskansahuincredible/StudyNotion-An-Online-Education-Platform/510af9c37a48b8eec913c5f6e98018665903ac3c/src/assets/Images/image3.webp -------------------------------------------------------------------------------- /src/assets/Images/image4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Muskansahuincredible/StudyNotion-An-Online-Education-Platform/510af9c37a48b8eec913c5f6e98018665903ac3c/src/assets/Images/image4.jpg -------------------------------------------------------------------------------- /src/assets/Images/image5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Muskansahuincredible/StudyNotion-An-Online-Education-Platform/510af9c37a48b8eec913c5f6e98018665903ac3c/src/assets/Images/image5.jpg -------------------------------------------------------------------------------- /src/assets/Images/image6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Muskansahuincredible/StudyNotion-An-Online-Education-Platform/510af9c37a48b8eec913c5f6e98018665903ac3c/src/assets/Images/image6.jpg -------------------------------------------------------------------------------- /src/assets/Images/image7.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Muskansahuincredible/StudyNotion-An-Online-Education-Platform/510af9c37a48b8eec913c5f6e98018665903ac3c/src/assets/Images/image7.jpeg -------------------------------------------------------------------------------- /src/assets/Images/image8.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Muskansahuincredible/StudyNotion-An-Online-Education-Platform/510af9c37a48b8eec913c5f6e98018665903ac3c/src/assets/Images/image8.webp -------------------------------------------------------------------------------- /src/assets/Images/image9.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Muskansahuincredible/StudyNotion-An-Online-Education-Platform/510af9c37a48b8eec913c5f6e98018665903ac3c/src/assets/Images/image9.webp -------------------------------------------------------------------------------- /src/assets/Images/loaderbg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Muskansahuincredible/StudyNotion-An-Online-Education-Platform/510af9c37a48b8eec913c5f6e98018665903ac3c/src/assets/Images/loaderbg.jpg -------------------------------------------------------------------------------- /src/assets/Images/login.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Muskansahuincredible/StudyNotion-An-Online-Education-Platform/510af9c37a48b8eec913c5f6e98018665903ac3c/src/assets/Images/login.webp -------------------------------------------------------------------------------- /src/assets/Images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Muskansahuincredible/StudyNotion-An-Online-Education-Platform/510af9c37a48b8eec913c5f6e98018665903ac3c/src/assets/Images/logo.png -------------------------------------------------------------------------------- /src/assets/Images/preloader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Muskansahuincredible/StudyNotion-An-Online-Education-Platform/510af9c37a48b8eec913c5f6e98018665903ac3c/src/assets/Images/preloader.gif -------------------------------------------------------------------------------- /src/assets/Images/signup.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Muskansahuincredible/StudyNotion-An-Online-Education-Platform/510af9c37a48b8eec913c5f6e98018665903ac3c/src/assets/Images/signup.webp -------------------------------------------------------------------------------- /src/assets/Logo/Logo-Full-Dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Muskansahuincredible/StudyNotion-An-Online-Education-Platform/510af9c37a48b8eec913c5f6e98018665903ac3c/src/assets/Logo/Logo-Full-Dark.png -------------------------------------------------------------------------------- /src/assets/Logo/Logo-Full-Light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Muskansahuincredible/StudyNotion-An-Online-Education-Platform/510af9c37a48b8eec913c5f6e98018665903ac3c/src/assets/Logo/Logo-Full-Light.png -------------------------------------------------------------------------------- /src/assets/Logo/Logo-Small-Dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Muskansahuincredible/StudyNotion-An-Online-Education-Platform/510af9c37a48b8eec913c5f6e98018665903ac3c/src/assets/Logo/Logo-Small-Dark.png -------------------------------------------------------------------------------- /src/assets/Logo/Logo-Small-Light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Muskansahuincredible/StudyNotion-An-Online-Education-Platform/510af9c37a48b8eec913c5f6e98018665903ac3c/src/assets/Logo/Logo-Small-Light.png -------------------------------------------------------------------------------- /src/assets/Logo/rzp_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Muskansahuincredible/StudyNotion-An-Online-Education-Platform/510af9c37a48b8eec913c5f6e98018665903ac3c/src/assets/Logo/rzp_logo.png -------------------------------------------------------------------------------- /src/assets/TimeLineLogo/Logo1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/TimeLineLogo/Logo2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/TimeLineLogo/Logo3.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/TimeLineLogo/Logo4.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/components/ContactPage/ContactDetails.jsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import * as Icon1 from "react-icons/bi" 3 | import * as Icon3 from "react-icons/hi2" 4 | import * as Icon2 from "react-icons/io5" 5 | 6 | const contactDetails = [ 7 | { 8 | icon: "HiChatBubbleLeftRight", 9 | heading: "Chat with us", 10 | description: "Our friendly team is here to help.", 11 | details: "info@studynotion.com", 12 | linkType: "email" 13 | }, 14 | { 15 | icon: "BiWorld", 16 | heading: "Visit us", 17 | description: "Come and say hello at our office HQ.", 18 | details: 19 | "Akshya Nagar 1st Block 1st Cross, Rammurthy nagar, Bangalore-560016", 20 | linkType: null 21 | }, 22 | { 23 | icon: "IoCall", 24 | heading: "Call us", 25 | description: "Mon - Fri From 8am to 5pm", 26 | details: "+123 456 7869", 27 | linkType: "phone" 28 | }, 29 | ] 30 | 31 | const ContactDetails = () => { 32 | return ( 33 |
34 | {contactDetails.map((ele, i) => { 35 | let Icon = Icon1[ele.icon] || Icon2[ele.icon] || Icon3[ele.icon]; 36 | let detailElement; 37 | 38 | if (ele.linkType === "email") { 39 | detailElement = ( 40 | 41 | {ele.details} 42 | 43 | ); 44 | } else if (ele.linkType === "phone") { 45 | detailElement = ( 46 | 47 | {ele.details} 48 | 49 | ); 50 | } else { 51 | detailElement =

{ele.details}

; 52 | } 53 | 54 | return ( 55 |
59 |
60 | 61 |

62 | {ele?.heading} 63 |

64 |
65 |

{ele?.description}

66 | {detailElement} 67 |
68 | ) 69 | })} 70 |
71 | ) 72 | } 73 | 74 | export default ContactDetails -------------------------------------------------------------------------------- /src/components/ContactPage/ContactForm.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ContactUsForm from "./ContactUsForm"; 3 | 4 | const ContactForm = () => { 5 | return ( 6 |
7 |

11 | Got an Idea? We've got the skills. Let's team up !! 12 |

13 |

14 | Tell us more about yourself and what you're got in mind. 15 |

16 | 17 |
18 | 19 |
20 |
21 | ); 22 | }; 23 | 24 | export default ContactForm; 25 | -------------------------------------------------------------------------------- /src/components/common/BackToTop.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp'; 3 | 4 | const BackToTop = () => { 5 | const [isVisible, setIsVisible] = useState(false); 6 | 7 | useEffect(() => { 8 | const handleScroll = () => { 9 | if (document.body.scrollTop > 20 || document.documentElement.scrollTop > 20) { 10 | setIsVisible(true); 11 | } else { 12 | setIsVisible(false); 13 | } 14 | }; 15 | 16 | window.addEventListener("scroll", handleScroll); 17 | return () => window.removeEventListener("scroll", handleScroll); 18 | }, []); 19 | 20 | const handleClick = () => { 21 | document.body.scrollTop = 0; 22 | document.documentElement.scrollTop = 0; 23 | }; 24 | 25 | return ( 26 | 34 | ); 35 | }; 36 | 37 | export default BackToTop; 38 | -------------------------------------------------------------------------------- /src/components/common/ConfirmationModal.jsx: -------------------------------------------------------------------------------- 1 | import IconBtn from "./IconBtn" 2 | 3 | export default function ConfirmationModal({ modalData }) { 4 | return ( 5 |
6 |
7 |

8 | {modalData?.text1} 9 |

10 |

11 | {modalData?.text2} 12 |

13 |
14 | 18 | 24 |
25 |
26 |
27 | ) 28 | } 29 | -------------------------------------------------------------------------------- /src/components/common/IconBtn.jsx: -------------------------------------------------------------------------------- 1 | export default function IconBtn({ 2 | text, 3 | onclick, 4 | children, 5 | disabled, 6 | outline = false, 7 | customClasses, 8 | type, 9 | }) { 10 | return ( 11 | 28 | ) 29 | } -------------------------------------------------------------------------------- /src/components/common/Loading.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { PropagateLoader } from 'react-spinners'; 3 | import loadingGif from '../../assets/Images/preloader.gif'; 4 | import bg from "../../assets/Images/loaderbg.jpg" 5 | 6 | const Loading = () => { 7 | return ( 8 | <> 9 |
10 | Loading GIF 15 | 16 |
17 | 18 | ); 19 | }; 20 | 21 | const styles = { 22 | loaderContainer: { 23 | display: 'flex', 24 | flexDirection: 'column', 25 | justifyContent: 'center', 26 | alignItems: 'center', 27 | width: '100vw', 28 | height: '100vh', 29 | backgroundColor: 'white', 30 | backgroundSize: 'cover', 31 | backgroundPosition: 'center', 32 | }, 33 | gif: { 34 | marginBottom: '20px', 35 | height: '250px', 36 | width: 'auto', 37 | backgroundColor: 'none' 38 | }, 39 | }; 40 | 41 | export default Loading; 42 | -------------------------------------------------------------------------------- /src/components/common/RatingStars.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react" 2 | import { 3 | TiStarFullOutline, 4 | TiStarHalfOutline, 5 | TiStarOutline, 6 | } from "react-icons/ti" 7 | 8 | function RatingStars({ Review_Count, Star_Size }) { 9 | const [starCount, SetStarCount] = useState({ 10 | full: 0, 11 | half: 0, 12 | empty: 0, 13 | }) 14 | 15 | useEffect(() => { 16 | const wholeStars = Math.floor(Review_Count) || 0 17 | SetStarCount({ 18 | full: wholeStars, 19 | half: Number.isInteger(Review_Count) ? 0 : 1, 20 | empty: Number.isInteger(Review_Count) ? 5 - wholeStars : 4 - wholeStars, 21 | }) 22 | }, [Review_Count]) 23 | return ( 24 |
25 | {[...new Array(starCount.full)].map((_, i) => { 26 | return 27 | })} 28 | {[...new Array(starCount.half)].map((_, i) => { 29 | return 30 | })} 31 | {[...new Array(starCount.empty)].map((_, i) => { 32 | return 33 | })} 34 |
35 | ) 36 | } 37 | 38 | export default RatingStars -------------------------------------------------------------------------------- /src/components/common/Tab.jsx: -------------------------------------------------------------------------------- 1 | export default function Tab({ tabData, field, setField }) { 2 | return ( 3 |
9 | {tabData.map((tab) => ( 10 | 21 | ))} 22 |
23 | ); 24 | } -------------------------------------------------------------------------------- /src/components/common/progess.css: -------------------------------------------------------------------------------- 1 | .progress-container { 2 | width: 100%; 3 | height: 3px; 4 | background-color:yellow; 5 | position: fixed; /* Ensure the bar stays fixed at the top */ 6 | 7 | z-index: 999; 8 | /* Smooth transition */ 9 | } 10 | 11 | .progress-bar { 12 | height: 100%; 13 | background-color: cyan; 14 | width: 60%; 15 | transition: width 0.2s ease-in-out; 16 | } -------------------------------------------------------------------------------- /src/components/common/progressbar.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import './progess.css'; 3 | 4 | const ProgressBar = () => { 5 | const [scrollProgress, setScrollProgress] = useState(0); 6 | 7 | const handleScroll = () => { 8 | const totalScroll = document.documentElement.scrollTop; 9 | const windowHeight = document.documentElement.scrollHeight - document.documentElement.clientHeight; 10 | const scroll = `${totalScroll / windowHeight * 100}`; 11 | setScrollProgress(scroll); 12 | }; 13 | 14 | useEffect(() => { 15 | window.addEventListener('scroll', handleScroll); 16 | return () => window.removeEventListener('scroll', handleScroll); 17 | }, []); 18 | 19 | return ( 20 |
21 |
25 | 26 |
27 |
28 | ); 29 | }; 30 | 31 | export default ProgressBar; 32 | -------------------------------------------------------------------------------- /src/components/core/AboutPage/ContactFormSection.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ContactUsForm from "../../ContactPage/ContactUsForm"; 3 | 4 | const ContactFormSection = () => { 5 | return ( 6 |
7 |

Get in Touch

8 |

9 | We'd love to hear form you. Please fill out this form. 10 |

11 | {/*
*/} 12 | 13 | {/*
*/} 14 |
15 | ); 16 | }; 17 | 18 | export default ContactFormSection; -------------------------------------------------------------------------------- /src/components/core/AboutPage/LearningGrid.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import HighlightText from "../../../components/core/HomePage/HighlightText"; 3 | import CTAButton from "../../../components/core/HomePage/Button"; 4 | 5 | const LearningGridArray = [ 6 | { 7 | order: -1, 8 | heading: "World-Class Learning for", 9 | highlightText: "Anyone, Anywhere", 10 | description: 11 | "Studynotion partners with more than 275+ leading universities and companies to bring flexible, affordable, job-relevant online learning to individuals and organizations worldwide.", 12 | BtnText: "Learn More", 13 | BtnLink: "/", 14 | }, 15 | { 16 | order: 1, 17 | heading: "Curriculum Based on Industry Needs", 18 | description: 19 | "Save time and money! The Belajar curriculum is made to be easier to understand and in line with industry needs.", 20 | }, 21 | { 22 | order: 2, 23 | heading: "Our Learning Methods", 24 | description: 25 | "Our learning method combines flexible and practical approaches to ensure a comprehensive and engaging educational experience.", 26 | }, 27 | { 28 | order: 3, 29 | heading: "Certification", 30 | description: 31 | "Studynotion provides industry-recognized certification to validate your new skills and enhance your career prospects.", 32 | }, 33 | { 34 | order: 4, 35 | heading: `Rating "Auto-grading"`, 36 | description: 37 | "Studynotion’s auto-grading feature provides instant, objective feedback to help learners assess their understanding and progress efficiently.", 38 | }, 39 | { 40 | order: 5, 41 | heading: "Ready to Work", 42 | description: 43 | "Studynotion equips learners with job-ready skills, preparing them to excel in the workforce.", 44 | }, 45 | ]; 46 | 47 | const LearningGrid = () => { 48 | return ( 49 |
50 | {LearningGridArray.map((card, i) => { 51 | const isHighlightCard = card.order < 0; 52 | const cardBgClass = 53 | card.order % 2 === 1 54 | ? "bg-richblack-600" 55 | : card.order % 2 === 0 56 | ? "bg-richblack-800" 57 | : "bg-transparent"; 58 | const cardHeightClass = "h-[320px]"; 59 | const colSpanClass = i === 0 ? "xl:col-span-2" : ""; 60 | const colStartClass = card.order === 3 ? "xl:col-start-2" : ""; 61 | 62 | return ( 63 |
67 | {isHighlightCard ? ( 68 |
69 |
70 | {card.heading} 71 | 72 |
73 |

74 | {card.description} 75 |

76 |
77 | 78 | {card.BtnText} 79 | 80 |
81 |
82 | ) : ( 83 |
84 |

85 | {card.heading} 86 |

87 |

88 | {card.description} 89 |

90 |
91 | )} 92 |
93 | ); 94 | })} 95 |
96 | ); 97 | }; 98 | 99 | export default LearningGrid; 100 | -------------------------------------------------------------------------------- /src/components/core/AboutPage/Quote.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import HighlightText from '../HomePage/HighlightText' 3 | 4 | const Quote = () => { 5 | return ( 6 |
7 | We are passionate about revolutionizing the way we learn. Our 8 | innovative platform ,{" "} 9 | 10 | {" "} 11 | expertise 12 | 13 | , and community to create an 14 | 15 | {" "} 16 | unparalleled educational 17 | experience. 18 | 19 |
20 | ) 21 | } 22 | 23 | export default Quote -------------------------------------------------------------------------------- /src/components/core/AboutPage/Stats.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useRef } from 'react'; 2 | import useIntersectionObserver from './useIntersectionObserver'; // Adjust the path as needed 3 | 4 | const countUp = (element, start, end, duration, finalDisplay) => { 5 | let startTime = null; 6 | 7 | const animation = (currentTime) => { 8 | if (startTime === null) startTime = currentTime; 9 | const elapsedTime = currentTime - startTime; 10 | const progress = Math.min(elapsedTime / duration, 1); 11 | const currentNumber = Math.floor(progress * (end - start) + start); 12 | 13 | element.textContent = currentNumber.toLocaleString(); // Format number with commas 14 | 15 | if (progress < 1) { 16 | requestAnimationFrame(animation); 17 | } else { 18 | element.textContent = finalDisplay; // Set final display value 19 | } 20 | }; 21 | 22 | requestAnimationFrame(animation); 23 | }; 24 | 25 | const parseCountValue = (count) => { 26 | if (count.endsWith('K')) { 27 | return parseFloat(count) * 1000; 28 | } else { 29 | return parseFloat(count); 30 | } 31 | }; 32 | 33 | const getFinalDisplay = (count) => { 34 | if (count.endsWith('K')) { 35 | return count; 36 | } else { 37 | return parseFloat(count).toLocaleString(); 38 | } 39 | }; 40 | 41 | const StatsComponent = () => { 42 | const stats = [ 43 | { count: '5K', label: 'Active Students' }, 44 | { count: '10', label: 'Mentors' }, 45 | { count: '200', label: 'Courses' }, 46 | { count: '50', label: 'Awards' }, 47 | ]; 48 | 49 | const elementsRef = useRef([]); 50 | const [isIntersecting, setElement] = useIntersectionObserver({ 51 | threshold: 0.5, // Adjust as needed 52 | }); 53 | 54 | useEffect(() => { 55 | if (isIntersecting) { 56 | elementsRef.current.forEach((element, index) => { 57 | const countValue = stats[index].count; 58 | const endValue = parseCountValue(countValue); 59 | const finalDisplay = getFinalDisplay(countValue); 60 | countUp(element, 0, endValue, 2000, finalDisplay); 61 | }); 62 | } 63 | }, [isIntersecting]); 64 | 65 | return ( 66 |
67 | {/* Stats */} 68 |
69 |
70 | {stats.map((data, index) => ( 71 |
72 |
73 |

(elementsRef.current[index] = el)} 76 | > 77 | {parseCountValue(data.count).toLocaleString()} 78 |

79 | + 80 |
81 |
82 |

83 | {data.label} 84 |

85 |
86 |
87 | ))} 88 |
89 |
90 |
91 | ); 92 | }; 93 | 94 | export default StatsComponent; 95 | -------------------------------------------------------------------------------- /src/components/core/AboutPage/useIntersectionObserver.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef, useState } from 'react'; 2 | 3 | const useIntersectionObserver = (options) => { 4 | const [isIntersecting, setIsIntersecting] = useState(false); 5 | const observerRef = useRef(null); 6 | 7 | const setElement = (element) => { 8 | if (observerRef.current) { 9 | observerRef.current.disconnect(); 10 | } 11 | 12 | if (element) { 13 | observerRef.current = new IntersectionObserver( 14 | ([entry]) => { 15 | setIsIntersecting(entry.isIntersecting); 16 | }, 17 | { ...options } 18 | ); 19 | 20 | observerRef.current.observe(element); 21 | } 22 | }; 23 | 24 | return [isIntersecting, setElement]; 25 | }; 26 | 27 | export default useIntersectionObserver; 28 | -------------------------------------------------------------------------------- /src/components/core/Auth/LoginForm.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react" 2 | import { AiOutlineEye, AiOutlineEyeInvisible } from "react-icons/ai" 3 | import { useDispatch } from "react-redux" 4 | import { Link, useNavigate } from "react-router-dom" 5 | import { SiGmail } from "react-icons/si"; 6 | import { FaLock } from "react-icons/fa"; 7 | import { login } from "../../../services/operations/authAPI" 8 | function LoginForm() { 9 | const navigate = useNavigate() 10 | const dispatch = useDispatch() 11 | const [formData, setFormData] = useState({ 12 | email: "", 13 | password: "", 14 | }) 15 | 16 | const [showPassword, setShowPassword] = useState(false) 17 | 18 | const { email, password } = formData 19 | 20 | const handleOnChange = (e) => { 21 | setFormData((prevData) => ({ 22 | ...prevData, 23 | [e.target.name]: e.target.value, 24 | })) 25 | } 26 | 27 | const handleOnSubmit = (e) => { 28 | e.preventDefault() 29 | dispatch(login(email, password, navigate)) 30 | } 31 | 32 | return ( 33 |
37 | 57 | 91 | 97 |
98 | ) 99 | } 100 | 101 | export default LoginForm -------------------------------------------------------------------------------- /src/components/core/Auth/OpenRoute.jsx: -------------------------------------------------------------------------------- 1 | // This will prevent authenticated users from accessing this route 2 | import { useSelector } from "react-redux" 3 | import { Navigate } from "react-router-dom" 4 | 5 | function OpenRoute({ children }) { 6 | const { token } = useSelector((state) => state.auth) 7 | 8 | if (token === null) { 9 | return children 10 | } else { 11 | return 12 | } 13 | } 14 | 15 | export default OpenRoute -------------------------------------------------------------------------------- /src/components/core/Auth/PrivateRoute.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useSelector } from 'react-redux' 3 | import { Navigate } from 'react-router-dom'; 4 | 5 | const PrivateRoute = ({children}) => { 6 | 7 | const {token} = useSelector((state) => state.auth); 8 | 9 | if(token !== null) 10 | return children 11 | else 12 | return 13 | 14 | } 15 | 16 | export default PrivateRoute 17 | -------------------------------------------------------------------------------- /src/components/core/Auth/ProfileDropDown.jsx: -------------------------------------------------------------------------------- 1 | import { useRef, useState } from "react" 2 | import { AiOutlineCaretDown } from "react-icons/ai" 3 | import { VscDashboard, VscSignOut } from "react-icons/vsc" 4 | import { useDispatch, useSelector } from "react-redux" 5 | import { Link, useNavigate } from "react-router-dom" 6 | 7 | import useOnClickOutside from "../../../hooks/useOnClickOutside" 8 | import { logout } from "../../../services/operations/authAPI" 9 | 10 | export default function ProfileDropdown() { 11 | const { user } = useSelector((state) => state.profile) 12 | const dispatch = useDispatch() 13 | const navigate = useNavigate() 14 | const [open, setOpen] = useState(false) 15 | const ref = useRef(null) 16 | 17 | useOnClickOutside(ref, () => setOpen(false)) 18 | 19 | if (!user) return null 20 | 21 | return ( 22 | 56 | ) 57 | } -------------------------------------------------------------------------------- /src/components/core/Auth/Template.jsx: -------------------------------------------------------------------------------- 1 | import { FcGoogle } from "react-icons/fc" 2 | import { useSelector } from "react-redux" 3 | 4 | import frameImg from "../../../assets/Images/frame.png" 5 | import LoginForm from "./LoginForm" 6 | import SignupForm from "./SignupForm" 7 | 8 | function Template({ title, description1, description2, image, formType }) { 9 | const { loading } = useSelector((state) => state.auth) 10 | 11 | return ( 12 |
13 | {loading ? ( 14 |
15 | ) : ( 16 |
17 |
18 |

19 | {title} 20 |

21 |

22 | {description1}{" "} 23 | 24 | {description2} 25 | 26 |

27 | {formType === "signup" ? : } 28 |
29 |
30 | Pattern 37 | Students 45 |
46 |
47 | )} 48 |
49 | ) 50 | } 51 | 52 | export default Template -------------------------------------------------------------------------------- /src/components/core/Catalog/CourseSlider.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Swiper, SwiperSlide } from 'swiper/react'; 3 | import 'swiper/css'; 4 | import 'swiper/css/free-mode'; 5 | import 'swiper/css/pagination'; 6 | import 'swiper/css/navigation'; 7 | import 'swiper/css/autoplay'; 8 | import { FreeMode, Pagination, Autoplay, Navigation } from 'swiper/modules'; 9 | 10 | import CourseCard from './Course_Card'; 11 | 12 | const CourseSlider = ({ Courses = [] }) => { 13 | return ( 14 | <> 15 | {Courses.length > 0 ? ( 16 | 32 | {Courses.map((course, i) => ( 33 | 34 | 35 | 36 | ))} 37 | 38 | ) : ( 39 |

No Course Found

40 | )} 41 | 42 | ); 43 | }; 44 | 45 | export default CourseSlider; 46 | -------------------------------------------------------------------------------- /src/components/core/Catalog/Course_Card.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react' 2 | import RatingStars from '../../common/RatingStars' 3 | import GetAvgRating from '../../../utils/avgRating'; 4 | import { Link } from 'react-router-dom'; 5 | 6 | const Course_Card = ({course, Height}) => { 7 | 8 | 9 | const [avgReviewCount, setAvgReviewCount] = useState(0); 10 | 11 | useEffect(()=> { 12 | const count = GetAvgRating(course.ratingAndReviews); 13 | setAvgReviewCount(count); 14 | },[course]) 15 | 16 | 17 | 18 | return ( 19 | <> 20 | 21 |
22 |
23 | course thumnail 28 |
29 |
30 |

{course?.courseName}

31 |

32 | {course?.instructor?.firstName} {course?.instructor?.lastName} 33 |

34 |
35 | {avgReviewCount || 0} 36 | 37 | 38 | {course?.ratingAndReviews?.length} Ratings 39 | 40 |
41 |

Rs. {course?.price}

42 |
43 |
44 | 45 | 46 | ) 47 | } 48 | 49 | export default Course_Card 50 | -------------------------------------------------------------------------------- /src/components/core/Course/CourseAccordionBar.jsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef, useState } from "react" 2 | import { AiOutlineDown } from "react-icons/ai" 3 | 4 | import CourseSubSectionAccordion from "./CourseSubSectionAccordion" 5 | 6 | export default function CourseAccordionBar({ course, isActive, handleActive }) { 7 | const contentEl = useRef(null) 8 | 9 | // Accordian state 10 | const [active, setActive] = useState(false) 11 | useEffect(() => { 12 | setActive(isActive?.includes(course._id)) 13 | }, [isActive]) 14 | const [sectionHeight, setSectionHeight] = useState(0) 15 | useEffect(() => { 16 | setSectionHeight(active ? contentEl.current.scrollHeight : 0) 17 | }, [active]) 18 | 19 | return ( 20 |
21 |
22 |
{ 25 | handleActive(course._id) 26 | }} 27 | > 28 |
29 | 34 | 35 | 36 |

{course?.sectionName}

37 |
38 |
39 | 40 | {`${course.subSection.length || 0} lecture(s)`} 41 | 42 |
43 |
44 |
45 |
52 |
53 | {course?.subSection?.map((subSec, i) => { 54 | return 55 | })} 56 |
57 |
58 |
59 | ) 60 | } -------------------------------------------------------------------------------- /src/components/core/Course/CourseSubSectionAccordion.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useRef, useState } from "react" 2 | import { AiOutlineDown } from "react-icons/ai" 3 | import { HiOutlineVideoCamera } from "react-icons/hi" 4 | 5 | function CourseSubSectionAccordion({ subSec }) { 6 | return ( 7 |
8 |
9 |
10 | 11 | 12 | 13 |

{subSec?.title}

14 |
15 |
16 |
17 | ) 18 | } 19 | 20 | export default CourseSubSectionAccordion -------------------------------------------------------------------------------- /src/components/core/Dashboard/AddCourse/CourseInformation/ChipInput.jsx: -------------------------------------------------------------------------------- 1 | // Importing React hook for managing component state 2 | import { useEffect, useState } from "react" 3 | // Importing React icon component 4 | import { MdClose } from "react-icons/md" 5 | import { useSelector } from "react-redux" 6 | 7 | // Defining a functional component ChipInput 8 | export default function ChipInput({ 9 | // Props to be passed to the component 10 | label, 11 | name, 12 | placeholder, 13 | register, 14 | errors, 15 | setValue, 16 | getValues, 17 | }) { 18 | const { editCourse, course } = useSelector((state) => state.course) 19 | 20 | // Setting up state for managing chips array 21 | const [chips, setChips] = useState([]) 22 | 23 | useEffect(() => { 24 | if (editCourse) { 25 | // console.log(course) 26 | setChips(course?.tag) 27 | } 28 | register(name, { required: true, validate: (value) => value.length > 0 }) 29 | // eslint-disable-next-line react-hooks/exhaustive-deps 30 | }, []) 31 | 32 | useEffect(() => { 33 | setValue(name, chips) 34 | // eslint-disable-next-line react-hooks/exhaustive-deps 35 | }, [chips]) 36 | 37 | // Function to handle user input when chips are added 38 | const handleKeyDown = (event) => { 39 | // Check if user presses "Enter" or "," 40 | if (event.key === "Enter" || event.key === ",") { 41 | // Prevent the default behavior of the event 42 | event.preventDefault() 43 | // Get the input value and remove any leading/trailing spaces 44 | const chipValue = event.target.value.trim() 45 | // Check if the input value exists and is not already in the chips array 46 | if (chipValue && !chips.includes(chipValue)) { 47 | // Add the chip to the array and clear the input 48 | const newChips = [...chips, chipValue] 49 | setChips(newChips) 50 | event.target.value = "" 51 | } 52 | } 53 | } 54 | 55 | // Function to handle deletion of a chip 56 | const handleDeleteChip = (chipIndex) => { 57 | // Filter the chips array to remove the chip with the given index 58 | const newChips = chips.filter((_, index) => index !== chipIndex) 59 | setChips(newChips) 60 | } 61 | 62 | // Render the component 63 | return ( 64 |
65 | {/* Render the label for the input */} 66 | 69 | {/* Render the chips and input */} 70 |
71 | {/* Map over the chips array and render each chip */} 72 | {chips.map((chip, index) => ( 73 |
77 | {/* Render the chip value */} 78 | {chip} 79 | {/* Render the button to delete the chip */} 80 | 87 |
88 | ))} 89 | {/* Render the input for adding new chips */} 90 | 98 |
99 | {/* Render an error message if the input is required and not filled */} 100 | {errors[name] && ( 101 | 102 | {label} is required 103 | 104 | )} 105 |
106 | ) 107 | } -------------------------------------------------------------------------------- /src/components/core/Dashboard/AddCourse/CourseInformation/RequirementField.jsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react" 2 | import { useSelector } from "react-redux" 3 | 4 | export default function RequirementsField({ 5 | name, 6 | label, 7 | register, 8 | setValue, 9 | errors, 10 | getValues, 11 | }) { 12 | const { editCourse, course } = useSelector((state) => state.course) 13 | const [requirement, setRequirement] = useState("") 14 | const [requirementsList, setRequirementsList] = useState([]) 15 | 16 | useEffect(() => { 17 | if (editCourse) { 18 | setRequirementsList(course?.instructions) 19 | } 20 | register(name, { required: true, validate: (value) => value.length > 0 }) 21 | // eslint-disable-next-line react-hooks/exhaustive-deps 22 | }, []) 23 | 24 | useEffect(() => { 25 | setValue(name, requirementsList) 26 | // eslint-disable-next-line react-hooks/exhaustive-deps 27 | }, [requirementsList]) 28 | 29 | const handleAddRequirement = () => { 30 | if (requirement) { 31 | setRequirementsList([...requirementsList, requirement]) 32 | setRequirement("") 33 | } 34 | } 35 | 36 | const handleRemoveRequirement = (index) => { 37 | const updatedRequirements = [...requirementsList] 38 | updatedRequirements.splice(index, 1) 39 | setRequirementsList(updatedRequirements) 40 | } 41 | 42 | return ( 43 |
44 | 47 |
48 | setRequirement(e.target.value)} 53 | className="form-style w-full" 54 | /> 55 | 62 |
63 | {requirementsList.length > 0 && ( 64 |
    65 | {requirementsList.map((requirement, index) => ( 66 |
  • 67 | {requirement} 68 | 75 |
  • 76 | ))} 77 |
78 | )} 79 | {errors[name] && ( 80 | 81 | {label} is required 82 | 83 | )} 84 |
85 | ) 86 | } -------------------------------------------------------------------------------- /src/components/core/Dashboard/AddCourse/PublishCourse/index.jsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react" 2 | import { useForm } from "react-hook-form" 3 | import { useDispatch, useSelector } from "react-redux" 4 | import { useNavigate } from "react-router-dom" 5 | 6 | import { editCourseDetails } from "../../../../../services/operations/courseDetailsAPI" 7 | import { resetCourseState, setStep } from "../../../../../slices/courseSlice" 8 | import { COURSE_STATUS } from "../../../../../utils/constants" 9 | import IconBtn from "../../../../common/IconBtn" 10 | 11 | export default function PublishCourse() { 12 | const { register, handleSubmit, setValue, getValues } = useForm() 13 | 14 | const dispatch = useDispatch() 15 | const navigate = useNavigate() 16 | const { token } = useSelector((state) => state.auth) 17 | const { course } = useSelector((state) => state.course) 18 | const [loading, setLoading] = useState(false) 19 | 20 | useEffect(() => { 21 | if (course?.status === COURSE_STATUS.PUBLISHED) { 22 | setValue("public", true) 23 | } 24 | }, []) 25 | 26 | const goBack = () => { 27 | dispatch(setStep(2)) 28 | } 29 | 30 | const goToCourses = () => { 31 | dispatch(resetCourseState()) 32 | navigate("/dashboard/my-courses") 33 | } 34 | 35 | const handleCoursePublish = async () => { 36 | // check if form has been updated or not 37 | if ( 38 | (course?.status === COURSE_STATUS.PUBLISHED && 39 | getValues("public") === true) || 40 | (course?.status === COURSE_STATUS.DRAFT && getValues("public") === false) 41 | ) { 42 | // form has not been updated 43 | // no need to make api call 44 | goToCourses() 45 | return 46 | } 47 | const formData = new FormData() 48 | formData.append("courseId", course._id) 49 | const courseStatus = getValues("public") 50 | ? COURSE_STATUS.PUBLISHED 51 | : COURSE_STATUS.DRAFT 52 | formData.append("status", courseStatus) 53 | setLoading(true) 54 | const result = await editCourseDetails(formData, token) 55 | if (result) { 56 | goToCourses() 57 | } 58 | setLoading(false) 59 | } 60 | 61 | const onSubmit = (data) => { 62 | // console.log(data) 63 | handleCoursePublish() 64 | } 65 | 66 | return ( 67 |
68 |

69 | Publish Settings 70 |

71 |
72 | {/* Checkbox */} 73 |
74 | 85 |
86 | 87 | {/* Next Prev Button */} 88 |
89 | 97 | 98 |
99 |
100 |
101 | ) 102 | } -------------------------------------------------------------------------------- /src/components/core/Dashboard/AddCourse/RenderSteps.jsx: -------------------------------------------------------------------------------- 1 | import { FaCheck } from "react-icons/fa" 2 | import { useSelector } from "react-redux" 3 | 4 | import CourseBuilderForm from "./CourseBuilder/CourseBuilderForm" 5 | import CourseInformationForm from "./CourseInformation/CourseInformationForm" 6 | import PublishCourse from "./PublishCourse" 7 | 8 | 9 | export default function RenderSteps() { 10 | const { step } = useSelector((state) => state.course) 11 | 12 | const steps = [ 13 | { 14 | id: 1, 15 | title: "Course Information", 16 | }, 17 | { 18 | id: 2, 19 | title: "Course Builder", 20 | }, 21 | { 22 | id: 3, 23 | title: "Publish", 24 | }, 25 | ] 26 | 27 | return ( 28 | <> 29 |
30 | {steps.map((item) => ( 31 | <> 32 |
36 | 49 | 50 |
51 | {item.id !== steps.length && ( 52 | <> 53 |
item.id ? "border-yellow-50" : "border-richblack-500" 56 | } `} 57 | >
58 | 59 | )} 60 | 61 | ))} 62 |
63 | 64 |
65 | {steps.map((item) => ( 66 | <> 67 |
71 | 72 |

= item.id ? "text-richblack-5" : "text-richblack-500" 75 | }`} 76 | > 77 | {item.title} 78 |

79 |
80 | 81 | 82 | ))} 83 |
84 | {/* Render specific component based on current step */} 85 | {step === 1 && } 86 | {step === 2 && } 87 | {step === 3 && } 88 | 89 | ) 90 | } -------------------------------------------------------------------------------- /src/components/core/Dashboard/AddCourse/index.jsx: -------------------------------------------------------------------------------- 1 | import RenderSteps from "./RenderSteps" 2 | 3 | export default function AddCourse() { 4 | return ( 5 | <> 6 |
7 |
8 |

9 | Add Course 10 |

11 |
12 | 13 |
14 |
15 | {/* Course Upload Tips */} 16 |
17 |

⚡ Course Upload Tips

18 |
    19 |
  • Set the Course Price option or make it free.
  • 20 |
  • Standard size for the course thumbnail is 1024x576.
  • 21 |
  • Video section controls the course overview video.
  • 22 |
  • Course Builder is where you create & organize a course.
  • 23 |
  • 24 | Add Topics in the Course Builder section to create lessons, 25 | quizzes, and assignments. 26 |
  • 27 |
  • 28 | Information from the Additional Data section shows up on the 29 | course single page. 30 |
  • 31 |
  • Make Announcements to notify any important
  • 32 |
  • Notes to all enrolled students at once.
  • 33 |
34 |
35 |
36 | 37 | ) 38 | } -------------------------------------------------------------------------------- /src/components/core/Dashboard/Cart/RenderCartCourses.jsx: -------------------------------------------------------------------------------- 1 | import { FaStar } from "react-icons/fa" 2 | import { RiDeleteBin6Line } from "react-icons/ri" 3 | import ReactStars from "react-rating-stars-component" 4 | import { useDispatch, useSelector } from "react-redux" 5 | 6 | import { removeFromCart } from "../../../../slices/cartSlice" 7 | 8 | export default function RenderCartCourses() { 9 | const { cart } = useSelector((state) => state.cart) 10 | const dispatch = useDispatch() 11 | return ( 12 |
13 | {cart.map((course, indx) => ( 14 |
20 |
21 | {course?.courseName} 26 |
27 |

28 | {course?.courseName} 29 |

30 |

31 | {course?.category?.name} 32 |

33 |
34 | 4.5 35 | } 42 | fullIcon={} 43 | /> 44 | 45 | {course?.ratingAndReviews?.length} Ratings 46 | 47 |
48 |
49 |
50 |
51 | 58 |

59 | ₹ {course?.price} 60 |

61 |
62 |
63 | ))} 64 |
65 | ) 66 | } -------------------------------------------------------------------------------- /src/components/core/Dashboard/Cart/RenderTotalAmount.jsx: -------------------------------------------------------------------------------- 1 | import { useDispatch, useSelector } from "react-redux" 2 | import { useNavigate } from "react-router-dom" 3 | 4 | import IconBtn from "../../../common/IconBtn" 5 | import { buyCourse } from "../../../../services/operations/studentFeaturesAPI" 6 | 7 | export default function RenderTotalAmount() { 8 | const { total, cart } = useSelector((state) => state.cart) 9 | const { token } = useSelector((state) => state.auth) 10 | const { user } = useSelector((state) => state.profile) 11 | const navigate = useNavigate() 12 | const dispatch = useDispatch() 13 | 14 | const handleBuyCourse = () => { 15 | const courses = cart.map((course) => course._id) 16 | buyCourse(token, courses, user, navigate, dispatch) 17 | } 18 | 19 | return ( 20 |
21 |

Total:

22 |

₹ {total}

23 | 28 |
29 | ) 30 | } -------------------------------------------------------------------------------- /src/components/core/Dashboard/Cart/index.jsx: -------------------------------------------------------------------------------- 1 | import { useSelector } from "react-redux" 2 | 3 | import RenderCartCourses from "./RenderCartCourses" 4 | import RenderTotalAmount from "./RenderTotalAmount" 5 | 6 | export default function Cart() { 7 | const { total, totalItems } = useSelector((state) => state.cart) 8 | 9 | return ( 10 | <> 11 |

Cart

12 |

13 | {totalItems} Courses in Cart 14 |

15 | {total > 0 ? ( 16 |
17 | 18 | 19 |
20 | ) : ( 21 |

22 | Your cart is empty 23 |

24 | )} 25 | 26 | ) 27 | } -------------------------------------------------------------------------------- /src/components/core/Dashboard/EditCourse/index.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react" 2 | import { useDispatch, useSelector } from "react-redux" 3 | import { useParams } from "react-router-dom" 4 | 5 | import { 6 | fetchCourseDetails, 7 | getFullDetailsOfCourse, 8 | } from "../../../../services/operations/courseDetailsAPI" 9 | import { setCourse, setEditCourse } from "../../../../slices/courseSlice" 10 | import RenderSteps from "../AddCourse/RenderSteps" 11 | 12 | export default function EditCourse() { 13 | const dispatch = useDispatch() 14 | const { courseId } = useParams() 15 | const { course } = useSelector((state) => state.course) 16 | const [loading, setLoading] = useState(false) 17 | const { token } = useSelector((state) => state.auth) 18 | 19 | useEffect(() => { 20 | ;(async () => { 21 | setLoading(true) 22 | const result = await getFullDetailsOfCourse(courseId, token) 23 | if (result?.courseDetails) { 24 | dispatch(setEditCourse(true)) 25 | dispatch(setCourse(result?.courseDetails)) 26 | } 27 | setLoading(false) 28 | })() 29 | // eslint-disable-next-line react-hooks/exhaustive-deps 30 | }, []) 31 | 32 | if (loading) { 33 | return ( 34 |
35 |
36 |
37 | ) 38 | } 39 | 40 | return ( 41 |
42 |

43 | Edit Course 44 |

45 |
46 | {course ? ( 47 | 48 | ) : ( 49 |

50 | Course not found 51 |

52 | )} 53 |
54 |
55 | ) 56 | } -------------------------------------------------------------------------------- /src/components/core/Dashboard/EnrolledCourses.jsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react" 2 | import ProgressBar from "@ramonak/react-progress-bar" 3 | import { BiDotsVerticalRounded } from "react-icons/bi" 4 | import { useSelector } from "react-redux" 5 | import { useNavigate } from "react-router-dom" 6 | 7 | import { getUserEnrolledCourses } from "../../../services/operations/profileAPI" 8 | 9 | export default function EnrolledCourses() { 10 | const { token } = useSelector((state) => state.auth) 11 | const navigate = useNavigate() 12 | 13 | const [enrolledCourses, setEnrolledCourses] = useState(null) 14 | const getEnrolledCourses = async () => { 15 | try { 16 | const res = await getUserEnrolledCourses(token); 17 | 18 | setEnrolledCourses(res); 19 | } catch (error) { 20 | console.log("Could not fetch enrolled courses.") 21 | } 22 | }; 23 | useEffect(() => { 24 | getEnrolledCourses(); 25 | }, []) 26 | 27 | return ( 28 | <> 29 |
Enrolled Courses
30 | {!enrolledCourses ? ( 31 |
32 |
33 |
34 | ) : !enrolledCourses.length ? ( 35 |

36 | You have not enrolled in any course yet. 37 | {/* TODO: Modify this Empty State */} 38 |

39 | ) : ( 40 |
41 | {/* Headings */} 42 |
43 |

Course Name

44 |

Duration

45 |

Progress

46 |
47 | {/* Course Names */} 48 | {enrolledCourses.map((course, i, arr) => ( 49 |
55 |
{ 58 | navigate( 59 | `/view-course/${course?._id}/section/${course.courseContent?.[0]?._id}/sub-section/${course.courseContent?.[0]?.subSection?.[0]?._id}` 60 | ) 61 | }} 62 | > 63 | course_img 68 |
69 |

{course.courseName}

70 |

71 | {course.courseDescription.length > 50 72 | ? `${course.courseDescription.slice(0, 50)}...` 73 | : course.courseDescription} 74 |

75 |
76 |
77 |
{course?.totalDuration}
78 |
79 |

Progress: {course.progressPercentage || 0}%

80 | 85 |
86 |
87 | ))} 88 |
89 | )} 90 | 91 | ) 92 | } -------------------------------------------------------------------------------- /src/components/core/Dashboard/InstructorDashboard/InstructorChart.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react" 2 | import { Chart, registerables } from "chart.js" 3 | import { Pie } from "react-chartjs-2" 4 | 5 | Chart.register(...registerables) 6 | 7 | export default function InstructorChart({ courses }) { 8 | // State to keep track of the currently selected chart 9 | const [currChart, setCurrChart] = useState("students") 10 | 11 | // Function to generate random colors for the chart 12 | const generateRandomColors = (numColors) => { 13 | const colors = [] 14 | for (let i = 0; i < numColors; i++) { 15 | const color = `rgb(${Math.floor(Math.random() * 256)}, ${Math.floor( 16 | Math.random() * 256 17 | )}, ${Math.floor(Math.random() * 256)})` 18 | colors.push(color) 19 | } 20 | return colors 21 | } 22 | 23 | // Data for the chart displaying student information 24 | const chartDataStudents = { 25 | labels: courses.map((course) => course.courseName), 26 | datasets: [ 27 | { 28 | data: courses.map((course) => course.totalStudentsEnrolled), 29 | backgroundColor: generateRandomColors(courses.length), 30 | }, 31 | ], 32 | } 33 | 34 | // Data for the chart displaying income information 35 | const chartIncomeData = { 36 | labels: courses.map((course) => course.courseName), 37 | datasets: [ 38 | { 39 | data: courses.map((course) => course.totalAmountGenerated), 40 | backgroundColor: generateRandomColors(courses.length), 41 | }, 42 | ], 43 | } 44 | 45 | // Options for the chart 46 | const options = { 47 | maintainAspectRatio: false, 48 | } 49 | 50 | return ( 51 |
52 |

Visualize

53 |
54 | {/* Button to switch to the "students" chart */} 55 | 65 | {/* Button to switch to the "income" chart */} 66 | 76 |
77 |
78 | {/* Render the Pie chart based on the selected chart */} 79 | 83 |
84 |
85 | ) 86 | } -------------------------------------------------------------------------------- /src/components/core/Dashboard/MyCourses.jsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react" 2 | import { VscAdd } from "react-icons/vsc" 3 | import { useSelector } from "react-redux" 4 | import { useNavigate } from "react-router-dom" 5 | 6 | import { fetchInstructorCourses } from "../../../services/operations/courseDetailsAPI" 7 | import IconBtn from "../../common/IconBtn" 8 | import CoursesTable from "./InstructorCourses/CoursesTable" 9 | 10 | export default function MyCourses() { 11 | const { token } = useSelector((state) => state.auth) 12 | const navigate = useNavigate() 13 | const [courses, setCourses] = useState([]) 14 | 15 | useEffect(() => { 16 | const fetchCourses = async () => { 17 | const result = await fetchInstructorCourses(token) 18 | if (result) { 19 | setCourses(result) 20 | } 21 | } 22 | fetchCourses() 23 | // eslint-disable-next-line react-hooks/exhaustive-deps 24 | }, []) 25 | 26 | return ( 27 |
28 |
29 |

My Courses

30 | navigate("/dashboard/add-course")} 33 | > 34 | 35 | 36 |
37 | {courses && } 38 |
39 | ) 40 | } -------------------------------------------------------------------------------- /src/components/core/Dashboard/Settings/ChangeProfilePicture.jsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef, useState } from "react" 2 | import { FiUpload } from "react-icons/fi" 3 | import { useDispatch, useSelector } from "react-redux" 4 | 5 | import { updateDisplayPicture } from "../../../../services/operations/SettingsAPI" 6 | import IconBtn from "../../../common/IconBtn" 7 | 8 | export default function ChangeProfilePicture() { 9 | const { token } = useSelector((state) => state.auth) 10 | const { user } = useSelector((state) => state.profile) 11 | const dispatch = useDispatch() 12 | 13 | const [loading, setLoading] = useState(false) 14 | const [imageFile, setImageFile] = useState(null) 15 | const [previewSource, setPreviewSource] = useState(null) 16 | 17 | const fileInputRef = useRef(null) 18 | 19 | const handleClick = () => { 20 | fileInputRef.current.click() 21 | } 22 | 23 | const handleFileChange = (e) => { 24 | const file = e.target.files[0] 25 | // console.log(file) 26 | if (file) { 27 | setImageFile(file) 28 | previewFile(file) 29 | } 30 | } 31 | 32 | const previewFile = (file) => { 33 | const reader = new FileReader() 34 | reader.readAsDataURL(file) 35 | reader.onloadend = () => { 36 | setPreviewSource(reader.result) 37 | } 38 | } 39 | 40 | const handleFileUpload = () => { 41 | try { 42 | console.log("uploading...") 43 | setLoading(true) 44 | const formData = new FormData() 45 | formData.append("displayPicture", imageFile) 46 | // console.log("formdata", formData) 47 | dispatch(updateDisplayPicture(token, formData)).then(() => { 48 | setLoading(false) 49 | }) 50 | } catch (error) { 51 | console.log("ERROR MESSAGE - ", error.message) 52 | } 53 | } 54 | 55 | useEffect(() => { 56 | if (imageFile) { 57 | previewFile(imageFile) 58 | } 59 | }, [imageFile]) 60 | return ( 61 | <> 62 |
63 |
64 | {`profile-${user?.firstName}`} 69 |
70 |

Change Profile Picture

71 |
72 | 79 | 86 | 90 | {!loading && ( 91 | 92 | )} 93 | 94 |
95 |
96 |
97 |
98 | 99 | ) 100 | } -------------------------------------------------------------------------------- /src/components/core/Dashboard/Settings/DeleteAccount.jsx: -------------------------------------------------------------------------------- 1 | import { FiTrash2 } from "react-icons/fi" 2 | import { useDispatch, useSelector } from "react-redux" 3 | import { useNavigate } from "react-router-dom" 4 | 5 | import { deleteProfile } from "../../../../services/operations/SettingsAPI" 6 | 7 | export default function DeleteAccount() { 8 | const { token } = useSelector((state) => state.auth) 9 | const dispatch = useDispatch() 10 | const navigate = useNavigate() 11 | 12 | async function handleDeleteAccount() { 13 | try { 14 | dispatch(deleteProfile(token, navigate)) 15 | } catch (error) { 16 | console.log("ERROR MESSAGE - ", error.message) 17 | } 18 | } 19 | 20 | return ( 21 | <> 22 |
23 |
24 | 25 |
26 |
27 |

28 | Delete Account 29 |

30 |
31 |

Would you like to delete account?

32 |

33 | This account may contain Paid Courses. Deleting your account is 34 | permanent and will remove all the contain associated with it. 35 |

36 |
37 | 44 |
45 |
46 | 47 | ) 48 | } -------------------------------------------------------------------------------- /src/components/core/Dashboard/Settings/index.jsx: -------------------------------------------------------------------------------- 1 | import ChangeProfilePicture from "./ChangeProfilePicture" 2 | import DeleteAccount from "./DeleteAccount" 3 | import EditProfile from "./EditProfile" 4 | import UpdatePassword from "./UpdatePassword" 5 | 6 | export default function Settings() { 7 | return ( 8 | <> 9 |

10 | Edit Profile 11 |

12 | {/* Change Profile Picture */} 13 | 14 | {/* Profile */} 15 | 16 | {/* Password */} 17 | 18 | {/* Delete Account */} 19 | 20 | 21 | ) 22 | } -------------------------------------------------------------------------------- /src/components/core/Dashboard/Sidebar.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react" 2 | import { VscSignOut } from "react-icons/vsc" 3 | import { useDispatch, useSelector } from "react-redux" 4 | import { useNavigate } from "react-router-dom" 5 | 6 | import { sidebarLinks } from "../../../data/dashboard-links" 7 | import { logout } from "../../../services/operations/authAPI" 8 | import ConfirmationModal from "../../common/ConfirmationModal" 9 | import SidebarLink from "./SidebarLink" 10 | 11 | export default function Sidebar() { 12 | const { user, loading: profileLoading } = useSelector( 13 | (state) => state.profile 14 | ) 15 | const { loading: authLoading } = useSelector((state) => state.auth) 16 | const dispatch = useDispatch() 17 | const navigate = useNavigate() 18 | const [confirmationModal, setConfirmationModal] = useState(null) 19 | 20 | if (profileLoading || authLoading) { 21 | return ( 22 |
23 |
24 |
25 | ) 26 | } 27 | 28 | return ( 29 | <> 30 |
31 |
32 | {sidebarLinks.map((link) => { 33 | if (link.type && user?.accountType !== link.type) return null 34 | return ( 35 | 36 | ) 37 | })} 38 |
39 |
40 |
41 | 45 | 63 |
64 |
65 | {confirmationModal && } 66 | 67 | ) 68 | } 69 | -------------------------------------------------------------------------------- /src/components/core/Dashboard/SidebarLink.jsx: -------------------------------------------------------------------------------- 1 | import * as Icons from "react-icons/vsc" 2 | import { useDispatch } from "react-redux" 3 | import { NavLink, matchPath, useLocation } from "react-router-dom" 4 | 5 | import { resetCourseState } from "../../../slices/courseSlice" 6 | 7 | export default function SidebarLink({ link, iconName }) { 8 | const Icon = Icons[iconName] 9 | const location = useLocation() 10 | const dispatch = useDispatch() 11 | 12 | const matchRoute = (route) => { 13 | return matchPath({ path: route }, location.pathname) 14 | } 15 | 16 | return ( 17 | dispatch(resetCourseState())} 20 | className={`relative px-8 py-2 text-sm font-medium ${ 21 | matchRoute(link.path) 22 | ? "bg-yellow-800 text-yellow-50" 23 | : "bg-opacity-0 text-richblack-300" 24 | } transition-all duration-200`} 25 | > 26 | 31 |
32 | {/* Icon Goes Here */} 33 | 34 | {link.name} 35 |
36 |
37 | ) 38 | } -------------------------------------------------------------------------------- /src/components/core/HomePage/Button.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Link } from "react-router-dom"; 3 | 4 | const Button = ({ children, active, linkto }) => { 5 | return ( 6 | 7 |
12 | {children} 13 |
14 | 15 | ); 16 | }; 17 | 18 | export default Button; -------------------------------------------------------------------------------- /src/components/core/HomePage/CodeBlocks.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import CTAButton from "./Button"; 3 | import { TypeAnimation } from "react-type-animation"; 4 | import { FaArrowRight } from "react-icons/fa"; 5 | 6 | const CodeBlocks = ({ 7 | position, 8 | heading, 9 | subheading, 10 | ctabtn1, 11 | ctabtn2, 12 | codeblock, 13 | backgroundGradient, 14 | codeColor, 15 | }) => { 16 | return ( 17 |
18 | 19 | 20 | {/* Section 1 */} 21 |
22 | {heading} 23 | 24 | {/* Sub Heading */} 25 |
26 | {subheading} 27 |
28 | 29 | {/* Button Group */} 30 |
31 | 32 |
33 | {ctabtn1.btnText} 34 | 35 |
36 |
37 | 38 | {ctabtn2.btnText} 39 | 40 |
41 |
42 | 43 | {/* Section 2 */} 44 |
45 | {backgroundGradient} 46 | {/* Indexing */} 47 |
48 |

1

49 |

2

50 |

3

51 |

4

52 |

5

53 |

6

54 |

7

55 |

8

56 |

9

57 |

10

58 |

11

59 |
60 | 61 | {/* Codes */} 62 |
65 | 75 |
76 |
77 |
78 | ); 79 | }; 80 | 81 | export default CodeBlocks; -------------------------------------------------------------------------------- /src/components/core/HomePage/CourseCard.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | // Importing React Icons 4 | import { HiUsers } from "react-icons/hi"; 5 | import { ImTree } from "react-icons/im"; 6 | 7 | const CourseCard = ({cardData, currentCard, setCurrentCard}) => { 8 | return ( 9 |
setCurrentCard(cardData?.heading)} 16 | > 17 |
18 |
23 | {cardData?.heading} 24 |
25 | 26 |
33 | {cardData?.description} 34 |
35 |
36 | 37 |
44 | {/* Level */} 45 |
46 | 47 |

{cardData?.level}

48 |
49 | 50 | {/* Flow Chart */} 51 |
52 | 53 |

{cardData?.lessonNumber}Lesson

54 |
55 |
56 |
57 | ); 58 | }; 59 | 60 | export default CourseCard; -------------------------------------------------------------------------------- /src/components/core/HomePage/ExploreMore.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { HomePageExplore } from "../../../data/homepage-explore"; 3 | import CourseCard from "./CourseCard"; 4 | import HighlightText from "./HighlightText"; 5 | 6 | const tabsName = [ 7 | "Free", 8 | "New to coding", 9 | "Most popular", 10 | "Skills paths", 11 | "Career paths", 12 | ]; 13 | 14 | const ExploreMore = () => { 15 | const [currentTab, setCurrentTab] = useState(tabsName[0]); 16 | const [courses, setCourses] = useState(HomePageExplore[0].courses); 17 | const [currentCard, setCurrentCard] = useState( 18 | HomePageExplore[0].courses[0].heading 19 | ); 20 | 21 | const setMyCards = (value) => { 22 | setCurrentTab(value); 23 | const result = HomePageExplore.filter((course) => course.tag === value); 24 | setCourses(result[0].courses); 25 | setCurrentCard(result[0].courses[0].heading); 26 | }; 27 | 28 | return ( 29 |
30 | {/* Explore more section */} 31 |
32 |
33 |
34 | Unlock the 35 | 36 |
37 |

38 | Learn to Build Anything You Can Imagine 39 |

40 |
41 |
42 | 43 | {/* Tabs Section */} 44 |
45 | {tabsName.map((ele, index) => { 46 | return ( 47 |
setMyCards(ele)} 54 | > 55 | {ele} 56 |
57 | ); 58 | })} 59 |
60 | 61 |
62 | 63 | {/* Cards Group */} 64 |
65 | {courses.map((ele, index) => { 66 | return ( 67 | 73 | ); 74 | })} 75 |
76 |
77 | ); 78 | }; 79 | 80 | export default ExploreMore; -------------------------------------------------------------------------------- /src/components/core/HomePage/HighlightText.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const HighlightText = ({text}) => { 4 | return ( 5 | 6 | {" "} 7 | {text} 8 | 9 | ); 10 | }; 11 | 12 | export default HighlightText; -------------------------------------------------------------------------------- /src/components/core/HomePage/InstructorSection.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import CTAButton from "../../../components/core/HomePage/Button"; 3 | import { FaArrowRight } from "react-icons/fa"; 4 | import Instructor from "../../../assets/Images/Instructor.png"; 5 | import HighlightText from './HighlightText'; 6 | 7 | const InstructorSection = () => { 8 | return ( 9 |
10 |
11 |
12 | 17 |
18 |
19 |

20 | Become an 21 | 22 |

23 | 24 |

25 | Instructors from around the world teach millions of students on 26 | StudyNotion. We provide the tools and skills to teach what you 27 | love. 28 |

29 | 30 |
31 | 32 |
33 | Start Teaching Today 34 | 35 |
36 |
37 |
38 |
39 |
40 |
41 | ) 42 | } 43 | 44 | export default InstructorSection -------------------------------------------------------------------------------- /src/components/core/HomePage/LearningLanguageSection.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import HighlightText from './HighlightText' 3 | import CTAButton from "../../../components/core/HomePage/Button"; 4 | import Know_your_progress from "../../../assets/Images/Know_your_progress.png"; 5 | import Compare_with_others from "../../../assets/Images/Compare_with_others.svg"; 6 | import Plan_your_lessons from "../../../assets/Images/Plan_your_lessons.svg"; 7 | 8 | const LearningLanguageSection = () => { 9 | return ( 10 |
11 |
12 | Your swiss knife for 13 | 14 |
15 | Using spin making learning multiple languages easy. with 20+ 16 | languages realistic voice-over, progress tracking, custom schedule 17 | and more. 18 |
19 |
20 | 25 | 30 | 35 |
36 |
37 | 38 |
39 | 40 |
Learn More
41 |
42 |
43 |
44 | ) 45 | } 46 | 47 | export default LearningLanguageSection -------------------------------------------------------------------------------- /src/components/core/HomePage/TimelineSection.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import TimeLineImage from "../../../assets/Images/TimelineImage.png"; 3 | import Logo1 from "../../../assets/TimeLineLogo/Logo1.svg"; 4 | import Logo2 from "../../../assets/TimeLineLogo/Logo2.svg"; 5 | import Logo3 from "../../../assets/TimeLineLogo/Logo3.svg"; 6 | import Logo4 from "../../../assets/TimeLineLogo/Logo4.svg"; 7 | 8 | const TimeLine = [ 9 | { 10 | Logo: Logo1, 11 | Heading: "Leadership", 12 | Description: "Fully committed to the success company", 13 | }, 14 | { 15 | Logo: Logo2, 16 | Heading: "Responsibility", 17 | Description: "Students will always be our top priority", 18 | }, 19 | { 20 | Logo: Logo3, 21 | Heading: "Flexibility", 22 | Description: "The ability to switch is an important skills", 23 | }, 24 | { 25 | Logo: Logo4, 26 | Heading: "Solve the problem", 27 | Description: "Code your way to a solution", 28 | }, 29 | ]; 30 | 31 | 32 | const TimelineSection = () => { 33 | return ( 34 |
35 |
36 |
37 | {TimeLine.map((ele, i) => { 38 | return ( 39 |
40 |
41 |
42 | 43 |
44 |
45 |

{ele.Heading}

46 |

{ele.Description}

47 |
48 |
49 |
54 |
55 | ); 56 | })} 57 |
58 |
59 |
60 | {/* Section 1 */} 61 |
62 |

10

63 |

64 | Years experiences 65 |

66 |
67 | 68 | {/* Section 2 */} 69 |
70 |

250

71 |

72 | types of courses 73 |

74 |
75 |
76 |
77 | timelineImage 82 |
83 |
84 |
85 | ); 86 | }; 87 | 88 | export default TimelineSection; -------------------------------------------------------------------------------- /src/data/dashboard-links.js: -------------------------------------------------------------------------------- 1 | import { ACCOUNT_TYPE } from "../utils/constants"; 2 | export const sidebarLinks = [ 3 | { 4 | id: 1, 5 | name: "My Profile", 6 | path: "/dashboard/my-profile", 7 | icon: "VscAccount", 8 | }, 9 | { 10 | id: 2, 11 | name: "Dashboard", 12 | path: "/dashboard/instructor", 13 | type: ACCOUNT_TYPE.INSTRUCTOR, 14 | icon: "VscDashboard", 15 | }, 16 | { 17 | id: 3, 18 | name: "My Courses", 19 | path: "/dashboard/my-courses", 20 | type: ACCOUNT_TYPE.INSTRUCTOR, 21 | icon: "VscVm", 22 | }, 23 | { 24 | id: 4, 25 | name: "Add Course", 26 | path: "/dashboard/add-course", 27 | type: ACCOUNT_TYPE.INSTRUCTOR, 28 | icon: "VscAdd", 29 | }, 30 | { 31 | id: 5, 32 | name: "Enrolled Courses", 33 | path: "/dashboard/enrolled-courses", 34 | type: ACCOUNT_TYPE.STUDENT, 35 | icon: "VscMortarBoard", 36 | }, 37 | { 38 | id: 6, 39 | name: "Your Cart", 40 | path: "/dashboard/cart", 41 | type: ACCOUNT_TYPE.STUDENT, 42 | icon: "VscHistory", 43 | }, 44 | ]; 45 | -------------------------------------------------------------------------------- /src/data/footer-links.js: -------------------------------------------------------------------------------- 1 | export const FooterLink2 = [ 2 | { 3 | title: "Subjects", 4 | links: [ 5 | { title: "Al", link: "/al" }, 6 | { title: "Cloud Computing", link: "/cloud-computing" }, 7 | { title: "Code Foundations", link: "/code-foundations" }, 8 | { title: "Computer Science", link: "/computer-science" }, 9 | { title: "Cybersecurity", link: "/cybersecurity" }, 10 | { title: "Data Analytics", link: "/data-analytics" }, 11 | { title: "Data Science", link: "/data-science" }, 12 | { title: "Data Visualization", link: "/data-visualization" }, 13 | { title: "Developer Tools", link: "/developer-tools" }, 14 | { title: "DevOps", link: "/devops" }, 15 | { title: "Game Development", link: "/game-development" }, 16 | { title: "IT", link: "/it" }, 17 | { title: "Machine Learning", link: "/machine-learning" }, 18 | { title: "Math", link: "/math" }, 19 | { title: "Mobile Development", link: "/mobile-development" }, 20 | { title: "Web Design", link: "/web-design" }, 21 | { title: "Web Development", link: "/web-development" }, 22 | ], 23 | }, 24 | { 25 | title: "Languages", 26 | links: [ 27 | { title: "Bash", link: "/bash" }, 28 | { title: "C++", link: "/c++" }, 29 | { title: "C#", link: "/csharp" }, 30 | { title: "Go", link: "/go" }, 31 | { title: "HTML & CSS", link: "/html-css" }, 32 | { title: "Java", link: "/java" }, 33 | { title: "JavaScript", link: "/javascript" }, 34 | { title: "Kotlin", link: "/kotlin" }, 35 | { title: "PHP", link: "/php" }, 36 | { title: "Python", link: "/python" }, 37 | { title: "R", link: "/r" }, 38 | { title: "Ruby", link: "/ruby" }, 39 | { title: "SQL", link: "/sql" }, 40 | { title: "Swift", link: "/swift" }, 41 | ], 42 | }, 43 | { 44 | title: "Career building", 45 | links: [ 46 | {title: "Career paths", link: "/career-paths"}, 47 | {title: "Career services", link: "/career-services"}, 48 | {title: "Interview prep", link: "/interview-prep"}, 49 | {title: "Professional certification", link: "/professional-certification"}, 50 | {title: "Full Catalog", link: "/full-catalog"}, 51 | {title: "Beta Content", link: "/beta-content"} 52 | ] 53 | } 54 | ]; 55 | -------------------------------------------------------------------------------- /src/data/navbar-links.js: -------------------------------------------------------------------------------- 1 | export const NavbarLinks = [ 2 | { 3 | title: "Home", 4 | path: "/", 5 | }, 6 | { 7 | title: "Catalog", 8 | path: '/catalog', 9 | }, 10 | { 11 | title: "Project", 12 | path: "/project", 13 | }, 14 | { 15 | title: "About Us", 16 | path: "/about", 17 | }, 18 | { 19 | title: "Contact Us", 20 | path: "/contact", 21 | }, 22 | { 23 | title:"Rate Us", 24 | path:"/rateus", 25 | } 26 | ]; 27 | -------------------------------------------------------------------------------- /src/hooks/useOnClickOutside.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | 3 | // This hook detects clicks outside of the specified component and calls the provided handler function. 4 | export default function useOnClickOutside(ref, handler) { 5 | useEffect(() => { 6 | // Define the listener function to be called on click/touch events 7 | const listener = (event) => { 8 | // If the click/touch event originated inside the ref element, do nothing 9 | if (!ref.current || ref.current.contains(event.target)) { 10 | return; 11 | } 12 | // Otherwise, call the provided handler function 13 | handler(event); 14 | }; 15 | 16 | // Add event listeners for mousedown and touchstart events on the document 17 | document.addEventListener("mousedown", listener); 18 | document.addEventListener("touchstart", listener); 19 | 20 | // Cleanup function to remove the event listeners when the component unmounts or when the ref/handler dependencies change 21 | return () => { 22 | document.removeEventListener("mousedown", listener); 23 | document.removeEventListener("touchstart", listener); 24 | }; 25 | }, [ref, handler]); // Only run this effect when the ref or handler function changes 26 | } -------------------------------------------------------------------------------- /src/hooks/useRouteMatch.js: -------------------------------------------------------------------------------- 1 | import { useLocation, matchPath } from "react-router-dom"; 2 | 3 | export default function useRouteMatch(path) { 4 | const location = useLocation(); 5 | return matchPath(location.pathname, { path }); 6 | } -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import App from "./App"; 4 | import "./index.css"; 5 | import { BrowserRouter } from "react-router-dom"; 6 | import { Provider } from "react-redux"; 7 | import rootReducer from "./reducer"; 8 | import {configureStore} from "@reduxjs/toolkit" 9 | import { Toaster } from "react-hot-toast"; 10 | 11 | 12 | const store = configureStore({ 13 | reducer:rootReducer, 14 | }); 15 | 16 | const root = ReactDOM.createRoot(document.getElementById("root")); 17 | root.render( 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | ); 29 | -------------------------------------------------------------------------------- /src/pages/Chatbot.jsx: -------------------------------------------------------------------------------- 1 | // src/components/Chatbot.js 2 | import React, { useState, useEffect } from 'react'; 3 | import './Rating.css'; 4 | 5 | const Chatbot = () => { 6 | const [messages, setMessages] = useState([]); 7 | const [input, setInput] = useState(''); 8 | const [visible, setVisible] = useState(false); 9 | 10 | const faqData = { 11 | "What is your return policy?": "Our return policy is 30 days with a receipt.", 12 | "What are your hours of operation?": "We are open from 9am to 9pm, Monday to Saturday.", 13 | "Where can I find the courses?": "You can find them all on the landing page of the website.", 14 | "Where can I find projects to learn?": "You can find them all at the Projects section collectively.", 15 | "Will this be really beneficial for me?": "Yes, of course, if you are a true learner." 16 | // Add more FAQs here 17 | 18 | }; 19 | 20 | useEffect(() => { 21 | // Add initial message on component mount 22 | setMessages([{ text: "Hello, How can I help you?", sender: 'bot' }]); 23 | }, []); 24 | 25 | const handleSend = () => { 26 | if (input.trim()) { 27 | const userMessage = { text: input, sender: 'user' }; 28 | const botResponse = faqData[input.trim()] || "Sorry, I can able to answer for courses, projects & return policy."; 29 | 30 | setMessages([...messages, userMessage, { text: botResponse, sender: 'bot' }]); 31 | 32 | setInput(''); 33 | } 34 | }; 35 | 36 | const handleKeyPress = (e) => { 37 | if (e.key === 'Enter') { 38 | 39 | handleSend(); 40 | } 41 | }; 42 | 43 | return ( 44 |
45 |
setVisible(!visible)}> 46 | 💬 47 |
48 | {visible && ( 49 |
50 |
51 |

FAQ Chatbot

52 | 53 |
54 |
55 | {messages.map((msg, index) => ( 56 |
57 | {msg.text} 58 |
59 | ))} 60 |
61 |
62 | setInput(e.target.value)} 66 | onKeyPress={handleKeyPress} 67 | placeholder="Ask a question..." 68 | /> 69 | 70 |
71 |
72 | )} 73 |
74 | ); 75 | }; 76 | 77 | export default Chatbot; 78 | -------------------------------------------------------------------------------- /src/pages/Contact.jsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import Footer from "../components/common/Footer" 3 | import ContactDetails from "../components/ContactPage/ContactDetails" 4 | import ContactForm from "../components/ContactPage/ContactForm" 5 | import ReviewSlider from "../components/common/ReviewSlider" 6 | 7 | const Contact = () => { 8 | return ( 9 |
10 |
11 | {/* Contact Details */} 12 |
13 | 14 |
15 | 16 | {/* Contact Form */} 17 |
18 | 19 |
20 |
21 |
22 | {/* Reviws from Other Learner */} 23 |

24 | Happy & Satisfied Learners! 25 |

26 | 27 |
28 |
29 |
30 | ) 31 | } 32 | 33 | export default Contact -------------------------------------------------------------------------------- /src/pages/Dashboard.jsx: -------------------------------------------------------------------------------- 1 | import { useSelector } from "react-redux" 2 | import { Outlet } from "react-router-dom" 3 | 4 | import Sidebar from "../components/core/Dashboard/Sidebar" 5 | 6 | function Dashboard() { 7 | const { loading: profileLoading } = useSelector((state) => state.profile) 8 | const { loading: authLoading } = useSelector((state) => state.auth) 9 | 10 | if (profileLoading || authLoading) { 11 | return ( 12 |
13 |
14 |
15 | ) 16 | } 17 | 18 | return ( 19 |
20 | 21 |
22 |
23 | 24 |
25 |
26 |
27 | ) 28 | } 29 | 30 | export default Dashboard 31 | -------------------------------------------------------------------------------- /src/pages/Error.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import CTAButton from "../components/core/HomePage/Button" 3 | import backgroundImage from '../assets/Images/404.png'; 4 | 5 | const Error = () => { 6 | return ( 7 |
11 |
12 |

Oops! It looks like you're lost.

13 |
14 | 15 | Go to Homepage 16 | 17 |
18 |
19 |
20 | ) 21 | } 22 | 23 | export default Error 24 | -------------------------------------------------------------------------------- /src/pages/ForgotPassword.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react" 2 | import { BiArrowBack } from "react-icons/bi" 3 | import { useDispatch, useSelector } from "react-redux" 4 | import { Link } from "react-router-dom" 5 | 6 | import { getPasswordResetToken } from "../services/operations/authAPI" 7 | 8 | function ForgotPassword() { 9 | const [email, setEmail] = useState("") 10 | const [emailSent, setEmailSent] = useState(false) 11 | const dispatch = useDispatch() 12 | const { loading } = useSelector((state) => state.auth) 13 | 14 | const handleOnSubmit = (e) => { 15 | e.preventDefault() 16 | dispatch(getPasswordResetToken(email, setEmailSent)) 17 | } 18 | 19 | return ( 20 |
21 | {loading ? ( 22 |
23 | ) : ( 24 |
25 |

26 | {!emailSent ? "Reset your password" : "Check email"} 27 |

28 |

29 | {!emailSent 30 | ? "Have no fear. We'll email you instructions to reset your password. If you dont have access to your email we can try account recovery" 31 | : `We have sent the reset email to ${email}`} 32 |

33 |
34 | {!emailSent && ( 35 | 49 | )} 50 | 56 |
57 |
58 | 59 |

60 | Back To Login 61 |

62 | 63 |
64 |
65 | )} 66 |
67 | ) 68 | } 69 | 70 | export default ForgotPassword -------------------------------------------------------------------------------- /src/pages/Login.jsx: -------------------------------------------------------------------------------- 1 | import loginImg from "../assets/Images/login.webp" 2 | import Template from "../components/core/Auth/Template" 3 | 4 | function Login() { 5 | return ( 6 |