├── backend
├── .gitkeep
├── public
│ └── .gitkeep
├── vercel.json
├── src
│ ├── utils
│ │ ├── ApiResponse.js
│ │ ├── asyncHandler.js
│ │ ├── ApiError.js
│ │ ├── cloudinary.js
│ │ └── Nodemailer.js
│ ├── middlewares
│ │ ├── multer.middleware.js
│ │ ├── joiLogin.middleware.js
│ │ ├── adminAuth.middleware.js
│ │ ├── stdAuth.middleware.js
│ │ └── teacherAuth.middleware.js
│ ├── database
│ │ └── db.js
│ ├── index.js
│ ├── models
│ │ ├── contact.model.js
│ │ ├── payment.model.js
│ │ ├── course.model.js
│ │ ├── admin.model.js
│ │ ├── student.model.js
│ │ └── teacher.model.js
│ ├── routes
│ │ ├── payment.routes.js
│ │ ├── student.routes.js
│ │ ├── course.routes.js
│ │ ├── admin.routes.js
│ │ └── teacher.routes.js
│ ├── app.js
│ └── controllers
│ │ └── payment.controller.js
├── package.json
└── .gitignore
├── .vscode
└── extensions.json
├── frontend
├── postcss.config.js
├── src
│ ├── Pages
│ │ ├── Dashboard
│ │ │ ├── Images
│ │ │ │ ├── Clock.png
│ │ │ │ └── Camera.png
│ │ │ ├── StudentDashboard
│ │ │ │ ├── StudentLayout.jsx
│ │ │ │ ├── Popup.jsx
│ │ │ │ ├── StudentCourses.jsx
│ │ │ │ ├── StudentClasses.jsx
│ │ │ │ ├── StudentDashboard.jsx
│ │ │ │ └── SearchTeacher.jsx
│ │ │ └── TeacherDashboard
│ │ │ │ ├── TeacherLayout.jsx
│ │ │ │ ├── DateTime.jsx
│ │ │ │ ├── TeacherCourses.jsx
│ │ │ │ ├── Withdrawal.jsx
│ │ │ │ ├── TeacherDashboard.jsx
│ │ │ │ ├── TeacherClasses.jsx
│ │ │ │ ├── AddClass.jsx
│ │ │ │ └── DashboardTeacher.jsx
│ │ ├── ErrorPage
│ │ │ └── ErrorPage.jsx
│ │ ├── Components
│ │ │ ├── DocumentVerification
│ │ │ │ ├── InputComponent
│ │ │ │ │ └── Input.jsx
│ │ │ │ ├── Inputupload
│ │ │ │ │ └── InputUpload.jsx
│ │ │ │ └── StudentDocument.jsx
│ │ │ ├── Searchbtn
│ │ │ │ ├── Search.css
│ │ │ │ └── Success.jsx
│ │ │ ├── VarifyEmail
│ │ │ │ └── VarifyEmail.jsx
│ │ │ ├── RadioBtn
│ │ │ │ ├── Radiobtn.jsx
│ │ │ │ └── Radiobtn.css
│ │ │ └── Admin
│ │ │ │ ├── Course.jsx
│ │ │ │ ├── Admin.jsx
│ │ │ │ └── VarifyDoc.jsx
│ │ ├── Home
│ │ │ ├── Header
│ │ │ │ ├── Header.css
│ │ │ │ └── Header.jsx
│ │ │ ├── About
│ │ │ │ └── About.jsx
│ │ │ ├── Contact
│ │ │ │ └── Contact.jsx
│ │ │ ├── Courses
│ │ │ │ └── Courses.jsx
│ │ │ ├── Landing
│ │ │ │ └── Landing.css
│ │ │ └── Search
│ │ │ │ └── Search.jsx
│ │ ├── Response
│ │ │ ├── Pending.jsx
│ │ │ └── Rejected.jsx
│ │ ├── Images
│ │ │ ├── logo.svg
│ │ │ ├── Plant2.svg
│ │ │ └── Plant.svg
│ │ ├── Login
│ │ │ ├── Images
│ │ │ │ └── Apple.svg
│ │ │ ├── Login.css
│ │ │ ├── AdminLogin.jsx
│ │ │ └── Login.jsx
│ │ ├── Footer
│ │ │ └── Footer.jsx
│ │ ├── Signup
│ │ │ ├── Styles.css
│ │ │ └── Signup.jsx
│ │ └── ForgetPassword
│ │ │ ├── Forgetpassword.jsx
│ │ │ ├── ResetPassword.jsx
│ │ │ └── ResetTeacher.jsx
│ ├── Layout.jsx
│ ├── index.css
│ └── main.jsx
├── tailwind.config.js
├── vite.config.js
├── .gitignore
├── README.md
├── index.html
├── .eslintrc.cjs
└── package.json
├── package.json
├── .env.example
└── README.md
/backend/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/backend/public/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "postman.postman-for-vscode"
4 | ]
5 | }
--------------------------------------------------------------------------------
/frontend/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/frontend/src/Pages/Dashboard/Images/Clock.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Pika003/e-Learning-Platform/HEAD/frontend/src/Pages/Dashboard/Images/Clock.png
--------------------------------------------------------------------------------
/frontend/src/Pages/Dashboard/Images/Camera.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Pika003/e-Learning-Platform/HEAD/frontend/src/Pages/Dashboard/Images/Camera.png
--------------------------------------------------------------------------------
/frontend/src/Layout.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {Outlet} from 'react-router-dom'
3 |
4 | function Layout() {
5 | return (
6 | <>
7 |
8 | >
9 | )
10 | }
11 |
12 | export default Layout
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "@emotion/react": "^11.11.4",
4 | "@emotion/styled": "^11.11.5",
5 | "@mui/material": "^5.15.19",
6 | "axios": "^1.7.2",
7 | "react-hot-toast": "^2.4.1"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/backend/vercel.json:
--------------------------------------------------------------------------------
1 | {
2 | "version":2,
3 | "builds": [
4 | { "src": "*.js", "use": "@vercel/node" }
5 | ],
6 | "routes": [
7 | {
8 | "src": "/(.*)",
9 | "dest": "/"
10 | }
11 | ]
12 | }
--------------------------------------------------------------------------------
/frontend/tailwind.config.js:
--------------------------------------------------------------------------------
1 | const withMT = require("@material-tailwind/react/utils/withMT");
2 |
3 | module.exports = withMT({
4 | content: ["./index.html", "./src/**/*.{vue,js,ts,jsx,tsx}"],
5 | theme: {
6 | extend: {},
7 | },
8 | plugins: [],
9 | });
10 |
--------------------------------------------------------------------------------
/frontend/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | server:{
7 | proxy:{
8 | '/api': 'http://localhost:4400'
9 | }
10 | },
11 | plugins: [react()],
12 | })
13 |
--------------------------------------------------------------------------------
/backend/src/utils/ApiResponse.js:
--------------------------------------------------------------------------------
1 | class ApiResponse{
2 | constructor(statusCode, data, message = "Success"){
3 | this.statusCode = statusCode
4 | this.data = data
5 | this.message = message
6 | this.success = statusCode < 399
7 |
8 | }
9 | }
10 |
11 | export {ApiResponse}
--------------------------------------------------------------------------------
/frontend/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 |
6 |
7 |
8 | *{
9 | margin: 0;
10 | padding: 0;
11 | }
12 | body{
13 | background:linear-gradient(90deg, #053a4f 8.26%, rgba(20, 120, 118, 0.58) 99.96%);
14 | overflow: scroll;
15 | }
16 | body::-webkit-scrollbar{
17 | display: none;
18 | }
19 |
--------------------------------------------------------------------------------
/frontend/src/Pages/Dashboard/StudentDashboard/StudentLayout.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {Outlet} from 'react-router-dom'
3 | import StudentDashboard from './StudentDashboard'
4 |
5 | function StudentLayout() {
6 | return (
7 | <>
8 |
9 |
10 | >
11 | )
12 | }
13 |
14 | export default StudentLayout
--------------------------------------------------------------------------------
/frontend/src/Pages/Dashboard/TeacherDashboard/TeacherLayout.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {Outlet} from 'react-router-dom'
3 | import TeacherDashboard from './TeacherDashboard'
4 |
5 | function TeacherLayout() {
6 | return (
7 | <>
8 |
9 |
10 | >
11 | )
12 | }
13 |
14 | export default TeacherLayout
--------------------------------------------------------------------------------
/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
1 | TEST =
2 | DB_NAME =
3 | PORT =
4 | MONGODB_URL =
5 | ACCESS_TOKEN_SECRET =
6 | ACCESS_TOKEN_EXPIRY =
7 | REFRESH_TOKEN_SECRET =
8 | REFRESH_TOKEN_EXPIRY =
9 |
10 | CORS =*
11 | SMTP_EMAIL =
12 | SMTP_PASS =
13 |
14 | CLOUDINARY_NAME=
15 | CLOUDINARY_API_KEY=
16 | CLOUDINARY_SECRET_KEY=
17 |
18 | KEY_ID =
19 | KEY_SECRET =
20 |
21 | FRONTEND_URL=
22 |
--------------------------------------------------------------------------------
/backend/src/middlewares/multer.middleware.js:
--------------------------------------------------------------------------------
1 | import multer from "multer";
2 |
3 | const storage = multer.diskStorage({
4 | destination: function (req, file, cb) {
5 | cb(null, "./public")
6 | },
7 | filename: function (req, file, cb) {
8 |
9 | cb(null, file.originalname)
10 | }
11 | })
12 |
13 | export const upload = multer({
14 | storage,
15 | })
--------------------------------------------------------------------------------
/backend/src/database/db.js:
--------------------------------------------------------------------------------
1 | import mongoose from "mongoose";
2 |
3 | const db = async() => {
4 | try{
5 | const connectionInstance = await mongoose.connect(`${process.env.MONGODB_URL}/eLearning`)
6 | console.log(`\n MongoDB connected !! DB HOST :: ${connectionInstance.connection.host}`)
7 | } catch (error){
8 | console.log("Mongodb connection error", error);
9 | process.exit(1)
10 | }
11 | }
12 |
13 |
14 |
15 | export default db
--------------------------------------------------------------------------------
/frontend/README.md:
--------------------------------------------------------------------------------
1 | # React + Vite
2 |
3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
4 |
5 | Currently, two official plugins are available:
6 |
7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
9 |
--------------------------------------------------------------------------------
/backend/src/index.js:
--------------------------------------------------------------------------------
1 | import dotenv from "dotenv"
2 | import db from './database/db.js';
3 | import {app} from './app.js'
4 | dotenv.config({
5 | path: './.env'
6 | })
7 |
8 | console.log(`${process.env.DB_NAME}`);
9 |
10 |
11 | db()
12 | .then(() => {
13 | app.listen(process.env.PORT || 8000, () => {
14 | console.log(`⚙️ Server is running at port : ${process.env.PORT}`);
15 | })
16 | })
17 | .catch((err) => {
18 | console.log(" mongodb connection failed !!! ", err);
19 | })
--------------------------------------------------------------------------------
/backend/src/utils/asyncHandler.js:
--------------------------------------------------------------------------------
1 | const asyncHandler = (fn) => async (req, res, next) => {
2 | try {
3 | await fn(req, res, next);
4 | } catch (error) {
5 | if (error.isJoi === true) {
6 | error.statusCode = 422;
7 | }
8 | res.status(error.statusCode || 500).json({
9 | statusCode: error.statusCode || 500,
10 | message: error.message || 'Internal Server Error'
11 | });
12 | }
13 | };
14 |
15 | export { asyncHandler };
16 |
--------------------------------------------------------------------------------
/frontend/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Shiksharthee
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/frontend/src/Pages/ErrorPage/ErrorPage.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { NavLink } from "react-router-dom";
3 | import Error from './Images/error.svg'
4 |
5 | function ErrorPage() {
6 | return (
7 | <>
8 |
9 |
10 |
Back To Home
11 |
12 | >
13 | )
14 | }
15 |
16 | export default ErrorPage
--------------------------------------------------------------------------------
/backend/src/models/contact.model.js:
--------------------------------------------------------------------------------
1 | import mongoose, { Schema } from "mongoose";
2 |
3 | const contactSchema = new Schema({
4 | name:{
5 | type:String,
6 | required:true,
7 | },
8 |
9 | email:{
10 | type:String,
11 | required:true,
12 | },
13 | message:{
14 | type:String,
15 | required:true,
16 | },
17 | status: {
18 | type: Boolean,
19 | default: false
20 | }
21 | })
22 |
23 | const contact = mongoose.model("contact", contactSchema)
24 |
25 | export {contact}
26 |
--------------------------------------------------------------------------------
/backend/src/middlewares/joiLogin.middleware.js:
--------------------------------------------------------------------------------
1 | import joi from "@hapi/joi"
2 | import { asyncHandler } from "../utils/asyncHandler.js"
3 |
4 |
5 |
6 | const authSchema = asyncHandler(async(req,_, next) =>{
7 |
8 | const schema = joi.object({
9 | Email: joi.string().email().lowercase().required(),
10 | Password: joi.string().min(6).max(16).required()
11 | })
12 |
13 | const result = await schema.validateAsync(req.body)
14 |
15 |
16 | req.user = result
17 | next()
18 | })
19 |
20 |
21 | export {authSchema}
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/backend/src/utils/ApiError.js:
--------------------------------------------------------------------------------
1 | class ApiError extends Error{
2 | constructor(
3 | statusCode,
4 | message = "Something went wrong",
5 | errors = [],
6 | stack = ""
7 | ){
8 | super(message)
9 | this.statusCode = statusCode
10 | this.data = null,
11 | this.message = message
12 | this.success = false
13 | this.errors = errors
14 |
15 | if(stack){
16 | this.stack = stack
17 | }
18 | else{
19 | Error.captureStackTrace(this,this.constructor)
20 | }
21 |
22 | }
23 | }
24 |
25 | export {ApiError}
--------------------------------------------------------------------------------
/frontend/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: { browser: true, es2020: true },
4 | extends: [
5 | 'eslint:recommended',
6 | 'plugin:react/recommended',
7 | 'plugin:react/jsx-runtime',
8 | 'plugin:react-hooks/recommended',
9 | ],
10 | ignorePatterns: ['dist', '.eslintrc.cjs'],
11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
12 | settings: { react: { version: '18.2' } },
13 | plugins: ['react-refresh'],
14 | rules: {
15 | 'react-refresh/only-export-components': [
16 | 'warn',
17 | { allowConstantExport: true },
18 | ],
19 | },
20 | }
21 |
--------------------------------------------------------------------------------
/frontend/src/Pages/Components/DocumentVerification/InputComponent/Input.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Input = ({label ,placeholder,value,onChange}) => {
4 | return (
5 |
6 | {label}
7 |
9 |
10 |
11 | )
12 | }
13 |
14 | export default Input
15 |
--------------------------------------------------------------------------------
/frontend/src/Pages/Components/Searchbtn/Search.css:
--------------------------------------------------------------------------------
1 | /* === search btn === */
2 | .search{
3 | display: flex;
4 | padding-top: 4rem;
5 | width: 450px;
6 | position: relative;
7 | }
8 | .search img{
9 | position: absolute;
10 | margin: 5px;
11 | }
12 | .search input{
13 | width: 450px;
14 | height: 3rem;
15 | border: none;
16 | border-radius: 3px;
17 | padding-left: 50px;
18 | background-color: #fff;
19 | color:#042439;
20 | font-weight: 600;
21 | }
22 |
23 | .search button{
24 | position: absolute;
25 | right: 0;
26 | border-radius: 2px;
27 | margin: 5.5px 5.5px;
28 | height: 35px;
29 | }
30 |
--------------------------------------------------------------------------------
/backend/src/models/payment.model.js:
--------------------------------------------------------------------------------
1 | import mongoose from "mongoose";
2 |
3 | const paymentSchema = new mongoose.Schema({
4 | razorpay_order_id: {
5 | type: String,
6 | required: true,
7 | },
8 | razorpay_payment_id: {
9 | type: String,
10 | required: true,
11 | },
12 | razorpay_signature: {
13 | type: String,
14 | required: true,
15 | },
16 | courseID: {
17 | type:mongoose.Schema.Types.ObjectId,
18 | ref: "course",
19 | require:true,
20 | },
21 | studentID: {
22 | type: mongoose.Schema.Types.ObjectId,
23 | ref: 'student',
24 | require:true,
25 | }
26 | });
27 |
28 | export const payment = mongoose.model("payment", paymentSchema);
--------------------------------------------------------------------------------
/frontend/src/Pages/Home/Header/Header.css:
--------------------------------------------------------------------------------
1 | .gapError{
2 | height: 10vh;
3 | }
4 | .logo{
5 | display: flex;
6 | align-items: center;
7 | gap: 1rem;
8 | }
9 | .logo img{
10 | height: 50px;
11 | }
12 | .logo h3{
13 | color: #fff;
14 | font-family: 'Gill Sans MT', sans-serif;
15 | text-decoration: none;
16 | }
17 | .link-nav ul{
18 | list-style: none;
19 | color: #fff;
20 | display: flex;
21 | gap: 2rem;
22 | }
23 |
24 | button{
25 | background-color: #4979D1;
26 | border: none;
27 | height: 40px;
28 | width: 90px;
29 | border-radius: 5px;
30 | color: #fff;
31 | }
32 |
33 | .deactive{
34 | color: #fff;
35 | text-decoration: none;
36 | }
37 | .active{
38 | color:#4979D1;
39 | text-decoration: none;
40 | }
--------------------------------------------------------------------------------
/backend/src/routes/payment.routes.js:
--------------------------------------------------------------------------------
1 | import { Router } from "express";
2 | import { authSTD } from "../middlewares/stdAuth.middleware.js";
3 | import { coursePayment, coursePaymentConfirmation, getkey, teacherAmount, withdrawAmount } from "../controllers/payment.controller.js";
4 | import { authTeacher } from "../middlewares/teacherAuth.middleware.js";
5 |
6 |
7 | const router = Router()
8 |
9 | router.route("/course/:courseID/:coursename").post(authSTD, coursePayment)
10 |
11 | router.route("/razorkey").get(authSTD, getkey)
12 |
13 | router.route("/confirmation/course/:courseID").post(authSTD, coursePaymentConfirmation)
14 |
15 | router.route("/teacher/:teacherID/balance").post(authTeacher, teacherAmount)
16 |
17 | router.route("/teacher/:teacherID/withdraw").post(authTeacher, withdrawAmount)
18 |
19 |
20 |
21 |
22 | export default router;
--------------------------------------------------------------------------------
/backend/src/utils/cloudinary.js:
--------------------------------------------------------------------------------
1 | import {v2 as cloudinary} from 'cloudinary';
2 | import fs from "fs"
3 |
4 |
5 |
6 | cloudinary.config({
7 | cloud_name: process.env.CLOUDINARY_NAME,
8 | api_key: process.env.CLOUDINARY_API_KEY,
9 | api_secret: process.env.CLOUDINARY_SECRET_KEY
10 | });
11 |
12 |
13 | const uploadOnCloudinary = async (localFilePath) => {
14 |
15 | try{
16 | if(!localFilePath) return null;
17 | const response = await cloudinary.uploader.upload(localFilePath, {
18 | resource_type: "auto"
19 | })
20 | fs.unlinkSync(localFilePath)
21 | return response;
22 | } catch(err){
23 | fs.unlinkSync(localFilePath)
24 | console.log("cloudinary upload error ", err)
25 | return null;
26 | }
27 | }
28 |
29 |
30 | export {uploadOnCloudinary}
31 |
--------------------------------------------------------------------------------
/backend/src/middlewares/adminAuth.middleware.js:
--------------------------------------------------------------------------------
1 | import {asyncHandler} from "../utils/asyncHandler.js";
2 | import {ApiError} from "../utils/ApiError.js";
3 | import { admin } from "../models/admin.model.js";
4 | import jwt from "jsonwebtoken";
5 |
6 | const authAdmin = asyncHandler(async(req,_,next) =>{
7 |
8 | const accToken = req.cookies?.Accesstoken
9 |
10 | if(!accToken) {
11 | throw new ApiError(401, "unauthorized req")
12 | }
13 |
14 |
15 | const decodedAccToken = jwt.verify(accToken,
16 | process.env.ACCESS_TOKEN_SECRET)
17 |
18 | const Admin = await admin.findById(decodedAccToken?._id).select("-password -Refreshtoken")
19 |
20 | if(!Admin){
21 | throw new ApiError(401, "invalid access token")
22 | }
23 |
24 | req.Admin = Admin
25 | next()
26 |
27 |
28 | })
29 |
30 | export { authAdmin }
--------------------------------------------------------------------------------
/backend/src/middlewares/stdAuth.middleware.js:
--------------------------------------------------------------------------------
1 | import {asyncHandler} from "../utils/asyncHandler.js";
2 | import {ApiError} from "../utils/ApiError.js";
3 | import {student} from "../models/student.model.js";
4 | import jwt from "jsonwebtoken";
5 |
6 | const authSTD = asyncHandler(async(req,_,next) =>{
7 |
8 | const accToken = req.cookies?.Accesstoken
9 |
10 | if(!accToken) {
11 | throw new ApiError(401, "unauthorized req")
12 | }
13 |
14 | const decodedAccToken = jwt.verify(accToken,
15 | process.env.ACCESS_TOKEN_SECRET)
16 |
17 | const Student = await student.findById(decodedAccToken?._id).select("-Password -Refreshtoken")
18 |
19 | if(!Student){
20 | throw new ApiError(401, "invalid access token")
21 | }
22 |
23 | req.Student = Student
24 | next()
25 |
26 |
27 | })
28 |
29 | export { authSTD }
--------------------------------------------------------------------------------
/backend/src/middlewares/teacherAuth.middleware.js:
--------------------------------------------------------------------------------
1 | import { ApiError } from "../utils/ApiError.js";
2 | import {Teacher} from "../models/teacher.model.js";
3 | import jwt from "jsonwebtoken";
4 | import { asyncHandler } from "../utils/asyncHandler.js";
5 |
6 | const authTeacher = asyncHandler(async(req,_,next)=>{
7 | const accToken = req.cookies?.Accesstoken
8 |
9 | if(!accToken){
10 | throw new ApiError(401, "unauthorized req")
11 | }
12 |
13 | const decodedAccToken = jwt.verify(accToken,
14 | process.env.ACCESS_TOKEN_SECRET)
15 |
16 | const teacher = await Teacher.findById(decodedAccToken?._id).select("-Password -Refreshtoken")
17 |
18 | if(!teacher){
19 | throw new ApiError(401, "invalid access token")
20 | }
21 |
22 |
23 | req.teacher = teacher
24 | next()
25 | })
26 |
27 | export {authTeacher}
--------------------------------------------------------------------------------
/frontend/src/Pages/Response/Pending.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import pending from "../Images/pending.svg";
3 | import { NavLink } from "react-router-dom";
4 |
5 | function Pending() {
6 | return (
7 | <>
8 |
9 |
10 |
Response Pending
11 |
12 | We take your response, now wait a little bit. when your Admin check your response and approve it or reject any reason we will notify you by your email it.
13 |
14 |
15 | ◀ go to home
16 |
17 |
18 | >
19 | );
20 | }
21 |
22 | export default Pending;
23 |
--------------------------------------------------------------------------------
/backend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "elearning",
3 | "version": "1.0.0",
4 | "description": "",
5 | "type": "module",
6 | "main": "index.js",
7 | "scripts": {
8 | "dev": "nodemon -r dotenv/config --experimental-json-modules src/index.js"
9 | },
10 | "github": "https://github.com/Pika003/e-Learning-Platform.git",
11 | "author": "Parag, Satyajit, Gopi, Aditya",
12 | "license": "ISC",
13 | "dependencies": {
14 | "@hapi/joi": "^17.1.1",
15 | "bcrypt": "^5.1.1",
16 | "cloudinary": "^2.0.3",
17 | "cookie-parser": "^1.4.6",
18 | "cors": "^2.8.5",
19 | "crypto": "^1.0.1",
20 | "dotenv": "^16.4.4",
21 | "express": "^4.18.2",
22 | "jsonwebtoken": "^9.0.2",
23 | "mongoose": "^8.1.2",
24 | "mongoose-aggregate-paginate-v2": "^1.0.7",
25 | "multer": "^1.4.5-lts.1",
26 | "nodemailer": "^6.9.9",
27 | "nodemon": "^3.1.0",
28 | "razorpay": "^2.9.3"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/frontend/src/Pages/Components/Searchbtn/Success.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | function Success({onClose}) {
4 | return (
5 |
6 |
7 |
8 |
Payment Successful
9 |
Payment successfully completed! Now you can join the classes. Wishing a happy journey with Shiksharthee
10 |
11 |
Back to Dashboard
12 |
13 |
14 | )
15 | }
16 |
17 | export default Success
--------------------------------------------------------------------------------
/frontend/src/Pages/Components/VarifyEmail/VarifyEmail.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Email from '../../Images/email.svg'
3 | import { NavLink } from "react-router-dom"
4 | import Header from '../../Home/Header/Header'
5 |
6 | function VarifyEmail() {
7 | return (
8 | <>
9 |
10 |
11 |
12 |
13 |
Send Email
14 |
We have sent a verification link to your Email. Click on the link to complete the verification process. You might need to check your spam folder.
15 |
16 | ◀ Back to Login
17 |
18 |
19 |
20 | >
21 | )
22 | }
23 |
24 | export default VarifyEmail
--------------------------------------------------------------------------------
/backend/src/utils/Nodemailer.js:
--------------------------------------------------------------------------------
1 |
2 | import nodemailer from "nodemailer"
3 |
4 |
5 | export const Sendmail =async function(email,subject,message){
6 |
7 |
8 | const transporter = nodemailer.createTransport({
9 | host:'smtp.gmail.com',
10 | secure: true, // Use `true` for port 465, `false` for all other ports
11 | port:465,
12 | auth: {
13 | user: process.env.SMTP_EMAIL,
14 | pass: process.env.SMTP_PASS,
15 | },
16 | });
17 |
18 | const receiver={
19 | from: process.env.SMTP_EMAIL, // sender address
20 | to: email, // list of receivers
21 | subject: subject, // Subject line
22 | html: message // html body
23 | }
24 |
25 | console.log(email,subject,message);
26 | try {
27 | const info = await transporter.sendMail(receiver);
28 | console.log('email sent:', info.response);
29 | return { success: true, message: 'Forget password email sent successfully' };
30 | } catch (error) {
31 | console.error('Error sending email:', error);
32 | return { success: false, error: 'Error sending email' };
33 | }
34 |
35 | }
--------------------------------------------------------------------------------
/frontend/src/Pages/Response/Rejected.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import rejected from "../Images/rejected.svg";
3 | import { NavLink, useParams } from "react-router-dom";
4 |
5 | function Rejected() {
6 | const { ID, user } = useParams();
7 | let type = '';
8 | if(user === 'student'){
9 | type = 'StudentDocument';
10 | }else{
11 | type = 'TeacherDocument';
12 | }
13 |
14 | return (
15 | <>
16 |
17 |
18 |
Response Rejected
19 |
20 | We take your response, but the image of your Aadhaar card is little
21 | unclear. Please submit one more time.
22 |
23 |
24 | ◀ go to document verification page
25 |
26 |
27 | >
28 | );
29 | }
30 |
31 | export default Rejected;
32 |
--------------------------------------------------------------------------------
/frontend/src/Pages/Components/DocumentVerification/Inputupload/InputUpload.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 |
3 | const InputUpload = ({ label, placeholder,value,onChange}) => {
4 |
5 |
6 |
7 | return (
8 |
9 |
{label}
10 |
11 |
12 |
18 |
19 |
20 |
21 | {value ? value.name : placeholder}
22 |
23 | Choose File
24 |
25 |
26 |
27 | );
28 |
29 | }
30 |
31 | export default InputUpload;
32 |
33 |
34 |
--------------------------------------------------------------------------------
/backend/src/app.js:
--------------------------------------------------------------------------------
1 | import express from "express";
2 | import cors from "cors";
3 | import cookieParser from "cookie-parser";
4 | import Razorpay from "razorpay"
5 |
6 | const app = express();
7 |
8 | app.use(cors())
9 |
10 | app.use(express.json({limit: "16kb"}))
11 | app.use(express.urlencoded({extended: true, limit: "16kb"}))
12 | app.use(express.static("public"))
13 | app.use(cookieParser())
14 |
15 |
16 | export const instance = new Razorpay({
17 | key_id: process.env.KEY_ID,
18 | key_secret: process.env.KEY_SECRET
19 | })
20 |
21 | //student routes
22 | import studentRouter from "./routes/student.routes.js";
23 | app.use("/api/student", studentRouter)
24 |
25 |
26 | //teacher routes
27 | import teacherRouter from "./routes/teacher.routes.js"
28 | app.use("/api/teacher", teacherRouter)
29 |
30 | //course routes
31 | import courseRouter from "./routes/course.routes.js"
32 | app.use("/api/course", courseRouter)
33 |
34 | import adminRouter from "./routes/admin.routes.js"
35 | app.use("/api/admin", adminRouter)
36 |
37 | import paymentRouter from "./routes/payment.routes.js"
38 | app.use("/api/payment", paymentRouter)
39 |
40 |
41 | export {app}
--------------------------------------------------------------------------------
/frontend/src/Pages/Images/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "fontent",
3 | "private": true,
4 | "proxy": "https://localhost:4400",
5 | "version": "0.0.0",
6 | "type": "module",
7 | "scripts": {
8 | "dev": "vite",
9 | "build": "vite build",
10 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
11 | "preview": "vite preview"
12 | },
13 | "dependencies": {
14 | "@material-tailwind/react": "^2.1.9",
15 | "axios": "^1.6.8",
16 | "date-fns": "^3.6.0",
17 | "razorpay": "^2.9.3",
18 | "react": "^18.2.0",
19 | "react-datepicker": "^7.1.0",
20 | "react-dom": "^18.2.0",
21 | "react-hot-toast": "^2.4.1",
22 | "react-icons": "^5.0.1",
23 | "react-loader-spinner": "^6.1.6",
24 | "react-router-dom": "^6.22.0",
25 | "yup": "^1.4.0"
26 | },
27 | "devDependencies": {
28 | "@types/react": "^18.2.43",
29 | "@types/react-dom": "^18.2.17",
30 | "@vitejs/plugin-react": "^4.2.1",
31 | "autoprefixer": "^10.4.18",
32 | "eslint": "^8.55.0",
33 | "eslint-plugin-react": "^7.33.2",
34 | "eslint-plugin-react-hooks": "^4.6.0",
35 | "eslint-plugin-react-refresh": "^0.4.5",
36 | "postcss": "^8.4.35",
37 | "tailwindcss": "^3.4.1",
38 | "vite": "^5.0.8"
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/backend/src/models/course.model.js:
--------------------------------------------------------------------------------
1 | import mongoose from "mongoose"
2 |
3 | const courseSchema= new mongoose.Schema({
4 |
5 | coursename:{
6 | type:String,
7 | require:true
8 | },
9 |
10 | description: {
11 | type: String,
12 | required: true
13 | },
14 |
15 | isapproved: {
16 | type: Boolean,
17 | default: false
18 | },
19 |
20 | liveClasses: [{
21 | title: String,
22 | timing: Number,
23 | date:Date,
24 | link: String,
25 | status: {
26 | type: String,
27 | enum: ['upcoming', 'in-progress', 'completed'],
28 | default: 'upcoming'
29 | }
30 | }],
31 |
32 | enrolledteacher:{
33 | type:mongoose.Schema.Types.ObjectId,
34 | ref: "teacher",
35 | require:true
36 | },
37 |
38 | enrolledStudent: [{
39 | type: mongoose.Schema.Types.ObjectId,
40 | ref: 'student'
41 | }],
42 |
43 | schedule: [{
44 | day: {
45 | type: Number,
46 | enum: [0, 1, 2, 3, 4, 5, 6]
47 | },
48 | starttime: {
49 | type: Number,
50 | min: 0,
51 | max: 24 * 60
52 | },
53 | endtime: {
54 | type: Number,
55 | min: 0,
56 | max: 24 * 60
57 | }
58 | }],
59 |
60 |
61 |
62 | },{timestamps:true})
63 |
64 | const course= mongoose.model('course',courseSchema)
65 |
66 | export {course}
--------------------------------------------------------------------------------
/backend/src/routes/student.routes.js:
--------------------------------------------------------------------------------
1 | import {Router} from "express";
2 | import {signup, mailVerified, login,logout, addStudentDetails, getStudent, forgetPassword, resetPassword } from "../controllers/student.controller.js";
3 | import {upload} from "../middlewares/multer.middleware.js"
4 | import {authSTD} from "../middlewares/stdAuth.middleware.js"
5 | import { authSchema } from "../middlewares/joiLogin.middleware.js";
6 |
7 | const router = Router()
8 |
9 | router.route("/signup").post(
10 | signup
11 | )
12 |
13 | router.route("/verify").get(
14 | mailVerified
15 | )
16 |
17 | router.route("/login").post(
18 | authSchema, login
19 | )
20 |
21 | router.route("/logout").post(authSTD, logout)
22 |
23 | router.route("/Verification/:id").post(authSTD,
24 | upload.fields([
25 | {
26 | name:"Aadhaar",
27 | maxCount:1,
28 | },
29 | {
30 | name:"Secondary",
31 | maxCount:1,
32 | },
33 | {
34 | name:"Higher",
35 | maxCount:1
36 | }
37 | ]) ,
38 | addStudentDetails)
39 |
40 | router.route("/StudentDocument/:id").get(authSTD, getStudent)
41 |
42 | router.route('/forgetpassword').post(forgetPassword)
43 |
44 | router.route('/forgetpassword/:token').post(resetPassword)
45 |
46 |
47 |
48 | export default router;
--------------------------------------------------------------------------------
/frontend/src/Pages/Components/RadioBtn/Radiobtn.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import './Radiobtn.css'
3 |
4 | function Radiobtn({userType,setUserType}) {
5 | return (
6 | <>
7 |
37 | >
38 | );
39 | }
40 |
41 | export default Radiobtn;
42 |
--------------------------------------------------------------------------------
/backend/src/routes/course.routes.js:
--------------------------------------------------------------------------------
1 | import { Router } from "express";
2 | import { addClass, addCourseStudent, addCourseTeacher, canStudentEnroll, enrolledcourseSTD, enrolledcourseTeacher, getCourse, getcourseTeacher, stdEnrolledCoursesClasses, teacherEnrolledCoursesClasses } from "../controllers/course.controller.js";
3 | import { authSTD } from "../middlewares/stdAuth.middleware.js";
4 | import { authTeacher } from "../middlewares/teacherAuth.middleware.js";
5 |
6 |
7 | const router = Router()
8 |
9 |
10 | router.route("/all").get(getCourse)
11 |
12 | router.route("/:coursename").get(getcourseTeacher)
13 |
14 | router.route("/:coursename/create/:id").post(authTeacher, addCourseTeacher)
15 |
16 | router.route("/:coursename/:courseID/add/student/:id").post(authSTD, addCourseStudent)
17 |
18 | router.route("/:coursename/:courseID/verify/student/:id").post(authSTD, canStudentEnroll)
19 |
20 | router.route("/student/:id/enrolled").get(authSTD, enrolledcourseSTD)
21 |
22 | router.route("/teacher/:id/enrolled").get(authTeacher, enrolledcourseTeacher)
23 |
24 | router.route("/:courseId/teacher/:teacherId/add-class").post(authTeacher, addClass)
25 |
26 | router.route("/classes/student/:studentId").get(authSTD, stdEnrolledCoursesClasses)
27 |
28 | router.route("/classes/teacher/:teacherId").get(authTeacher, teacherEnrolledCoursesClasses)
29 |
30 | export default router;
--------------------------------------------------------------------------------
/backend/src/routes/admin.routes.js:
--------------------------------------------------------------------------------
1 | import { Router } from "express";
2 | import { adminLogin, adminLogout, adminSignUp, approveStudent, approveTeacher, checkStudentDocuments, checkTeacherDocuments, forApproval, sendmessage, allmessages, readMessage, toapproveCourse, approveCourse } from "../controllers/admin.controller.js";
3 | import { authAdmin } from "../middlewares/adminAuth.middleware.js";
4 |
5 | const router = Router()
6 |
7 | router.route("/signup").post(adminSignUp)
8 |
9 | router.route("/login").post(adminLogin)
10 |
11 | router.route("/:adminID/approve").post(authAdmin, forApproval)
12 |
13 | router.route("/:adminID/approve/student/:studentID").post(authAdmin, approveStudent)
14 |
15 | router.route("/:adminID/approve/teacher/:teacherID").post(authAdmin,approveTeacher)
16 |
17 | router.route("/:adminID/documents/student/:studentID").get(authAdmin, checkStudentDocuments)
18 |
19 | router.route("/:adminID/documents/teacher/:teacherID").get(authAdmin, checkTeacherDocuments)
20 |
21 | router.route("/logout").post(authAdmin, adminLogout)
22 |
23 | router.route("/contact-us").post(sendmessage)
24 |
25 | router.route("/messages/all").get(authAdmin, allmessages)
26 |
27 | router.route("/message/read").patch(authAdmin, readMessage)
28 |
29 | router.route("/:adminID/approve/course").get(authAdmin, toapproveCourse)
30 |
31 | router.route("/:adminID/approve/course/:courseID").post(authAdmin, approveCourse)
32 |
33 | export default router;
34 |
35 | //testing
--------------------------------------------------------------------------------
/backend/src/models/admin.model.js:
--------------------------------------------------------------------------------
1 | import mongoose from "mongoose"
2 | import jwt from "jsonwebtoken"
3 | import bcrypt from "bcrypt"
4 |
5 |
6 | const adminSchema = new mongoose.Schema({
7 |
8 | username:{
9 | type:String,
10 | required:true,
11 | trim:true,
12 | lowercase:true
13 | },
14 |
15 | password:{
16 | type:String,
17 | required: true,
18 | },
19 |
20 | Refreshtoken:{
21 | type:String,
22 | },
23 |
24 | })
25 |
26 |
27 | adminSchema.pre("save", async function (next) {
28 | if(!this.isModified("password")) return next();
29 | this.password = await bcrypt.hash(this.password, 10)
30 | next()
31 | })
32 |
33 | adminSchema.methods.isPasswordCorrect = async function (password){
34 | return await bcrypt.compare(password, this.password)
35 | }
36 |
37 | adminSchema.methods.generateAccessToken = function(){
38 | return jwt.sign({
39 | _id:this._id,
40 | Email:this.Email,
41 | },
42 | process.env.ACCESS_TOKEN_SECRET,{
43 | expiresIn:process.env.ACCESS_TOKEN_EXPIRY
44 | })
45 | }
46 |
47 | adminSchema.methods.generateRefreshToken = function(){
48 | return jwt.sign({
49 | _id:this._id,
50 | Email:this.Email,
51 | },
52 | process.env.REFRESH_TOKEN_SECRET,{
53 | expiresIn:process.env.REFRESH_TOKEN_EXPIRY
54 | })
55 | }
56 |
57 | const admin = mongoose.model("admin",adminSchema);
58 |
59 | export {admin}
--------------------------------------------------------------------------------
/frontend/src/Pages/Components/RadioBtn/Radiobtn.css:
--------------------------------------------------------------------------------
1 | .radio-buttons-container {
2 | display: flex;
3 | align-items: center;
4 | gap: 24px;
5 | }
6 |
7 | .radio-button {
8 | display: inline-block;
9 | position: relative;
10 | cursor: pointer;
11 | }
12 |
13 | .radio-button__input {
14 | position: absolute;
15 | opacity: 0;
16 | width: 0;
17 | height: 0;
18 | }
19 |
20 | .radio-button__label {
21 | display: inline-block;
22 | padding-left: 30px;
23 | margin-bottom: 10px;
24 | position: relative;
25 | font-size: 16px;
26 | color: #fff;
27 | cursor: pointer;
28 | transition: all 0.3s cubic-bezier(0.23, 1, 0.320, 1);
29 | }
30 |
31 | .radio-button__custom {
32 | position: absolute;
33 | top: 50%;
34 | left: 0;
35 | transform: translateY(-50%);
36 | width: 20px;
37 | height: 20px;
38 | border-radius: 50%;
39 | border: 2px solid #555;
40 | transition: all 0.3s cubic-bezier(0.23, 1, 0.320, 1);
41 | }
42 |
43 | .radio-button__input:checked + .radio-button__label .radio-button__custom {
44 | transform: translateY(-50%) scale(0.9);
45 | border: 5px solid #4c8bf5;
46 | color: #4c8bf5;
47 | }
48 |
49 | .radio-button__input:checked + .radio-button__label {
50 | color: #4c8bf5;
51 | }
52 |
53 | .radio-button__label:hover .radio-button__custom {
54 | transform: translateY(-50%) scale(1.2);
55 | border-color: #4c8bf5;
56 | box-shadow: 0 0 10px #4c8bf580;
57 | }
58 |
59 |
--------------------------------------------------------------------------------
/frontend/src/Pages/Home/Header/Header.jsx:
--------------------------------------------------------------------------------
1 | import './Header.css'
2 | import { NavLink } from 'react-router-dom'
3 | import Logo from '../../Images/logo.svg'
4 |
5 | function Header() {
6 | return (
7 | <>
8 |
28 |
29 | >
30 | )
31 | }
32 |
33 | export default Header
34 |
--------------------------------------------------------------------------------
/frontend/src/Pages/Login/Images/Apple.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/backend/src/routes/teacher.routes.js:
--------------------------------------------------------------------------------
1 | import {Router} from "express";
2 | import {signup, mailVerified, login, logout, addTeacherDetails, getTeacher, teacherdocuments,ForgetPassword,ResetPassword} from "../controllers/teacher.controller.js";
3 | import {upload} from "../middlewares/multer.middleware.js"
4 | import { authTeacher } from "../middlewares/teacherAuth.middleware.js";
5 | import { authSchema } from "../middlewares/joiLogin.middleware.js";
6 |
7 |
8 | const router = Router()
9 |
10 | router.route("/signup").post(
11 | signup
12 | )
13 |
14 | router.route("/verify").get(
15 | mailVerified
16 | )
17 |
18 | router.route("/login").post(
19 | authSchema, login
20 | )
21 |
22 | router.route("/logout").post(
23 | authTeacher, logout
24 | )
25 |
26 | router.route("/verification/:id").post(authTeacher,
27 | upload.fields([
28 | {
29 | name:"Aadhaar",
30 | maxCount:1,
31 | },
32 | {
33 | name:"Secondary",
34 | maxCount:1,
35 | },
36 | {
37 | name:"Higher",
38 | maxCount:1
39 | },
40 | {
41 | name:"UG",
42 | maxCount:1
43 | },
44 | {
45 | name:"PG",
46 | maxCount:1
47 | }
48 | ]) ,
49 | addTeacherDetails)
50 |
51 | router.route("/teacherdocument/:id").get(authTeacher, getTeacher)
52 |
53 | router.route("/teacherdocuments").post(teacherdocuments)
54 |
55 |
56 | router.route('/forgetpassword').post(ForgetPassword)
57 |
58 | router.route('/forgetpassword/:token').post(ResetPassword)
59 |
60 | export default router;
61 |
--------------------------------------------------------------------------------
/frontend/src/Pages/Login/Login.css:
--------------------------------------------------------------------------------
1 | .main{
2 | margin-top: 10vh;
3 | margin-left: 10vh;
4 | color:white;
5 | font-family: sans-serif;
6 | display: flex;
7 | gap: 10rem;
8 | align-items: center;
9 | justify-content: center;
10 | }
11 |
12 | .main::-webkit-scrollbar {
13 | width: 0 !important;
14 | }
15 |
16 | .container{
17 | min-height: auto;
18 | width:380px;
19 | background-color: #0E3A59;
20 | text-align: center;
21 | border-radius: 15px;
22 | }
23 |
24 | .para1{
25 | font-size: large;
26 | margin-top: 10px;
27 | }
28 |
29 | .logo{
30 | padding: 15px;
31 | }
32 |
33 | .para{
34 | margin-top: 20px;
35 | font-size: 16px;
36 | letter-spacing: 1px;
37 | }
38 | .form{
39 | margin-top:40px;
40 | }
41 | .input-0{
42 | border:0;
43 | outline: 0;
44 | background: transparent;
45 | border: 2px solid gray;
46 | width: 70%;
47 | padding-block: 17px ;
48 | border-radius: 15px;
49 | padding-left: 1rem;
50 | color: white;
51 | }
52 |
53 | .input-0::placeholder{
54 | font-size: 16px;
55 | padding-left: 20px;
56 | }
57 |
58 | .input-2{
59 | margin-top:20px;
60 | }
61 |
62 | .btns{
63 | margin-top: 2rem;
64 | display: flex;
65 | justify-content: center;
66 | gap: 80px;
67 | margin-bottom: 1rem;
68 | }
69 |
70 | .btns-1{
71 | background-color: #157776;
72 | padding: 10px;
73 | width: 200px;
74 | }
75 |
76 | .radio-btn{
77 | display: flex;
78 | justify-content: center;
79 | margin-top: 2rem;
80 | }
81 |
82 | .signup-link{
83 | margin-top: 20px;
84 | font-size: small;
85 | }
86 |
87 | .signup-link p{
88 | color: #fff;
89 | }
90 |
91 | /* .signup-link .link{
92 | color: green;
93 | font-size: medium;
94 | } */
95 |
--------------------------------------------------------------------------------
/frontend/src/Pages/Dashboard/TeacherDashboard/DateTime.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from 'react';
2 | import DatePicker, { registerLocale } from 'react-datepicker';
3 | import 'react-datepicker/dist/react-datepicker.css';
4 | import { setHours, setMinutes, format } from 'date-fns';
5 |
6 | const DateTime = ({setDate, allowedDays}) => {
7 | const [selectedDate, setSelectedDate] = useState(null);
8 |
9 | const isAllowedDate = (date) => {
10 | // Check if the date's day is in the allowedDays array
11 | return allowedDays.some((allowedDay) => allowedDay.day === date.getDay());
12 | };
13 |
14 | const isAllowedTime = (date) => {
15 | const allowedDay = allowedDays.find((day) => day.day === date.getDay());
16 | if (!allowedDay) return false;
17 |
18 | const minutes = date.getHours() * 60 + date.getMinutes();
19 | return minutes >= allowedDay.starttime && minutes <= allowedDay.endtime - 60;
20 | };
21 |
22 | const filterTime = (time) => {
23 | return isAllowedTime(time);
24 | };
25 |
26 | useEffect(() => {
27 | if (selectedDate) {
28 | const formattedDate = format(selectedDate, "yyyy-MM-dd'T'HH:mm:ssXXX");
29 | // console.log('Selected Date and Time:', formattedDate.slice(0,19)+'Z');
30 | setDate(formattedDate.slice(0,19)+'Z');
31 | }
32 | }, [selectedDate]);
33 |
34 | return (
35 | <>
36 | setSelectedDate(date)}
39 | filterDate={isAllowedDate}
40 | showTimeSelect
41 | timeIntervals={15}
42 | timeFormat="HH:mm"
43 | minTime={setHours(setMinutes(new Date(), 0), 0)}
44 | maxTime={setHours(setMinutes(new Date(), 0), 23)}
45 | filterTime={filterTime}
46 | dateFormat="MMMM d, yyyy h:mm aa"
47 | placeholderText="Select a date and time"
48 | />
49 | >
50 | );
51 | };
52 |
53 | export default DateTime;
54 |
--------------------------------------------------------------------------------
/frontend/src/Pages/Home/About/About.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Plant from "../../Images/Plant.svg";
3 | import Plant2 from "../../Images/Plant2.svg";
4 | import '../Landing/Landing.css'
5 | import Footer from "../../Footer/Footer.jsx"
6 | import Header from '../Header/Header.jsx';
7 |
8 | function About({backgroundC}) {
9 | return (
10 | <>
11 |
12 |
13 |
About Us
14 |
15 |
16 |
17 |
18 |
19 |
20 | At Shiksharthee, we believe in the power of education to transform lives. Our platform is designed to be a gateway to knowledge, offering a diverse range of courses and learning experiences for students.
21 |
Our Story
22 | Shiksharthee was born out of a passion for learning and a desire to make quality education accessible to everyone. We understand the challenges faced by modern learners and strive to provide a solution that is both convenient and effective.
23 |
Our Mission
24 | Our mission is simple yet profound: to empower individuals through education. We aim to create a global learning community where students can discover new passions, enhance their skills, and achieve their academic and professional goals. By leveraging technology and innovative teaching methods, we strive to make learning engaging, interactive, and enjoyable.
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | >
33 | )
34 | }
35 |
36 | export default About
--------------------------------------------------------------------------------
/frontend/src/Pages/Dashboard/TeacherDashboard/TeacherCourses.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import Popup from './Popup';
3 |
4 | function TeacherCourses() {
5 | const [showPopup, setShowPopup] = useState(false);
6 | const [subject, setSubject] = useState('');
7 |
8 | const crreateCourse = (sub)=>{
9 | setShowPopup(true);
10 | setSubject(sub);
11 | }
12 |
13 | return (
14 | <>
15 |
16 |
crreateCourse("Physics")}>
17 |
18 |
Physics
19 |
20 |
crreateCourse("Chemistry")}>
21 |
22 |
Chemistry
23 |
24 |
crreateCourse("Biology")}>
25 |
26 |
Biology
27 |
28 |
crreateCourse("Math")}>
29 |
30 |
Math
31 |
32 |
crreateCourse("Computer")}>
33 |
34 |
Computer
35 |
36 |
37 | {showPopup && (
38 | setShowPopup(false)} subject={subject}/>
39 | )}
40 | >
41 |
42 | )}
43 |
44 | export default TeacherCourses
--------------------------------------------------------------------------------
/frontend/src/Pages/Footer/Footer.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import '../Home/Landing/Landing.css'
3 |
4 | function Footer() {
5 | return (
6 |
7 |
8 |
9 |
10 |
Title
11 |
12 |
Copyright © 2024
13 |
Small Change. Big Change.
14 |
15 |
16 |
17 |
21 |
25 |
26 |
27 |
How it works
28 |
29 |
33 |
34 |
35 |
Contact Us
36 |
37 |
38 |
39 |
Privacy Policy
40 |
41 |
42 |
43 |
49 |
50 |
51 | )
52 | }
53 |
54 | export default Footer
--------------------------------------------------------------------------------
/frontend/src/Pages/Signup/Styles.css:
--------------------------------------------------------------------------------
1 | .section{
2 | display: flex;
3 | align-items: center;
4 | margin-left: 200px;
5 | }
6 |
7 | .article{
8 | min-height: auto;
9 | width: 23rem;
10 | color: #FFF;
11 | font-family: sans-serif;
12 | background-color:#0E3A59;
13 | padding: 3px;
14 | border-radius: 15px;
15 | /* box-shadow: 2px 4px 10px black; */
16 | margin: auto;
17 | margin-top: 20px;
18 | }
19 |
20 | .logo-1{
21 | margin-left: 30px;
22 | }
23 |
24 | .header{
25 | text-align: center;
26 | margin-top: 5px;
27 | }
28 |
29 | .head{
30 | font-size: 24px;
31 | letter-spacing: 1px;
32 | font-weight: 600;
33 | }
34 |
35 | .Sub-head{
36 | margin-top: 10px;
37 | font-size: 14px;
38 | }
39 |
40 | .inpts{
41 | text-align: center;
42 | margin-top: 15px;
43 | }
44 |
45 | .input-x{
46 | border:0;
47 | outline: 0;
48 | background: transparent;
49 | border: 2px solid gray;
50 | width: 70%;
51 | padding-block:10px ;
52 | border-radius: 10px;
53 | padding-left: 1rem;
54 | color: white;
55 | margin:10px;
56 | }
57 |
58 | .input-x::placeholder{
59 | font-size: 16px;
60 | padding-left: 10px;
61 | }
62 |
63 | .rad-btns{
64 | margin: 1.2rem;
65 | margin-left: 5rem;
66 | }
67 |
68 | .signupage{
69 | margin-top: 15px;
70 | text-align: center;
71 | }
72 | .signupage span{
73 | font-size: small;
74 | }
75 |
76 | .btn-4{
77 | color: #FFF;
78 | background-color:#157776;
79 | display:block;
80 | text-align: center;
81 | margin-top: 20px;
82 | margin-left: 5.5rem;
83 | width:50%;
84 | padding: 10px;
85 | border-radius: 5px;
86 | transition: all linear 0.1s;
87 |
88 | }
89 |
90 | .btn-4:hover{
91 | background-color: #04848492;
92 | color:#fff;
93 | }
94 |
95 | .btn{
96 | margin-bottom: 1rem;
97 | }
98 |
99 | .imgs{
100 | width: 80%;
101 | margin-top: 40px;
102 | }
103 |
104 | .right-part{
105 | margin-left: 90px;
106 | }
107 |
108 | .error-message{
109 | color: #d41717;
110 | font-size: 0.9rem;
111 | }
--------------------------------------------------------------------------------
/frontend/src/Pages/Home/Contact/Contact.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import "../Landing/Landing.css";
3 | import Mail from "../../Images/Meet-the-team.svg";
4 | import Header from '../Header/Header';
5 |
6 | function Contact() {
7 | const [name, setName] = useState('');
8 | const [email, setEmail] = useState('');
9 | const [msg, setMsg] = useState('');
10 |
11 | const handlemsg = async(e)=>{
12 | e.preventDefault();
13 | if(name === '' || email === '' || msg === ''){
14 | alert("All filds are required!")
15 | }else if((!/\S+@\S+\.\S+/.test(email))){
16 | alert("Enter a valid email!")
17 | }else{
18 | const data = await fetch('/api/admin/contact-us',{
19 | method: 'POST',
20 | credentials: "include",
21 | headers: {
22 | "Content-Type": "application/json",
23 | },
24 | body: JSON.stringify({name, email, message: msg}),
25 | })
26 |
27 | const response = await data.json();
28 | alert(response.message);
29 | setName('');
30 | setEmail('');
31 | setMsg('');
32 | }
33 | }
34 |
35 | return (
36 | <>
37 |
38 |
39 |
Contact Us
40 |
41 |
42 |
43 |
67 |
68 |
69 | >
70 | )
71 | }
72 |
73 | export default Contact
--------------------------------------------------------------------------------
/backend/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 | .pnpm-debug.log*
9 |
10 | # Diagnostic reports (https://nodejs.org/api/report.html)
11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
12 |
13 | # Runtime data
14 | pids
15 | *.pid
16 | *.seed
17 | *.pid.lock
18 |
19 | # Directory for instrumented libs generated by jscoverage/JSCover
20 | lib-cov
21 |
22 | # Coverage directory used by tools like istanbul
23 | coverage
24 | *.lcov
25 |
26 | # nyc test coverage
27 | .nyc_output
28 |
29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
30 | .grunt
31 |
32 | # Bower dependency directory (https://bower.io/)
33 | bower_components
34 |
35 | # node-waf configuration
36 | .lock-wscript
37 |
38 | # Compiled binary addons (https://nodejs.org/api/addons.html)
39 | build/Release
40 |
41 | # Dependency directories
42 | node_modules/
43 | jspm_packages/
44 |
45 | # Snowpack dependency directory (https://snowpack.dev/)
46 | web_modules/
47 |
48 | # TypeScript cache
49 | *.tsbuildinfo
50 |
51 | # Optional npm cache directory
52 | .npm
53 |
54 | # Optional eslint cache
55 | .eslintcache
56 |
57 | # Microbundle cache
58 | .rpt2_cache/
59 | .rts2_cache_cjs/
60 | .rts2_cache_es/
61 | .rts2_cache_umd/
62 |
63 | # Optional REPL history
64 | .node_repl_history
65 |
66 | # Output of 'npm pack'
67 | *.tgz
68 |
69 | # Yarn Integrity file
70 | .yarn-integrity
71 |
72 | # dotenv environment variables file
73 | .env
74 | .env.test
75 | .env.production
76 |
77 | # parcel-bundler cache (https://parceljs.org/)
78 | .cache
79 | .parcel-cache
80 |
81 | # Next.js build output
82 | .next
83 | out
84 |
85 | # Nuxt.js build / generate output
86 | .nuxt
87 | dist
88 |
89 | # Gatsby files
90 | .cache/
91 | # Comment in the public line in if your project uses Gatsby and not Next.js
92 | # https://nextjs.org/blog/next-9-1#public-directory-support
93 | # public
94 |
95 | # vuepress build output
96 | .vuepress/dist
97 |
98 | # Serverless directories
99 | .serverless/
100 |
101 | # FuseBox cache
102 | .fusebox/
103 |
104 | # DynamoDB Local files
105 | .dynamodb/
106 |
107 | # TernJS port file
108 | .tern-port
109 |
110 | # Stores VSCode versions used for testing VSCode extensions
111 | .vscode-test
112 |
113 | # yarn v2
114 | .yarn/cache
115 | .yarn/unplugged
116 | .yarn/build-state.yml
117 | .yarn/install-state.gz
118 | .pnp.*
119 |
120 | # End of https://mrkandreev.name/snippets/gitignore-generator/#Node
--------------------------------------------------------------------------------
/frontend/src/Pages/Dashboard/StudentDashboard/Popup.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from 'react'
2 |
3 | function Popup({onClose, subject, allSubject}) {
4 | const [details, setDetails] = useState({})
5 | const [Tdec, setTeacherDetails] = useState(null);
6 | const [starCount, setStar] = useState(5);
7 |
8 | const price = {
9 | math: 700,
10 | physics: 800,
11 | computer: 1000,
12 | chemistry: 600,
13 | biology: 500,
14 | };
15 |
16 | useEffect(()=>{
17 | const fetchData = async()=>{
18 | let actualdts = await allSubject?.filter( res => res._id === subject._id)
19 | setDetails(actualdts[0]?.enrolledteacher)
20 | }
21 | fetchData();
22 | },[details])
23 |
24 | useEffect(()=>{
25 | const getData = async()=>{
26 | const data = await fetch('/api/teacher/teacherdocuments',{
27 | method: 'POST',
28 | credentials: "include",
29 | headers: {
30 | "Content-Type": "application/json",
31 | },
32 | body: JSON.stringify({teacherID : details.Teacherdetails}),
33 | })
34 | const res = await data.json();
35 | // console.log(res.data);
36 | setTeacherDetails(res.data);
37 | }
38 |
39 | getData();
40 | },[])
41 |
42 | return (
43 |
44 |
45 |
✖️
46 |
47 |
{subject.coursename.toUpperCase()}
48 |
49 |
50 |
{subject.description}
51 |
52 |
53 |
54 | {details && (
55 |
56 |
Teacher : {details.Firstname} {details.Lastname}
57 | {/*
Teacher : {details.Firstname} {details.Lastname} {'⭐'.repeat(starCount)}
*/}
58 |
Email : {details.Email}
59 | {/*
Course Duration : 6 Months
*/}
60 |
Fees : Rs. {price[subject.coursename]} per month / per student
61 |
62 | )}
63 |
64 |
65 | )
66 | }
67 |
68 | export default Popup
--------------------------------------------------------------------------------
/frontend/src/Pages/Dashboard/TeacherDashboard/Withdrawal.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import { useParams } from 'react-router-dom';
3 |
4 | function Withdrawal({onClose,TA}) {
5 | const { ID } = useParams();
6 | const [amount, setAmount] = useState(0);
7 | const [accName, setAccName] = useState('');
8 | const [accNumber, setAccNumber] = useState('');
9 | const [ifc, setIfc] = useState('');
10 |
11 | const handleWithdrawl = async()=>{
12 | if(accName == '' || accNumber == '' || ifc == ''){
13 | alert('All filds are required')
14 | }else if(TA < amount){
15 | alert('Insufficient Amount')
16 | }else if(amount < 0){
17 | alert('Enter a valid Amount')
18 | }else{
19 | try {
20 | const response = await fetch(`/api/payment/teacher/${ID}/withdraw`, {
21 | method: 'POST',
22 | headers: {
23 | 'Content-Type': 'application/json',
24 | },
25 | body: JSON.stringify({amount : amount})
26 | });
27 |
28 | if (!response.ok) {
29 | throw new Error('Failed to fetch data');
30 | }
31 |
32 | const user = await response.json();
33 | // setAmount(user.data.newTeacher.Balance);
34 | console.log(user)
35 | onClose();
36 | } catch (error) {
37 | // setError(error.message)
38 | console.log(error);
39 | }
40 | }
41 | }
42 |
43 | return (
44 |
60 | )
61 | }
62 |
63 | export default Withdrawal
--------------------------------------------------------------------------------
/frontend/src/Pages/ForgetPassword/Forgetpassword.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import { IoArrowBack } from "react-icons/io5";
3 | import { useNavigate } from 'react-router-dom';
4 | import Radiobtn from '../Components/RadioBtn/Radiobtn';
5 | import axios from 'axios';
6 | import { toast } from 'react-hot-toast';
7 |
8 | const Forgetpassword = () => {
9 | const [userType, setUserType] = useState('');
10 | const [data, setData] = useState({ email: '' });
11 | const navigate = useNavigate();
12 |
13 | const handleChange = (e) => {
14 | const { name, value } = e.target;
15 | setData({
16 | ...data,
17 | [name]: value
18 | });
19 | };
20 |
21 | const onFormSubmit = async (e) => {
22 | e.preventDefault();
23 |
24 | if (!data.email) {
25 | toast.error('Email is required');
26 | return;
27 | }
28 |
29 | const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
30 | if (!emailRegex.test(data.email)) {
31 | toast.error('Please provide a valid email');
32 | return;
33 | }
34 |
35 | try {
36 | const response = await axios.post(`/api/${userType}/forgetpassword`, { Email: data.email});
37 | console.log(response.data);
38 | toast.success('Email sent successfully');
39 | } catch (error) {
40 |
41 | toast.error('An error occurred while sending the email');
42 | }
43 | };
44 |
45 | console.log(userType);
46 |
47 | return (
48 |
49 |
50 | Forgot Your Password?
51 | Enter your email address below to reset your password.
52 | Email Address
53 |
62 |
63 |
64 |
65 |
66 |
Send
67 |
navigate(-1)}>
68 | Go back
69 |
70 |
71 |
72 |
73 | );
74 | };
75 |
76 | export default Forgetpassword;
77 |
--------------------------------------------------------------------------------
/frontend/src/Pages/Images/Plant2.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/frontend/src/Pages/Home/Courses/Courses.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import '../Landing/Landing.css'
3 | import Footer from '../../Footer/Footer'
4 | import Header from '../Header/Header'
5 |
6 | function Courses() {
7 | const [facList, setFacList] = useState([]);
8 | const [loading, setLoading] = useState(true);
9 |
10 | const teachersList = async(sub)=>{
11 | setLoading(true);
12 |
13 | const response = await fetch(`/api/course/${sub}`, {
14 | method: 'GET',
15 | credentials: "include",
16 | headers: {
17 | "Content-Type": "application/json",
18 | }
19 | });
20 |
21 | const data = await response.json();
22 | setFacList(data.data);
23 | console.log(data.data);
24 | setLoading(false);
25 | }
26 |
27 | return (
28 | <>
29 |
30 |
31 |
Faculty List
32 |
33 |
34 |
teachersList("physics")}>
35 |
36 |
Physics
37 |
38 |
teachersList("chemistry")}>
39 |
40 |
Chemistry
41 |
42 |
teachersList("biology")}>
43 |
44 |
Biology
45 |
46 |
teachersList("math")}>
47 |
48 |
Math
49 |
50 |
teachersList("computer")}>
51 |
52 |
Computer
53 |
54 |
55 |
56 |
57 |
58 | {!loading && facList && (
59 | facList.map(fac => (
60 |
61 |
62 |
63 |
64 |
{fac.enrolledteacher.Firstname} {fac.enrolledteacher.Lastname}
65 |
{fac.enrolledteacher.Email}
66 |
67 |
68 | { fac.enrolledteacher.Email === "urttsg@gmail.com" ?
69 |
Education : Post graduate from Calcutta University
70 | :
71 |
Education : Post graduate from Sister Nivedita university
72 | }
73 | { fac.enrolledteacher.Email === "urttsg@gmail.com" ?
1 years of teaching experience :
2 years of teaching experience }
74 |
75 | )))}
76 |
77 |
78 |
79 |
80 | >
81 | )
82 | }
83 |
84 | export default Courses
--------------------------------------------------------------------------------
/frontend/src/Pages/Dashboard/StudentDashboard/StudentCourses.jsx:
--------------------------------------------------------------------------------
1 | import React,{ useEffect, useState } from 'react'
2 | import { useParams } from 'react-router-dom'
3 | import Popup from './Popup';
4 | import axios from 'axios';
5 |
6 | function StudentCourses() {
7 | const { ID } = useParams();
8 | const [data, setdata] = useState([]);
9 | const [popup, setPopup] = useState(false);
10 | const [subDetails, setsubDetails] = useState({});
11 | const [subD, setsubD] = useState();
12 |
13 | useEffect(() => {
14 | const getData = async () => {
15 | try {
16 | const response = await fetch(`/api/course/student/${ID}/enrolled`, {
17 | method: 'GET',
18 | headers: {
19 | 'Content-Type': 'application/json',
20 | },
21 | });
22 |
23 | if (!response.ok) {
24 | throw new Error('Failed to fetch data');
25 | }
26 |
27 | const user = await response.json();
28 | setdata(user.data);
29 | console.log(user.data);
30 |
31 | } catch (error) {
32 | setError(error.message)
33 | }
34 | };
35 | getData();
36 | },[]);
37 |
38 | const openpopup = async(sub)=>{
39 | setsubDetails(sub);
40 | await axios.get(`/api/course/${sub.coursename}`)
41 | .then(res => {setPopup(true);
42 | setsubD(res.data.data)})
43 | }
44 |
45 | const price = {
46 | math: 700,
47 | physics: 800,
48 | computer: 1000,
49 | chemistry: 600,
50 | biology: 500,
51 | };
52 |
53 | const daysName = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
54 |
55 | const Image = {
56 | "physics" : "https://www.figma.com/file/6b4R8evBkii6mI53IA4vSS/image/8e9bf690d23d886f63466a814cfbec78187f91d2",
57 | "chemistry" : "https://www.figma.com/file/6b4R8evBkii6mI53IA4vSS/image/3e546b344774eb0235acc6bf6dad7814a59d6e95",
58 | "biology" : "https://www.figma.com/file/6b4R8evBkii6mI53IA4vSS/image/28ac70002ae0a676d9cfb0f298f3e453d12b5555",
59 | "math" : "https://www.figma.com/file/6b4R8evBkii6mI53IA4vSS/image/61930117e428a1f0f7268f888a84145f93aa0664",
60 | "computer" : "https://www.figma.com/file/6b4R8evBkii6mI53IA4vSS/image/a64c93efe984ab29f1dfb9e8d8accd9ba449f272",
61 | }
62 |
63 | return (
64 | <>
65 |
66 | {data.map(sub => (
67 |
openpopup(sub)}>
68 |
69 |
70 |
{sub.coursename.toUpperCase()}
71 |
72 |
{sub.description}
73 |
74 | {sub.schedule && (
75 |
76 |
Timing:
77 | {'[ '}
78 | {sub.schedule.map(daytime => {
79 | return `${daysName[daytime.day]} ${Math.floor(daytime.starttime / 60)}:${daytime.starttime % 60 === 0 ? "00" : daytime.starttime % 60} - ${Math.floor(daytime.endtime/60)}:${daytime.endtime % 60 === 0 ? "00" : daytime.endtime % 60}`;
80 | }).join(', ')}
81 | {' ]'}
82 |
83 | )}
84 |
85 | {/*
Fees : Rs. {price[sub.coursename]}
*/}
86 |
87 | ))}
88 |
89 | {popup && (
90 | setPopup(false)} subject={subDetails} allSubject={subD}/>
91 | )}
92 | >
93 | )
94 | }
95 |
96 | export default StudentCourses
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # Online Learning Platform
3 |
4 | Online Learning Platform using MERN Stack,
5 | Please visit [here](https://code2tutorial.com/tutorial/55a23a94-6cb7-4c48-92c1-f96db207f791/index.md) for a better understanding of the project.
6 | ## Objective:
7 |
8 | Develop a comprehensive online learning platform with three user types (Student, Teacher, Admin), featuring course creation, approval process, and live video conferencing.
9 |
10 | ## *BACKEND for the PROJECT
11 | - Backend is developed by [Parag](https://github.com/paragkadyan).
12 |
13 | ## Features
14 | 
15 |
16 |
17 | #### 1. *User Authentication:*
18 | - Student Login [/login]
19 | - Teacher Login [/login]
20 | - Admin Login [/adminLogin]
21 |
22 | 
23 |
24 | 
25 |
26 | #### 2. *Application Approval:*
27 | - Students and teachers can submit applications for approval.
28 | - Admin validates and approves applications.
29 |
30 | 
31 |
32 |
33 | #### 3. *Dashboard:*
34 | - Students see purchased courses, progress, and communication options.
35 | - Teachers view created courses, student enrollments, and communication features.
36 |
37 |
38 | 
39 | 
40 | #### 4. *Course Purchase:*
41 |
42 | - Students can browse and buy courses on the platform.
43 |
44 | 
45 |
46 | #### 5. *Live Video Conferencing:*
47 | - Integrated video conferencing tool (similar to Google Meet) for real-time teacher-student interaction.
48 |
49 | #### 6. *Communication:*
50 | - An in-platform messaging system for communication between teachers and students.
51 |
52 | #### 7. *Payment Integration:*
53 | - Integrate a secure payment gateway for course purchases.
54 |
55 | ----
56 |
57 |
58 | ## *Tech Stack:*
59 |
60 | #### *UI/UX:*
61 | - [figma](https://www.figma.com/file/6b4R8evBkii6mI53IA4vSS/Online-Learning-Platform?type=design&node-id=0-1&mode=design&t=HBUPk2hRYW3ioAUj-0)
62 | - Dribbble
63 |
64 | #### *Frontend:*
65 | - React (Vite) for dynamic and responsive UI.
66 |
67 | #### *Backend:*
68 | - Node.js, Express and Mongoose for server-side development.
69 |
70 | #### *Database:*
71 | - MongoDB for storing user profiles, course details, and application data.
72 |
73 | #### *Authentication:*
74 | - JWT (JSON Web Tokens) for secure authentication.
75 |
76 | #### *Video Conferencing:*
77 |
78 | ---
79 |
80 | ## 🛠️ Installation & Running Locally
81 |
82 | Follow the steps below to run the project on your local machine.
83 |
84 | ### ✅ Prerequisites
85 |
86 | - [Node.js](https://nodejs.org/) (v14 or above)
87 | - [MongoDB](https://www.mongodb.com/) (local or Atlas cloud)
88 | - [Git](https://git-scm.com/)
89 |
90 | ---
91 |
92 | ### 📦 Clone the Repository
93 |
94 | ```bash
95 | git clone https://github.com/Pika003/e-Learning-Platform.git
96 | cd e-Learning-Platform
97 | - Fill the env file with your credentials
98 | npm install
99 | npm run dev
100 |
101 |
102 |
--------------------------------------------------------------------------------
/frontend/src/Pages/ForgetPassword/ResetPassword.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import toast from 'react-hot-toast';
3 | import axios from 'axios';
4 |
5 | import { useNavigate, useParams } from 'react-router-dom';
6 |
7 | const ResetPassword = () => {
8 | const [data, setData] = useState({
9 | password: '',
10 | confirmPassword: ''
11 | });
12 |
13 | const navigate=useNavigate()
14 |
15 | const { token }=useParams()
16 |
17 | const handleChange = (e) => {
18 | const { name, value } = e.target;
19 | setData({
20 | ...data,
21 | [name]: value
22 | });
23 | };
24 |
25 | const handleSubmit = async (e) => {
26 | e.preventDefault();
27 |
28 | const { password, confirmPassword } = data;
29 |
30 | // Validation checks
31 | if (!password || !confirmPassword) {
32 | toast.error("Both fields are required");
33 | return;
34 | }
35 |
36 | if (password !== confirmPassword) {
37 | toast.error("Passwords do not match");
38 | return;
39 | }
40 |
41 | const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;
42 |
43 | if (!passwordRegex.test(password)) {
44 | toast.error("Password must be at least 8 characters long and include an uppercase letter, a lowercase letter, a number, and a special character.");
45 | return;
46 | }
47 |
48 | const response= axios.post(`/api/student/forgetpassword/${token}`,{password:data.password,confirmPassword:data.confirmPassword})
49 | toast.promise(response,{
50 | loading:"wait for processing",
51 | success:(response)=> response?.data?.message,
52 | error:"Time limit Reached Try again"
53 |
54 | })
55 |
56 | if((await response).data.success){
57 | navigate('/login')
58 | }
59 |
60 |
61 | };
62 |
63 | return (
64 |
65 |
66 |
71 | This link is valid for 15 mins otherwise password will not updated
72 |
76 | New Password
77 |
78 |
87 |
88 |
92 | Confirm Password
93 |
94 |
103 |
107 | Submit
108 |
109 |
110 |
111 | );
112 | };
113 |
114 | export default ResetPassword;
115 |
--------------------------------------------------------------------------------
/frontend/src/Pages/Dashboard/StudentDashboard/StudentClasses.jsx:
--------------------------------------------------------------------------------
1 | import React,{ useEffect, useState } from 'react'
2 | import Camera from '../Images/Camera.png'
3 | import Clock from '../Images/Clock.png'
4 | import { NavLink, useParams } from 'react-router-dom'
5 |
6 | function StudentClasses() {
7 | const { ID } = useParams();
8 | const [data, setdata] = useState([]);
9 |
10 | useEffect(() => {
11 | const getData = async () => {
12 | try {
13 | const response = await fetch(`/api/course/classes/student/${ID}`, {
14 | method: 'GET',
15 | headers: {
16 | 'Content-Type': 'application/json',
17 | },
18 | });
19 |
20 | if (!response.ok) {
21 | throw new Error('Failed to fetch data');
22 | }
23 |
24 | const user = await response.json();
25 | setdata(user.data.classes[0].liveClasses);
26 | console.log(user.data.classes[0].liveClasses);
27 |
28 | } catch (error) {
29 | setError(error.message)
30 | }
31 | };
32 | getData();
33 | },[ID]);
34 |
35 | return (
36 |
37 |
Weekly Schedule
38 |
39 |
40 | {data.filter(clas => {
41 | const classDate = new Date(clas.date.slice(0, 10));
42 | const today = new Date();
43 | const oneWeekFromNow = new Date(today);
44 | oneWeekFromNow.setDate(today.getDate() + 7);
45 |
46 | return classDate >= today && classDate <= oneWeekFromNow;
47 | }).map((clas) => (
48 |
49 |
50 |
51 |
{clas.coursename}
52 |
53 | {clas.date.slice(0, 10)} {Math.floor(clas.timing / 60)}:{clas.timing % 60 === 0 ? "00" : clas.timing % 60}
54 |
55 |
56 |
{clas.title.slice(0, 35)} ...
57 |
58 |
{clas.status}
59 |
60 | ))}
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
{typeof data[0]?.date === 'string' ? data[0]?.date.slice(0,10) : ''}
69 |
70 | {typeof data[0]?.timing === 'number' ? `${Math.floor(data[0]?.timing / 60)}:${data[0]?.timing % 60 === 0 ?"00":data[0]?.timing % 60}` :''}
71 |
72 |
73 |
74 |
75 |
Your next Class
76 |
{data[0]?.coursename}
77 |
{data[0]?.title.slice(0,25)} ...
78 |
79 |
80 |
81 |
82 |
83 |
84 | )
85 | }
86 |
87 | export default StudentClasses
--------------------------------------------------------------------------------
/frontend/src/Pages/Dashboard/StudentDashboard/StudentDashboard.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from 'react'
2 | import teachingImg from '../../Images/Teaching.svg'
3 | import { NavLink, useParams, useNavigate } from 'react-router-dom'
4 | import logo from '../../Images/logo.svg'
5 |
6 |
7 | function StudentDashboard() {
8 | const { ID } = useParams();
9 | const navigator = useNavigate();
10 | const [data, setdata] = useState([]);
11 | const [error, setError] = useState(null);
12 |
13 | const Handlelogout = async() =>{
14 | const response = await fetch(`/api/student/logout`, {
15 | method: 'POST',
16 | credentials: "include",
17 | headers: {
18 | "Content-Type": "application/json",
19 | }
20 | });
21 | const data = await response.json();
22 | if(data.statusCode == 200){
23 | navigator('/');
24 | }
25 | }
26 |
27 | useEffect(() => {
28 | const getData = async () => {
29 | try {
30 | const response = await fetch(`/api/Student/StudentDocument/${ID}`, {
31 | method: 'GET',
32 | headers: {
33 | 'Content-Type': 'application/json',
34 | }
35 | });
36 |
37 | if (!response.ok) {
38 | throw new Error('Failed to fetch data');
39 | }
40 |
41 | const user = await response.json();
42 | setdata(user.data);
43 | } catch (error) {
44 | setError(error.message)
45 | }
46 | };
47 | getData();
48 | },[]);
49 |
50 | return (
51 | <>
52 | {/* navbar */}
53 |
54 |
55 |
56 |
58 |
Shiksharthee
59 |
60 |
61 |
64 |
65 |
66 |
67 |
68 |
Welcome to Shiksharthee
69 | {data.Firstname} {data.Lastname}
70 |
71 |
72 |
73 |
74 |
75 |
76 | {/* sidebar */}
77 |
78 |
79 |
80 |
{data.Firstname} {data.Lastname}
81 |
82 |
83 |
84 | isActive ? "bg-white p-3 px-[4.61rem] text-center font-semibold text-[#4E84C1]" : "p-3 text-center font-semibold text-[#4E84C1]" }>
85 | Teacher
86 |
87 |
88 | isActive ? "bg-white p-3 px-[4.61rem] text-center font-semibold text-[#4E84C1]" : "p-3 text-center font-semibold text-[#4E84C1]" }>
89 | Classes
90 |
91 |
92 | isActive ? "bg-white p-3 px-[4.61rem] text-center font-semibold text-[#4E84C1]" : "p-3 text-center font-semibold text-[#4E84C1]" }>
93 | Courses
94 |
95 |
96 |
97 |
98 | >
99 | )
100 | }
101 |
102 | export default StudentDashboard
--------------------------------------------------------------------------------
/frontend/src/Pages/Dashboard/TeacherDashboard/TeacherDashboard.jsx:
--------------------------------------------------------------------------------
1 | import React , { useEffect, useState } from 'react'
2 | import teachingImg from '../../Images/Teaching.svg'
3 | import { NavLink, useParams, useNavigate } from 'react-router-dom'
4 | import logo from '../../Images/logo.svg'
5 |
6 | function TeacherDashboard() {
7 | const { ID } = useParams();
8 | const navigator = useNavigate();
9 | const [data, setdata] = useState([]);
10 |
11 | const Handlelogout = async() =>{
12 | const response = await fetch(`/api/teacher/logout`, {
13 | method: 'POST',
14 | credentials: "include",
15 | headers: {
16 | "Content-Type": "application/json",
17 | }
18 | });
19 | const data = await response.json();
20 | console.log(data);
21 | if(data.statusCode == 200){
22 | navigator('/');
23 | }
24 | }
25 |
26 | useEffect(() => {
27 | const getData = async () => {
28 | try {
29 | const response = await fetch(`/api/Teacher/TeacherDocument/${ID}`, {
30 | method: 'GET',
31 | headers: {
32 | 'Content-Type': 'application/json',
33 | },
34 | });
35 |
36 | if (!response.ok) {
37 | throw new Error('Failed to fetch data');
38 | }
39 |
40 | const user = await response.json();
41 | setdata(user.data);
42 | // console.log(user)
43 |
44 |
45 | } catch (error) {
46 | // setError(error.message)
47 | }
48 | };
49 | getData();
50 | },[]);
51 |
52 | return (
53 | <>
54 | {/* navbar */}
55 |
56 |
57 |
58 |
60 |
Shiksharthee
61 |
62 |
63 |
66 |
67 |
68 |
69 |
70 |
Welcome to Shiksharthee
71 | {data.Firstname} {data.Lastname}
72 |
73 |
74 |
75 |
76 |
77 |
78 | {/* sidebar */}
79 |
80 |
81 |
82 |
{data.Firstname} {data.Lastname}
83 |
84 |
85 |
86 | isActive ? "bg-white p-3 px-[4.61rem] text-center font-semibold text-[#4E84C1]" : "p-3 text-center font-semibold text-[#4E84C1]" }>
87 | Dashboard
88 |
89 |
90 | isActive ? "bg-white p-3 px-[4.61rem] text-center font-semibold text-[#4E84C1]" : "p-3 text-center font-semibold text-[#4E84C1]" }>
91 | Classes
92 |
93 |
94 | isActive ? "bg-white p-3 px-[4.61rem] text-center font-semibold text-[#4E84C1]" : "p-3 text-center font-semibold text-[#4E84C1]" }>
95 | Courses
96 |
97 |
98 |
99 |
100 | >
101 | )
102 | }
103 |
104 | export default TeacherDashboard
--------------------------------------------------------------------------------
/frontend/src/Pages/ForgetPassword/ResetTeacher.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import toast from 'react-hot-toast';
3 | import axios from 'axios';
4 | import { useNavigate, useParams } from 'react-router-dom';
5 |
6 | const ResetTeacher = () => {
7 | const [data, setData] = useState({
8 | password: '',
9 | confirmPassword: ''
10 | });
11 |
12 | const { token } = useParams();
13 | const navigate = useNavigate();
14 |
15 | const handleChange = (e) => {
16 | const { name, value } = e.target;
17 | setData({
18 | ...data,
19 | [name]: value
20 | });
21 | };
22 |
23 | const handleSubmit = async (e) => {
24 | e.preventDefault();
25 |
26 | const { password, confirmPassword } = data;
27 |
28 | // Validation checks
29 | if (!password || !confirmPassword) {
30 | toast.error("Both fields are required");
31 | return;
32 | }
33 |
34 | if (password !== confirmPassword) {
35 | toast.error("Passwords do not match");
36 | return;
37 | }
38 |
39 | const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;
40 |
41 | if (!passwordRegex.test(password)) {
42 | toast.error("Password must be at least 8 characters long and include an uppercase letter, a lowercase letter, a number, and a special character.");
43 | return;
44 | }
45 |
46 | try {
47 | const response = axios.post(`/api/teacher/forgetpassword/${token}`, {
48 | password: data.password,
49 | confirmPassword: data.confirmPassword
50 | });
51 |
52 | toast.promise(response, {
53 | loading: "Wait for processing",
54 | success: (response) => response?.data?.message,
55 | error: "Time limit reached. Try again."
56 | });
57 |
58 | if ((await response).data.success) {
59 | navigate('/login');
60 | }
61 | } catch (error) {
62 | toast.error("An error occurred. Please try again.");
63 | }
64 | };
65 |
66 | return (
67 |
117 | );
118 | };
119 |
120 | export default ResetTeacher;
121 |
--------------------------------------------------------------------------------
/frontend/src/main.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import './index.css'
3 | import ReactDOM from 'react-dom/client'
4 | import Landing from './Pages/Home/Landing/Landing'
5 | import About from './Pages/Home/About/About'
6 | import Contact from './Pages/Home/Contact/Contact'
7 | import Courses from './Pages/Home/Courses/Courses'
8 | import Login from './Pages/Login/Login'
9 | import Signup from './Pages/Signup/Signup'
10 | import AdminLogin from './Pages/Login/AdminLogin'
11 |
12 | import { RouterProvider, Route, createBrowserRouter, createRoutesFromElements } from 'react-router-dom'
13 | import Layout from './Layout'
14 | import StudentDocument from './Pages/Components/DocumentVerification/StudentDocument'
15 | import TeacherDocument from './Pages/Components/DocumentVerification/TeacherDocument'
16 | import VarifyEmail from './Pages/Components/VarifyEmail/VarifyEmail'
17 | import Rejected from './Pages/Response/Rejected'
18 | import Pending from './Pages/Response/Pending'
19 | import Admin from './Pages/Components/Admin/Admin'
20 | import VarifyDoc from './Pages/Components/Admin/VarifyDoc'
21 | import TeacherLayout from './Pages/Dashboard/TeacherDashboard/TeacherLayout'
22 | import StudentLayout from './Pages/Dashboard/StudentDashboard/StudentLayout'
23 | import SearchTeacher from './Pages/Dashboard/StudentDashboard/SearchTeacher'
24 | import StudentClasses from './Pages/Dashboard/StudentDashboard/StudentClasses'
25 | import StudentCourses from './Pages/Dashboard/StudentDashboard/StudentCourses'
26 | import DashboardTeacher from './Pages/Dashboard/TeacherDashboard/DashboardTeacher'
27 | import TeacherClasses from './Pages/Dashboard/TeacherDashboard/TeacherClasses'
28 | import TeacherCourses from './Pages/Dashboard/TeacherDashboard/TeacherCourses'
29 | import SearchData from './Pages/Home/Search/Search'
30 | import ErrorPage from './Pages/ErrorPage/ErrorPage'
31 | import Forgetpassword from './Pages/ForgetPassword/Forgetpassword'
32 | import ResetPassword from './Pages/ForgetPassword/ResetPassword'
33 | import { Toaster } from 'react-hot-toast'
34 | import ResetTeacher from './Pages/ForgetPassword/ResetTeacher'
35 | import Course from './Pages/Components/Admin/Course'
36 |
37 |
38 | const router = createBrowserRouter(
39 | createRoutesFromElements(
40 |
41 | }>
42 |
43 | }/>
44 | }/>
45 | }/>
46 | }/>
47 | }/>
48 | }/>
49 | }/>
50 | }/>
51 | }/>
52 | }/>
53 | }/>
54 | }/>
55 | }/>
56 | }/>
57 | }/>
58 | }/>
59 | }>
60 | }/>
61 | }/>
62 | }/>
63 |
64 | }>
65 | }/>
66 | }/>
67 | }/>
68 |
69 |
70 | }/>
71 | }/>
72 | }/>
73 |
74 |
75 | }/>
76 |
77 |
78 | )
79 | )
80 |
81 | ReactDOM.createRoot(document.getElementById('root')).render(
82 |
83 |
84 |
85 | ,
86 | )
87 |
88 | //testing
--------------------------------------------------------------------------------
/backend/src/models/student.model.js:
--------------------------------------------------------------------------------
1 | import mongoose from "mongoose";
2 | import jwt from "jsonwebtoken";
3 | import bcrypt from "bcrypt";
4 | import crypto from "crypto"
5 |
6 |
7 | const studentSchema = new mongoose.Schema({
8 |
9 | Email:{
10 | type:String,
11 | required:true,
12 | unique:true,
13 | trim:true,
14 | lowercase:true,
15 | index:true,
16 | },
17 |
18 | Firstname:{
19 | type:String,
20 | required:true,
21 | trim:true,
22 |
23 | },
24 |
25 | Lastname:{
26 | type:String,
27 | required:true,
28 | trim:true,
29 | },
30 |
31 | Password:{
32 | type:String,
33 | required: true,
34 | },
35 |
36 | Isverified: {
37 | type:Boolean,
38 | default:false,
39 | },
40 |
41 | Isapproved:{
42 | type: String,
43 | enum: ['approved', 'rejected', 'pending', 'reupload'],
44 | default: 'pending',
45 | },
46 |
47 | Remarks:{
48 | type:String
49 | },
50 |
51 | Refreshtoken:{
52 | type:String,
53 | },
54 |
55 | Studentdetails:{
56 | type:mongoose.Schema.Types.ObjectId,
57 | ref:"studentdocs"
58 | },
59 |
60 | forgetPasswordToken: String,
61 |
62 | forgetPasswordExpiry: Date,
63 |
64 | },
65 |
66 | {
67 | timestamps:true,
68 | }
69 | )
70 |
71 | studentSchema.pre("save", async function(next) {
72 | if(this.isModified('Firstname') || this.isNew){
73 | this.Firstname = this.Firstname.charAt(0).toUpperCase() + this.Firstname.slice(1).toLowerCase();
74 | }
75 |
76 | if(this.isModified('Lastname') || this.isNew){
77 | this.Lastname = this.Lastname.charAt(0).toUpperCase() + this.Lastname.slice(1).toLowerCase();
78 | }
79 |
80 | next()
81 | })
82 |
83 | studentSchema.pre("save", async function (next) {
84 | if(!this.isModified("Password")) return next();
85 | this.Password = await bcrypt.hash(this.Password, 10)
86 | next()
87 | })
88 |
89 | studentSchema.methods.isPasswordCorrect = async function (Password){
90 | return await bcrypt.compare(Password, this.Password)
91 | }
92 |
93 | studentSchema.methods.generateAccessToken = function(){
94 | return jwt.sign({
95 | _id:this._id,
96 | Email:this.Email,
97 | },
98 | process.env.ACCESS_TOKEN_SECRET,{
99 | expiresIn:process.env.ACCESS_TOKEN_EXPIRY
100 | })
101 | }
102 |
103 | studentSchema.methods.generateRefreshToken = function(){
104 | return jwt.sign({
105 | _id:this._id,
106 | Email:this.Email,
107 | },
108 | process.env.REFRESH_TOKEN_SECRET,{
109 | expiresIn:process.env.REFRESH_TOKEN_EXPIRY
110 | })
111 | }
112 |
113 | studentSchema.methods.generateResetToken =async function(){
114 |
115 | const reset=crypto.randomBytes(20).toString('hex') ;
116 |
117 | this.forgetPasswordToken=crypto.createHash('sha256').update(reset).digest('hex') ;
118 |
119 | this.forgetPasswordExpiry=Date.now() + 15 * 60 * 1000 ;
120 |
121 | await this.save() ;
122 |
123 | }
124 |
125 |
126 |
127 |
128 |
129 |
130 | const studentDetailsSchema = new mongoose.Schema({
131 | Phone:{
132 | type:Number,
133 | required: true,
134 | trim:true,
135 | unique:true,
136 | },
137 |
138 | Address:{
139 | type:String,
140 | required:true,
141 | },
142 |
143 | Highesteducation:{
144 | type:String,
145 | required:true,
146 | },
147 |
148 | SecondarySchool:{
149 | type:String,
150 | required:true,
151 | },
152 |
153 | HigherSchool:{
154 | type:String,
155 | required:true,
156 | },
157 |
158 | SecondaryMarks:{
159 | type:Number,
160 | required:true,
161 | },
162 |
163 | HigherMarks:{
164 | type:Number,
165 | required:true,
166 | },
167 |
168 | Aadhaar:{
169 | type:String,
170 | required:true,
171 | },
172 |
173 | Secondary:{
174 | type:String,
175 | required:true,
176 | },
177 |
178 | Higher:{
179 | type:String,
180 | required:true,
181 | },
182 |
183 | }, {
184 | timestamps:true,
185 | })
186 |
187 |
188 |
189 | const student = mongoose.model("student",studentSchema)
190 |
191 | const studentdocs = mongoose.model("studentdocs", studentDetailsSchema)
192 |
193 | export {student, studentdocs}
--------------------------------------------------------------------------------
/backend/src/controllers/payment.controller.js:
--------------------------------------------------------------------------------
1 | import { asyncHandler } from "../utils/asyncHandler.js";
2 | import { ApiError } from "../utils/ApiError.js";
3 | import { ApiResponse } from "../utils/ApiResponse.js";
4 | import {instance} from "../app.js"
5 | import crypto from "crypto"
6 | import {payment} from "../models/payment.model.js"
7 | import { Teacher } from "../models/teacher.model.js";
8 |
9 |
10 | const coursePayment = asyncHandler(async(req,res)=>{
11 | const {fees, } = req.body
12 |
13 | if(!fees){
14 | throw new ApiError(400,"fees is required")
15 | }
16 |
17 | const options = {
18 | amount: fees, // amount in the smallest currency unit
19 | currency: "INR",
20 | receipt: "order_rcptid_11"
21 | };
22 | const order = await instance.orders.create(options)
23 |
24 | return res
25 | .status(200)
26 | .json( new ApiResponse(200, order,"order fetched"))
27 | })
28 |
29 |
30 | const getkey = asyncHandler(async(req,res)=>{
31 | return res
32 | .status(200)
33 | .json(new ApiResponse(200,{key:process.env.KEY_ID}, "razor key fetched"))
34 | })
35 |
36 |
37 | const coursePaymentConfirmation = asyncHandler(async(req,res)=>{
38 | const { razorpay_order_id, razorpay_payment_id, razorpay_signature } =
39 | req.body;
40 |
41 | const studentID = req.Student._id
42 | const courseID = req.params.courseID
43 | console.log(courseID)
44 |
45 | const body = razorpay_order_id + "|" + razorpay_payment_id;
46 |
47 | const expectedSignature = crypto
48 | .createHmac("sha256", process.env.KEY_SECRET)
49 | .update(body.toString())
50 | .digest("hex");
51 |
52 | const isAuthentic = expectedSignature === razorpay_signature;
53 |
54 | if (isAuthentic) {
55 |
56 | const orderDetails = await payment.create({
57 | razorpay_order_id,
58 | razorpay_payment_id,
59 | razorpay_signature,
60 | courseID,
61 | studentID,
62 | });
63 |
64 | return res
65 | .status(200)
66 | .json(new ApiResponse(200,{orderDetails}, "payment confirmed" ))
67 | } else {
68 | throw new ApiError(400, "payment failed")
69 | }
70 | })
71 |
72 |
73 | const teacherAmount = asyncHandler(async(req,res)=>{
74 | const teacher = req.teacher
75 |
76 | const newEnrolledStudentCount = await Teacher.aggregate([
77 | {
78 | $match: { _id: teacher._id }
79 | },
80 | {
81 | $unwind: "$enrolledStudent"
82 | },
83 | {
84 | $match: { "enrolledStudent.isNewEnrolled": true }
85 | },
86 | {
87 | $group: {
88 | _id: null,
89 | count: { $sum: 1 }
90 | }
91 | }
92 | ]);
93 |
94 | const count = newEnrolledStudentCount.length > 0 ? newEnrolledStudentCount[0].count : 0;
95 |
96 |
97 | await Teacher.findByIdAndUpdate(
98 | teacher._id,
99 | { $inc: { Balance: count * 500 } },
100 |
101 | );
102 |
103 | const newTeacher = await Teacher.findOneAndUpdate(
104 | { _id: teacher._id, "enrolledStudent.isNewEnrolled": true },
105 | { $set: { "enrolledStudent.$[elem].isNewEnrolled": false } },
106 | {
107 | new: true,
108 | arrayFilters: [{ "elem.isNewEnrolled": true }],
109 | }
110 | );
111 |
112 | if(!newTeacher){
113 | const newTeacher = await Teacher.findById(
114 | teacher._id
115 | )
116 |
117 | return res
118 | .status(200)
119 | .json(new ApiResponse(200, {newTeacher}, "balance"))
120 | }
121 |
122 |
123 | return res
124 | .status(200)
125 | .json(new ApiResponse(200, {newTeacher}, "balance"))
126 |
127 | })
128 |
129 |
130 | const withdrawAmount = asyncHandler(async(req,res)=>{
131 |
132 | const teacherId = req.teacher._id
133 | const amount = req.body.amount
134 |
135 | console.log(amount);
136 |
137 | const teacher = await Teacher.findById(teacherId);
138 |
139 | if (!teacher) {
140 | return res.status(404).json({ message: "Teacher not found" });
141 | }
142 |
143 | if (teacher.Balance < amount) {
144 | return res.status(400).json({ message: "Insufficient balance" });
145 | }
146 |
147 | teacher.Balance -= amount;
148 | teacher.WithdrawalHistory.push({ amount });
149 | await teacher.save();
150 |
151 | const newTeacher = await Teacher.findById(teacherId)
152 |
153 | return res
154 | .status(200)
155 | .json(new ApiResponse(200, {newTeacher}, "balance"))
156 |
157 | })
158 |
159 |
160 |
161 | export {coursePayment, getkey, coursePaymentConfirmation, teacherAmount, withdrawAmount}
--------------------------------------------------------------------------------
/frontend/src/Pages/Dashboard/StudentDashboard/SearchTeacher.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import Search from '../../Components/Searchbtn/Search'
3 |
4 | function SearchTeacher() {
5 | const [popup, SetPopup] = useState(false);
6 | return (
7 |
8 |
9 | {popup && (
10 |
11 |
12 | {/*
✖️
*/}
13 |
14 |
Student Feedback Form
15 |
Please help us improve our courses by filling out this student feedback form. We highly appreciate your involvement. Thank you!
16 |
17 |
18 | Teacher / Instructor
19 |
20 | Course Name
21 |
22 | What you like about this course?
23 |
24 |
25 |
26 |
Please rate each following statement :
27 |
28 |
55 |
56 |
61 |
62 |
63 | Submit Form
64 |
65 |
66 |
67 |
68 | )}
69 |
70 | )
71 | }
72 |
73 | export default SearchTeacher
--------------------------------------------------------------------------------
/frontend/src/Pages/Login/AdminLogin.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import "./Login.css";
3 | import Admin from './Images/Admin.svg'
4 | import { useNavigate } from "react-router-dom";
5 | import Header from '../Home/Header/Header';
6 |
7 | export default function AdminLogin() {
8 | // State to hold user input and errors
9 | const [User, setUser] = useState("");
10 | const [Password, setPassword] = useState("");
11 | const [errors, setErrors] = useState({});
12 | const [err, setErr] = useState('');
13 |
14 | const navigate = useNavigate()
15 |
16 | // Function to handle form submission
17 | const handleSubmit = async (e) => {
18 | e.preventDefault();
19 |
20 | // Client-side validation
21 | const newErrors = {};
22 |
23 | if (!User.trim()) {
24 | newErrors.User = "User Name is required";
25 | }
26 |
27 | if (!Password.trim()) {
28 | newErrors.password = "Password is required";
29 | }
30 |
31 | if (Object.keys(newErrors).length > 0) {
32 | // Update the errors state and prevent form submission
33 | setErrors(newErrors);
34 | return;
35 | }
36 |
37 | // Prepare data object to send to the backend
38 | const data = {
39 | username: User,
40 | password: Password,
41 | };
42 |
43 | try {
44 | // Send data to backend
45 | const response = await fetch(`/api/admin/login`, {
46 | method: 'POST',
47 | credentials: "include",
48 | headers: {
49 | "Content-Type": "application/json",
50 | },
51 | body: JSON.stringify(data),
52 | });
53 |
54 | const responesData = await response.json()
55 | setErr(responesData.message);
56 | const userid = responesData.data.admin._id
57 |
58 | // Handle response
59 | if (response.ok) {
60 | console.log(response);
61 |
62 | navigate(`/admin/${userid}`)
63 | } else if (response.status === 401) {
64 | // Incorrect password
65 | setErrors({ password: responesData.message || "Incorrect password" });
66 | } else if (response.status === 403) {
67 | // Account locked, disabled, or other authentication issues
68 |
69 | setErrors({ general: responesData.message || "Login failed" });
70 | } else if (response.status === 400) {
71 | setErrors({ general: responesData.message || "Admin does not exist" });
72 | } else {
73 | // Other unexpected errors
74 | setErrors({ general: "An unexpected error occurred" });
75 | }
76 | } catch (error) {
77 |
78 | setErrors(error.message);
79 | }
80 | };
81 |
82 | return (
83 | <>
84 |
85 |
86 | {/* image */}
87 |
88 |
89 |
90 |
91 |
92 |
WELCOME BACK!
93 |
94 |
95 |
96 |
Please Log Into Your Account.
97 |
98 |
99 |
100 |
101 |
102 |
setUser(e.target.value)}
108 | />
109 | {errors.User && (
110 |
{errors.User}
111 | )}
112 |
113 |
114 |
setPassword(e.target.value)}
120 | />
121 | {errors.password && (
122 |
{errors.password}
123 | )}
124 |
125 |
126 | {/* btns */}
127 |
128 |
129 | Log In
130 |
131 |
132 | {errors.general && (
133 | {errors.general}
134 | )}
135 | {err && (
136 | {err}
137 | )}
138 |
139 |
140 |
141 |
142 | >
143 | );
144 | }
145 |
--------------------------------------------------------------------------------
/frontend/src/Pages/Home/Landing/Landing.css:
--------------------------------------------------------------------------------
1 | button:hover{
2 | cursor: pointer;
3 | }
4 |
5 | .underLine{
6 | width: 10rem;
7 | margin-left: 45%;
8 | }
9 |
10 | /* === top section === */
11 | .top{
12 | /* background: linear-gradient(90.05deg, #008280 1.26%, rgba(31, 163, 161, 0.58) 99.96%); */
13 | height: 60vh;
14 | display: flex;
15 | gap: 10px;
16 | margin-top: 10vh;
17 | }
18 | .top .left{
19 | padding: 25px;
20 | color: #fff;
21 | width: 50%;
22 | padding-left: 4rem;
23 | }
24 | .top .left h1{
25 | font-size: 35px;
26 | text-align: left;
27 | }
28 | .top .right{
29 | padding: 25px;
30 | }
31 |
32 | /* === features section === */
33 | .features {
34 | background-color: #042439;
35 | }
36 | .features p{
37 | color: #9433E0;
38 | text-align: center;
39 | padding: 10px;
40 | font-family: Georgia, serif;
41 | position: relative;
42 | line-height: 2;
43 | }
44 |
45 | .fets2{
46 | height: 75%;
47 | display: flex;
48 | gap: 4rem;
49 | justify-content: center;
50 | /* align-items: end; */
51 | }
52 | .fet{
53 | background-color: #0E3A59;
54 | height: 200px;
55 | width: 300px;
56 | border-radius: 5px;
57 | display: flex;
58 | flex-direction: column;
59 | justify-content: center;
60 | align-items: center;
61 | gap: 10px;
62 | }
63 |
64 | .fet img{
65 | width: 70px;
66 | }
67 | .fet h4{
68 | color: #9433E0;
69 | }
70 | .fet p{
71 | color: #ffffff9f;
72 | font-size: 10px;
73 | }
74 |
75 | /* === About === */
76 |
77 | .about h4{
78 | color: #fff;
79 | padding: 1rem;
80 | text-align: center;
81 | font-size: 1.1rem;
82 | }
83 |
84 | .about p{
85 | color: #fff;
86 | padding: 50px;
87 | line-height: 2rem;
88 | }
89 |
90 | .content{
91 | padding: 0 5rem;
92 | display: flex;
93 | align-items: center;
94 | justify-content: space-around; /* Apply for all */
95 | gap: 5rem;
96 | }
97 |
98 | /* === Subjects sectuon === */
99 |
100 | .courses{
101 | margin: 5.5rem 0;
102 | }
103 | .courses p{
104 | color: #042439;
105 | font-size: larger;
106 | font-weight: 700;
107 | text-align: center;
108 | }
109 | .subjects{
110 | display: flex;
111 | gap: 2.5rem;
112 | margin: 4rem;
113 | align-items: center;
114 | justify-content: center;
115 | }
116 |
117 | .subject{
118 | display: flex;
119 | flex-direction: column;
120 | align-items: center;
121 | justify-content: center;
122 | gap: 0.8rem;
123 | background-color: #042439;
124 | width: 150px;
125 | height: 130px;
126 | border-radius: 5px;
127 | cursor: pointer;
128 | }
129 |
130 | .subject p{
131 | color: #fff;
132 | font-size: medium;
133 | }
134 |
135 | .subject img{
136 | width: 70px;
137 | }
138 |
139 | /* === Contact Us === */
140 |
141 | .contact-us{
142 | margin-top: -5rem;
143 | }
144 |
145 | .contact h4{
146 | text-align: center;
147 | margin: 1rem;
148 | font-size: 1.1rem;
149 | }
150 |
151 | .contact .form-submit{
152 | display: flex;
153 | flex-direction: column;
154 | align-items: center;
155 | gap: 2rem;
156 | }
157 |
158 | .form-submit input{
159 | width: 300px;
160 | padding: 1rem;
161 | border-radius: 5px;
162 | border: none;
163 | background-color: #042439;
164 | color: #fff;
165 | }
166 |
167 | .textArea{
168 | height: 200px;
169 | width: 300px;
170 | resize: none;
171 | padding: 1rem;
172 | border-radius: 5px;
173 | background-color: #042439;
174 | color: #fff;
175 | line-height: 1.5rem;
176 | }
177 |
178 | .Submit-btn{
179 | width: 333px;
180 | background-color: #1021ba;
181 | margin-bottom: 1rem;
182 | }
183 |
184 | /* === Footer === */
185 | .footer{
186 | height: 230px;
187 | background-color: #042439;
188 | display: flex;
189 | justify-content: space-between;
190 | align-items: center;
191 | padding: 0 5rem;
192 | }
193 |
194 | .footer .leftPart{
195 | display: flex;
196 | flex-direction: column;
197 | gap: 1rem;
198 | color: #fff;
199 | }
200 |
201 | .footer .leftPart .title{
202 | display: flex;
203 | align-items: center;
204 | color: #fff;
205 | gap: 1rem;
206 | }
207 |
208 | .rightPart .links{
209 | width: 25rem;
210 | display: flex;
211 | flex-wrap: wrap;
212 | gap: 1.5rem;
213 | color: #fff;
214 | }
215 |
216 | .rightPart .links .link{
217 | display: flex;
218 | gap: 0.7rem;
219 | width: 7rem;
220 | }
221 |
222 | .rightPart .links .link :hover{
223 | color: #9433E0;
224 | }
225 |
226 | .hr{
227 | margin: 2rem 0;
228 | }
229 |
230 | .icons{
231 | margin-left: 15rem;
232 | display: flex;
233 | gap: 1rem;
234 | }
--------------------------------------------------------------------------------
/frontend/src/Pages/Images/Plant.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/frontend/src/Pages/Dashboard/TeacherDashboard/TeacherClasses.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from 'react';
2 | import Camera from '../Images/Camera.png';
3 | import Clock from '../Images/Clock.png';
4 | import AddClass from './AddClass';
5 | import { NavLink, useParams } from 'react-router-dom';
6 |
7 | function TeacherClasses() {
8 | const [showPopup, setShowPopup] = useState(false);
9 | const { ID } = useParams();
10 | const [data, setData] = useState([]);
11 | const [error, setError] = useState('');
12 |
13 | useEffect(() => {
14 | const getData = async () => {
15 | try {
16 | const response = await fetch(`/api/course/classes/teacher/${ID}`, {
17 | method: 'GET',
18 | headers: {
19 | 'Content-Type': 'application/json',
20 | },
21 | });
22 | if (!response.ok) {
23 | throw new Error('Failed to fetch data');
24 | }
25 |
26 | const user = await response.json();
27 | setData(user.data.classes[0]?.liveClasses || []);
28 | console.log(user.data);
29 |
30 | } catch (error) {
31 | setError(error.message);
32 | }
33 | };
34 | getData();
35 |
36 | }, [showPopup, ID]);
37 |
38 | if (error) {
39 | return Error: {error}
;
40 | }
41 |
42 | return (
43 |
44 |
Weekly Schedule
45 |
46 |
47 | {data.filter(clas => {
48 | const classDate = new Date(clas.date.slice(0, 10));
49 | const today = new Date();
50 | const oneWeekFromNow = new Date();
51 | oneWeekFromNow.setDate(today.getDate() + 7);
52 |
53 | return classDate >= today && classDate <= oneWeekFromNow;
54 | }).map(clas => (
55 |
56 |
57 |
58 |
59 | {clas.coursename}
60 |
61 | {clas.date.slice(0, 10)} {Math.floor(clas.timing / 60)}:{clas.timing % 60 === 0 ? "00" : clas.timing % 60}
62 |
63 |
64 |
{clas.title.slice(0, 35)} ...
65 |
66 |
{clas.status}
67 |
68 | ))}
69 |
70 |
71 |
72 | {data.length > 0 && (
73 |
74 |
75 |
76 |
77 |
{typeof data[0]?.date === 'string' ? data[0]?.date.slice(0,10) : ''}
78 |
79 | {typeof data[0]?.timing === 'number' ? `${Math.floor(data[0]?.timing / 60)}:${data[0]?.timing % 60 === 0 ?"00":data[0]?.timing % 60}` :''}
80 |
81 |
82 |
83 |
84 |
Your next Class
85 |
{data[0]?.coursename.toUpperCase()}
86 |
{data[0]?.title.slice(0, 25)} ...
87 |
88 |
89 |
90 |
91 |
92 | )}
93 |
94 |
setShowPopup(true)} className='absolute right-10 bg-blue-900 p-2 rounded-sm cursor-pointer'>
95 | + ADD CLASS
96 |
97 | {showPopup && (
98 |
setShowPopup(false)} />
99 | )}
100 |
101 | );
102 | }
103 |
104 | export default TeacherClasses;
105 |
--------------------------------------------------------------------------------
/backend/src/models/teacher.model.js:
--------------------------------------------------------------------------------
1 | import mongoose from "mongoose";
2 | import jwt from "jsonwebtoken";
3 | import bcrypt from "bcrypt";
4 | import crypto from "crypto"
5 |
6 | const teacherSchema = new mongoose.Schema({
7 |
8 | Email:{
9 | type:String,
10 | required:true,
11 | unique:true,
12 | trim:true,
13 | lowercase:true,
14 | index:true,
15 | },
16 |
17 | Firstname:{
18 | type:String,
19 | required:true,
20 | trim:true,
21 |
22 | },
23 |
24 | Lastname:{
25 | type:String,
26 | required:true,
27 | trim:true,
28 | },
29 |
30 | Password:{
31 | type:String,
32 | required: true,
33 | },
34 |
35 | forgetPasswordToken: String,
36 |
37 |
38 | forgetPasswordExpiry: Date,
39 |
40 |
41 | Isverified: {
42 | type:Boolean,
43 | default:false,
44 | },
45 |
46 | Isapproved:{
47 | type: String,
48 | enum: ['approved', 'rejected', 'pending', 'reupload'],
49 | default: 'pending',
50 | },
51 |
52 | Remarks:{
53 | type:String
54 | },
55 |
56 | Refreshtoken:{
57 | type:String,
58 | },
59 |
60 | Teacherdetails:{
61 | type:mongoose.Schema.Types.ObjectId,
62 | ref:"Teacherdocs"
63 | },
64 |
65 | Balance: {
66 | type: Number,
67 | default: 0,
68 | },
69 |
70 | WithdrawalHistory: [{
71 | amount: {
72 | type: Number,
73 | required: true,
74 | },
75 | date: {
76 | type: Date,
77 | default: Date.now,
78 | }
79 | }],
80 |
81 |
82 | enrolledStudent: [{
83 | studentId: {
84 | type: mongoose.Schema.Types.ObjectId,
85 | ref: 'student'
86 | },
87 | isNewEnrolled: {
88 | type: Boolean,
89 | default: true
90 | }
91 | }]
92 |
93 | },
94 |
95 |
96 | {
97 | timestamps:true,
98 | }
99 | )
100 |
101 | teacherSchema.pre("save", async function(next) {
102 | if(this.isModified('Firstname') || this.isNew){
103 | this.Firstname = this.Firstname.charAt(0).toUpperCase() + this.Firstname.slice(1).toLowerCase();
104 | }
105 |
106 | if(this.isModified('Lastname') || this.isNew){
107 | this.Lastname = this.Lastname.charAt(0).toUpperCase() + this.Lastname.slice(1).toLowerCase();
108 | }
109 |
110 | next()
111 | })
112 |
113 | teacherSchema.pre("save", async function (next) {
114 | if(!this.isModified("Password")) return next();
115 | this.Password = await bcrypt.hash(this.Password, 10)
116 | next()
117 | })
118 |
119 | teacherSchema.methods.isPasswordCorrect = async function (Password){
120 | return await bcrypt.compare(Password, this.Password)
121 | }
122 |
123 | teacherSchema.methods.generateAccessToken = function(){
124 | return jwt.sign({
125 | _id:this._id,
126 | Email:this.Email,
127 | },
128 | process.env.ACCESS_TOKEN_SECRET,{
129 | expiresIn:process.env.ACCESS_TOKEN_EXPIRY
130 | })
131 | }
132 |
133 | teacherSchema.methods.generateRefreshToken = function(){
134 | return jwt.sign({
135 | _id:this._id,
136 | Email:this.Email,
137 | },
138 | process.env.REFRESH_TOKEN_SECRET,{
139 | expiresIn:process.env.REFRESH_TOKEN_EXPIRY
140 | })
141 | }
142 |
143 |
144 | teacherSchema.methods.generateResetToken =async function(){
145 |
146 | const reset=crypto.randomBytes(20).toString('hex') ;
147 |
148 | this.forgetPasswordToken=crypto.createHash('sha256').update(reset).digest('hex') ;
149 |
150 | this.forgetPasswordExpiry=Date.now() + 15 * 60 * 1000 ;
151 |
152 | await this.save() ;
153 |
154 | }
155 |
156 |
157 | const TeacherDetailsSchema = new mongoose.Schema({
158 | Phone:{
159 | type:Number,
160 | required: true,
161 | trim:true,
162 | unique:true,
163 | },
164 |
165 | Address:{
166 | type:String,
167 | required:true,
168 | },
169 |
170 | Experience:{
171 | type:Number,
172 | required:true,
173 | },
174 |
175 | SecondarySchool:{
176 | type:String,
177 | required:true,
178 | },
179 |
180 | HigherSchool:{
181 | type:String,
182 | required:true,
183 | },
184 |
185 | UGcollege:{
186 | type:String,
187 | required:true,
188 | },
189 |
190 | PGcollege:{
191 | type:String,
192 | required:true,
193 | },
194 |
195 | SecondaryMarks:{
196 | type:Number,
197 | required:true,
198 | },
199 |
200 | HigherMarks:{
201 | type:Number,
202 | required:true,
203 | },
204 |
205 | UGmarks:{
206 | type:Number,
207 | required:true,
208 | },
209 |
210 | PGmarks:{
211 | type:Number,
212 | required:true,
213 | },
214 |
215 | Aadhaar:{
216 | type:String,
217 | required:true,
218 | },
219 |
220 | Secondary:{
221 | type:String,
222 | required:true,
223 | },
224 |
225 | Higher:{
226 | type:String,
227 | required:true,
228 | },
229 |
230 | UG:{
231 | type:String,
232 | required:true,
233 | },
234 |
235 | PG:{
236 | type:String,
237 | required:true,
238 | }
239 |
240 | }, {
241 | timestamps:true,
242 | })
243 |
244 |
245 | const Teacher = mongoose.model("teacher",teacherSchema)
246 |
247 | const Teacherdocs = mongoose.model("teacherdocs", TeacherDetailsSchema)
248 |
249 | export {Teacher, Teacherdocs}
250 |
--------------------------------------------------------------------------------
/frontend/src/Pages/Home/Search/Search.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from 'react'
2 | import { NavLink, useParams } from 'react-router-dom';
3 | import Header from '../Header/Header'
4 |
5 | function search() {
6 | const { subject } = useParams();
7 | const [data, setData] = useState(subject);
8 | const [course, setCourse] = useState([]);
9 | const [openTM, setOpenTM] = useState(false);
10 | const [Tdec, setTeacherDetails] = useState(null);
11 | const [tname, setTname] = useState({});
12 | const [starCount, setStarCount] = useState(5);
13 |
14 | const price = {
15 | math: 700,
16 | physics: 800,
17 | computer: 1000,
18 | chemistry: 600,
19 | biology: 500,
20 | };
21 |
22 | const daysName = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
23 |
24 | let SearchTeacher = async()=>{
25 | let Subject = data.toLowerCase();
26 | let Data = await fetch(`/api/course/${Subject}`)
27 | let response = await Data.json();
28 | if(response.statusCode == 200){
29 | setCourse(response.data)
30 | // console.log(response.data.length)
31 | }
32 | setData('')
33 | }
34 |
35 | useEffect(()=>{
36 | SearchTeacher();
37 | },[])
38 |
39 | const openTeacherDec = async(id,fname,lname,sub)=>{
40 | setTname({fname,lname,sub});
41 |
42 | const data = await fetch('/api/teacher/teacherdocuments',{
43 | method: 'POST',
44 | credentials: "include",
45 | headers: {
46 | "Content-Type": "application/json",
47 | },
48 | body: JSON.stringify({teacherID : id}),
49 | })
50 |
51 | const res = await data.json();
52 | // console.log(res.data);
53 | setTeacherDetails(res.data);
54 | setOpenTM(true);
55 | }
56 |
57 |
58 | return (
59 | <>
60 |
61 |
62 |
63 |
64 |
setData(e.target.value)}/>
65 |
Find Teacher
66 |
67 |
68 | { course && (
69 | course.map((Data)=>(
70 |
71 |
72 | {Data.coursename.toUpperCase()}
73 |
74 |
openTeacherDec(Data.enrolledteacher.Teacherdetails, Data.enrolledteacher.Firstname, Data.enrolledteacher.Lastname, Data.coursename)} className='text-gray-300 cursor-pointer font-bold'>{Data.enrolledteacher.Firstname} {Data.enrolledteacher.Lastname}
75 |
Desc : {Data.description}
76 |
77 |
{Data.enrolledStudent.length}/20
78 |
79 |
80 |
alert('Pls login to enroll it')} className='text-white bg-blue-900 py-2 px-3 cursor-not-allowed'>Enroll Now
81 |
82 |
83 | Timing :
84 | {'[ '}
85 | {Data.schedule.map(daytime => {
86 | return `${daysName[daytime.day]} ${Math.floor(daytime.starttime / 60)}:${daytime.starttime % 60 === 0 ? "00" : daytime.starttime % 60} - ${Math.floor(daytime.endtime/60)}:${daytime.endtime % 60 === 0 ? "00" : daytime.endtime % 60}`;
87 | }).join(', ')}
88 | {' ]'}
89 |
90 |
91 | ))
92 | )}
93 |
94 | {openTM && (
95 |
96 |
97 |
setOpenTM(false)}>✖️
98 |
99 |
{tname.sub.toUpperCase()}
100 |
Teacher Name : {tname.fname} {tname.lname}
101 | {/*
Teacher Name : {tname.fname} {tname.lname} {'⭐'.repeat(starCount)}
*/}
102 |
Education : Postgraduate from {Tdec.PGcollege} with {Tdec.PGmarks} CGPA
103 |
Experience : {Tdec.Experience} years
104 |
Course Name : {tname.sub.toUpperCase()}
105 | {/*
Course Duration : 6 Months
*/}
106 | {/*
Fees : Rs. {price[tname.sub]}
*/}
107 |
108 |
109 |
110 | )}
111 |
112 | >
113 | )
114 | }
115 |
116 | export default search
--------------------------------------------------------------------------------
/frontend/src/Pages/Dashboard/TeacherDashboard/AddClass.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import { useParams } from 'react-router-dom';
3 | import DateTime from './DateTime';
4 |
5 | function AddClass({ onClose }) {
6 | const { ID } = useParams();
7 | const [courses, setCourses] = useState([]);
8 | const [error, setError] = useState([]);
9 | const [date, setDate] = useState("");
10 | const [link, setLink] = useState("");
11 | const [note, setNote] = useState("");
12 | const [CourseId, setCourseId] = useState('');
13 | const [allowedDays, setCurrData] = useState([]);
14 |
15 | const DAY = [
16 | "Sunday",
17 | "Monday",
18 | "Tuesday",
19 | "Wednesday",
20 | "Thursday",
21 | "Friday",
22 | "Saturday"
23 | ];
24 |
25 | function setToMidnight(dateTimeString) {
26 | // Create a new Date object from the input string
27 | let date = new Date(dateTimeString);
28 |
29 | // Extract the time part
30 | let hours = date.getUTCHours();
31 | let minutes = date.getUTCMinutes();
32 | let seconds = date.getUTCSeconds();
33 |
34 | let totalMinutes = (hours * 60) + minutes;
35 | date.setUTCHours(0, 0, 0, 0);
36 | let modifiedDateTimeString = date.toISOString();
37 |
38 | const DATETIME = [totalMinutes, modifiedDateTimeString];
39 |
40 | return DATETIME;
41 | }
42 |
43 | useEffect(() => {
44 | const getCourses = async () => {
45 | try {
46 | const response = await fetch(`/api/course/Teacher/${ID}/enrolled`, {
47 | method: 'GET',
48 | headers: {
49 | 'Content-Type': 'application/json',
50 | },
51 | });
52 |
53 | // console.log(response);
54 |
55 | if (!response.ok) {
56 | throw new Error('Failed to fetch data');
57 | }
58 |
59 | const res = await response.json();
60 |
61 | // console.log(res.data);
62 | setCourses(res.data);
63 | setCourseId(res.data[0]._id);
64 | } catch (error) {
65 | setError(error.message);
66 | }
67 | };
68 | getCourses();
69 | }, [ID]);
70 |
71 | useEffect(() => {
72 | const filteredData = courses.filter(course => course._id === CourseId);
73 | setCurrData(filteredData[0]?.schedule);
74 | // console.log("output:", filteredData[0]?.schedule);
75 | }, [CourseId]);
76 |
77 |
78 | const addCourses = async () => {
79 | const currentDate = new Date();
80 | const givenDate = new Date(date);
81 |
82 | const modifyDate = setToMidnight(date);
83 |
84 | const data = {
85 | title: note,
86 | timing: modifyDate[0],
87 | date: modifyDate[1],
88 | link: link,
89 | status: 'upcoming',
90 | };
91 |
92 | // console.log("add classes",data)
93 |
94 |
95 | if (currentDate > givenDate) {
96 | alert('choose a valid Date!');
97 | } else if (note === '' || date === '' || link === '') {
98 | alert('All fields are required!');
99 | } else {
100 | try {
101 | const response = await fetch(`/api/course/${CourseId}/teacher/${ID}/add-class`, {
102 | method: 'POST',
103 | headers: {
104 | 'Content-Type': 'application/json',
105 | },
106 | body: JSON.stringify(data),
107 | });
108 |
109 | const res = await response.json();
110 | alert(res.message);
111 |
112 | if (!response.ok) {
113 | throw new Error('Failed to fetch data');
114 | }
115 |
116 |
117 |
118 |
119 | if (res.statusCode === 200) {
120 | onClose();
121 | }
122 | } catch (error) {
123 | setError(error.message);
124 | }
125 | }
126 | };
127 |
128 | return (
129 |
130 |
131 |
✖️
132 |
133 |
134 |
Create next class
135 |
setCourseId(e.target.value)} className='text-gray-900 rounded-md w-28 px-2 border-0 outline-0'>
136 | {courses && (
137 | courses.filter((course) => course.isapproved)
138 | .map((course) => (
139 | {course.coursename.toUpperCase()} {'['} {course.schedule.map(day => DAY[day.day]).join(', ')} {']'}
140 | ))
141 | )}
142 |
143 |
144 |
145 |
146 |
147 |
148 | Date & Time:
149 |
150 |
151 |
152 |
153 |
164 |
165 |
168 |
169 |
170 | );
171 | }
172 |
173 | export default AddClass;
174 |
--------------------------------------------------------------------------------
/frontend/src/Pages/Signup/Signup.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import "./Styles.css";
3 | import { NavLink, useNavigate } from "react-router-dom";
4 | import Images from "../Images/Grammar-correction.svg";
5 | import Radiobtn from "../Components/RadioBtn/Radiobtn";
6 | import Header from "../Home/Header/Header";
7 |
8 | const Signup = () => {
9 | // State to hold user input and errors
10 | const [Firstname, setFirstName] = useState("");
11 | const [Lastname, setLastName] = useState("");
12 | const [Email, setEmail] = useState("");
13 | const [Password, setPassword] = useState("");
14 | const [errors, setErrors] = useState({});
15 | const [userType, setUserType] = useState('');
16 | const [err, setErr] = useState('');
17 |
18 | const navigate = useNavigate()
19 |
20 |
21 | // Function to handle form submission
22 | const handleSubmit = async (e) => {
23 | e.preventDefault();
24 |
25 | // Client-side validation
26 | const newErrors = {};
27 |
28 | if (!Firstname.trim()) {
29 | newErrors.firstname = 'First name is required';
30 | }
31 |
32 | if (!Lastname.trim()) {
33 | newErrors.lastname = 'Last name is required';
34 | }
35 |
36 | if (!Email.trim()) {
37 | newErrors.email = 'Email is required';
38 | } else if (!/\S+@\S+\.\S+/.test(Email)) {
39 | newErrors.email = 'Invalid email format';
40 | }
41 |
42 | const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;
43 |
44 | if (!passwordRegex.test(Password)) {
45 | newErrors.password = 'Password must be at least 8 characters long and include an uppercase letter, a lowercase letter, a number, and a special character.';
46 | }
47 |
48 | if (Object.keys(newErrors).length > 0) {
49 | // Update the errors state and prevent form submission
50 | setErrors(newErrors);
51 | return;
52 | }
53 |
54 | // Prepare data object to send to the backend
55 | const data = {
56 | Firstname: Firstname,
57 | Lastname: Lastname,
58 | Email: Email,
59 | Password: Password,
60 | };
61 |
62 | try {
63 | // Send data to backend (you need to implement this part)
64 | const response = await fetch(`/api/${userType}/signup`, {
65 | method: "POST",
66 | mode: "cors",
67 | credentials: "include",
68 | headers: {
69 | "Content-Type": "application/json",
70 | },
71 | body: JSON.stringify(data),
72 | });
73 |
74 | // Handle response
75 | const responseData = await response.json();
76 |
77 | setErr(responseData.message);
78 |
79 | if (response.ok) {
80 | // Registration successful, you can redirect or do something else
81 | console.log("Registration successful");
82 | navigate('/varifyEmail');
83 | } else if (response.status === 400) {
84 | // Handle specific validation errors returned by the server
85 | setErrors(responseData.errors || {});
86 | } else {
87 | // Other status codes (e.g., 500 Internal Server Error)
88 | console.error("Registration failed with status code:", response.status);
89 | }
90 | } catch (error) {
91 | setErrors(error.message);
92 |
93 | }
94 | };
95 |
96 | return (
97 | <>
98 |
99 |
100 |
101 | {/*
102 |
103 |
logo
104 |
*/}
105 |
106 |
WELCOME
107 | join us today !!
108 |
109 |
110 |
176 |
177 |
178 |
179 |
180 |
181 |
182 | * Password must be at least 8 characters long and include an uppercase letter, a lowercase letter, a number, and a special character.
183 | >
184 | );
185 | };
186 |
187 | export default Signup;
188 |
--------------------------------------------------------------------------------
/frontend/src/Pages/Login/Login.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import HR from "../Login/Images/HR.svg";
3 | import "./Login.css";
4 | import { NavLink, useNavigate } from "react-router-dom";
5 | import Radiobtn from "../Components/RadioBtn/Radiobtn";
6 | import Header from "../Home/Header/Header";
7 |
8 | export default function Login() {
9 | // State to hold user input and errors
10 | const [Email, setEmail] = useState("");
11 | const [Password, setPassword] = useState("");
12 | const [errors, setErrors] = useState({});
13 | const [userType, setUserType] = useState('');
14 | const [err, setErr] = useState('');
15 |
16 |
17 | const navigate=useNavigate()
18 |
19 | // Function to handle form submission
20 | const handleSubmit = async (e) => {
21 | e.preventDefault();
22 |
23 | // Client-side validation
24 | const newErrors = {};
25 |
26 | if (!Email.trim()) {
27 | newErrors.email = "Email is required";
28 | } else if (!/\S+@\S+\.\S+/.test(Email)) {
29 | newErrors.email = "Invalid email format";
30 | }
31 |
32 | if (!Password.trim()) {
33 | newErrors.password = "Password is required";
34 | }
35 |
36 | if (Object.keys(newErrors).length > 0) {
37 | // Update the errors state and prevent form submission
38 | setErrors(newErrors);
39 | return;
40 | }
41 |
42 | // Prepare data object to send to the backend
43 | const data = {
44 | Email: Email,
45 | Password: Password,
46 | };
47 |
48 | try {
49 | // Send data to backend (you need to implement this part)
50 | const response = await fetch(`/api/${userType}/login`, {
51 | method: 'POST',
52 | credentials: "include",
53 | headers: {
54 | "Content-Type": "application/json",
55 | },
56 | body: JSON.stringify(data),
57 | });
58 |
59 | const responesData = await response.json()
60 | if(responesData.message != 'Logged in'){
61 | setErr(responesData.message);
62 | }
63 | const userid = responesData.data.user._id
64 |
65 | // Handle response
66 | if (response.ok) {
67 | // Authentication successful, you can redirect or do something else
68 | console.log("Login successful");
69 | console.log(responesData.data.user.Isapproved);
70 |
71 |
72 | if(responesData.data.user.Isapproved === "pending"){
73 | if(responesData.data.user.Teacherdetails || responesData.data.user.Studentdetails){
74 | navigate('/pending')
75 | }else{
76 | if(userType === 'student'){
77 | navigate(`/StudentDocument/${userid}`)
78 | }else if(userType === 'teacher'){
79 | navigate(`/TeacherDocument/${userid}`)
80 | }
81 | }
82 | }else if(responesData.data.user.Isapproved === "approved"){
83 | if(userType === 'student'){
84 | navigate(`/Student/Dashboard/${userid}/Search`)
85 | }else if(userType === 'teacher'){
86 | navigate(`/Teacher/Dashboard/${userid}/Home`)
87 | }
88 | }else if(responesData.data.user.Isapproved === "reupload"){
89 | if(userType === 'teacher'){
90 | navigate(`/rejected/${userType}/${userid}`)
91 | }else{
92 | navigate(`/rejected/${userType}/${userid}`)
93 | }
94 | }else{
95 | setErr('You are ban from our platform!');
96 | }
97 |
98 | } else if (response.status === 401) {
99 | // Incorrect password
100 | setErrors({ password: responesData.message || "Incorrect password" });
101 | } else if (response.status === 403) {
102 | // Account locked, disabled, or other authentication issues
103 |
104 | setErrors({ general: responesData.message || "Login failed" });
105 | } else if (response.status === 400) {
106 | setErrors({ general: responesData.message || "User does not exist" });
107 | } else if (response.status === 422) {
108 | setErrors({
109 | general: responesData.message || '"Email" must be a valid email',
110 | });
111 | } else {
112 | // Other unexpected errors
113 | setErrors({ general: "An unexpected error occurred" });
114 | }
115 | } catch (error) {
116 |
117 | setErrors(error.message);
118 | }
119 | };
120 |
121 | return (
122 | <>
123 |
124 |
125 |
126 | {/*
127 |
128 |
Logo
129 |
*/}
130 | {/* headings */}
131 |
132 |
WELCOME BACK!
133 |
134 |
135 |
136 |
Please Log Into Your Account.
137 |
138 |
139 |
140 |
141 |
142 |
setEmail(e.target.value)}
148 | />
149 | {errors.email && (
150 |
{errors.email}
151 | )}
152 |
153 |
154 |
setPassword(e.target.value)}
160 | />
161 | {errors.password && (
162 |
{errors.password}
163 | )}
164 |
165 |
166 | {/* radio buttons */}
167 |
168 |
169 |
170 |
171 |
172 | Don't have an account?
173 |
174 | signup
175 |
176 |
177 |
178 | navigate('/forgetpassword')} >
179 | Forget Password?
180 |
181 |
182 | {/* btns */}
183 |
184 |
185 | Log In
186 |
187 |
188 | {err != '' && (
189 | {err}
190 | )}
191 | {/* {errors.general && (
192 | {errors.general}
193 | )} */}
194 |
195 |
196 |
197 |
198 | {/* image */}
199 |
200 |
201 |
202 |
203 | >
204 | );
205 |
206 | }
207 |
--------------------------------------------------------------------------------
/frontend/src/Pages/Components/DocumentVerification/StudentDocument.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import Input from "../DocumentVerification/InputComponent/Input.jsx";
3 | import InputUpload from "../DocumentVerification/Inputupload/InputUpload.jsx";
4 | import { useNavigate, useParams } from "react-router-dom";
5 | import { RotatingLines } from "react-loader-spinner";
6 | import logo from "../../Images/logo.svg";
7 |
8 | const StudentDocument = () => {
9 | const [data, setdata] = useState([]);
10 | const [error, setError] = useState("");
11 | const { Data } = useParams();
12 | const navigate = useNavigate();
13 | const [loader, setLoader] = useState(false);
14 |
15 | useEffect(() => {
16 | const getData = async () => {
17 | try {
18 | const response = await fetch(`/api/student/StudentDocument/${Data}`, {
19 | method: "GET",
20 | headers: {
21 | "Content-Type": "application/json",
22 | },
23 | });
24 |
25 | if (!response.ok) {
26 | throw new Error("Failed to fetch data");
27 | }
28 |
29 | const user = await response.json();
30 | setdata(user.data);
31 | } catch (error) {
32 | setError(error.message);
33 | }
34 | };
35 |
36 | getData();
37 | }, []);
38 |
39 | const [formData, setFormData] = useState({
40 | Phone: data.Phone || "",
41 | Address: data.Address || "",
42 | Highesteducation: data.Highesteducation || "",
43 | SecondarySchool: data.SecondarySchool || "",
44 | HigherSchool: data.HigherSchool || "",
45 | SecondaryMarks: data.SecondaryMarks || "",
46 | HigherMarks: data.HigherMarks || "",
47 | Aadhaar: null,
48 | Secondary: null,
49 | Higher: null,
50 | });
51 |
52 | const handleFileChange = (fileType, e) => {
53 | setFormData({
54 | ...formData,
55 | [fileType]: e.target.files[0],
56 | });
57 | };
58 |
59 | const handleInputChange = (field, value) => {
60 | setFormData({
61 | ...formData,
62 | [field]: value,
63 | });
64 | };
65 |
66 | const handleSubmit = async (e) => {
67 | e.preventDefault();
68 | setLoader(true);
69 |
70 | const formDataObj = new FormData();
71 |
72 | Object.keys(formData).forEach((key) => {
73 | formDataObj.append(key, formData[key]);
74 | });
75 |
76 | try {
77 | const response = await fetch(`/api/student/verification/${Data}`, {
78 | method: "POST",
79 | body: formDataObj,
80 | });
81 |
82 | const responseData = await response.json();
83 | console.log("response", responseData);
84 |
85 | setLoader(false);
86 | if (!response.ok) {
87 | setError(responseData.message);
88 | } else {
89 | console.log("Form submitted successfully!");
90 | navigate("/pending");
91 | }
92 | } catch (e) {
93 | console.error("Error:", e);
94 | }
95 | };
96 |
97 | return (
98 | <>
99 | {loader && (
100 |
101 | {" "}
112 | Uploading ...
113 |
114 | )}
115 |
116 |
117 |
118 |
Shiksharthee
119 |
120 |
Document Verification (Student)
121 |
122 |
123 |
124 | Personal Information
125 |
126 |
132 |
138 | handleInputChange("Phone", e.target.value)}
143 | />
144 |
145 |
146 |
147 | handleInputChange("Address", e.target.value)}
152 | />
153 |
158 | handleInputChange("Highesteducation", e.target.value)
159 | }
160 | />
161 | handleFileChange("Aadhaar", e)}
166 | />
167 |
168 |
169 |
170 | Educational Information
171 |
172 |
173 |
174 |
177 |
181 | handleInputChange("SecondarySchool", e.target.value)
182 | }
183 | />
184 |
188 | handleInputChange("SecondaryMarks", e.target.value)
189 | }
190 | />
191 |
192 | handleFileChange("Secondary", e)}
196 | />
197 |
198 |
199 |
200 |
201 |
202 |
Higher Secondary
203 |
204 |
208 | handleInputChange("HigherSchool", e.target.value)
209 | }
210 | />
211 |
handleInputChange("HigherMarks", e.target.value)}
215 | />
216 |
217 | handleFileChange("Higher", e)}
221 | />
222 |
223 |
224 |
225 | {error && !! {error}
}
226 |
227 |
228 | Submit ▶️
229 |
230 |
231 |
232 | >
233 | );
234 | };
235 |
236 | export default StudentDocument;
237 |
--------------------------------------------------------------------------------
/frontend/src/Pages/Components/Admin/Course.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import axios from 'axios';
3 | import { IoIosNotificationsOutline } from "react-icons/io";
4 | import { NavLink, useNavigate, useParams } from "react-router-dom";
5 | import logo from '../../Images/logo.svg'
6 |
7 | const Course = () => {
8 | const [courseReq, setCourseReq] = useState([]);
9 |
10 | const { data } = useParams();
11 | const navigator = useNavigate();
12 |
13 |
14 | // useEffect((data)=>{
15 | // const Postrequest=async()=>{
16 | // try{
17 | // const response=await axios.post(`api/admin/${data}/approve/student/:studentID`)
18 | // // console.log(response);
19 | // }catch(error){
20 | // console.error('Error fetching course requests:', error);
21 | // }
22 | // }
23 | // Postrequest();
24 | // },[])
25 |
26 |
27 |
28 |
29 |
30 |
31 | const formatDay = (day) => {
32 | const daysOfWeek = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
33 | return daysOfWeek[day];
34 | };
35 |
36 | const formatTime = (time) => {
37 | const hours = Math.floor(time / 60);
38 | const minutes = time % 60;
39 |
40 | return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}`;
41 | };
42 |
43 |
44 | useEffect(() => {
45 | const fetchCourseRequests = async () => {
46 | try {
47 | const response = await axios.get(`/api/admin/${data}/approve/course`);
48 | console.log("dtat",response.data.data);
49 | setCourseReq(response.data.data);
50 | } catch (error) {
51 | console.error('Error fetching course requests:', error);
52 | }
53 | };
54 |
55 | fetchCourseRequests();
56 | }, [data]);
57 |
58 |
59 | // const handleAccept = async (id,info) => {
60 | // console.log(id);
61 | // try {
62 | // const response = await fetch(`/api/admin/${data}/approve/course/${id}`, {
63 | // method: 'POST',
64 | // headers: {
65 | // "Content-Type": "application/json",
66 | // },
67 | // body: JSON.stringify({
68 | // Isapproved: true,
69 | // Email:info.Email,
70 | // Firstname:info.enrolledteacher,
71 | // }),
72 | // });
73 |
74 | // console.log(response);
75 |
76 | // if (response.ok) {
77 | // setCourseReq(courseReq.filter(req => req._id !== id));
78 |
79 | // }
80 | // } catch (error) {
81 | // console.error('Error approving course request:', error);
82 | // }
83 | // };
84 | const handleAccept = async (id, info) => {
85 | console.log(id);
86 | console.log(info.Email)
87 | try {
88 | const response = await axios.post(`/api/admin/${data}/approve/course/${id}`, {
89 | Isapproved: true,
90 | email: info.Email,
91 | Firstname: info.enrolledteacher,
92 | }, {
93 | headers: {
94 | "Content-Type": "application/json",
95 | },
96 | });
97 |
98 | console.log(response);
99 |
100 | if (response.status === 200) {
101 | setCourseReq(courseReq.filter(req => req._id !== id));
102 | alert(response.data.message);
103 | }
104 | } catch (error) {
105 | console.error('Error approving course request:', error);
106 | }
107 | };
108 |
109 |
110 | const handleReject = async (id, info) => {
111 | console.log(id, info);
112 | try {
113 | const response = await axios.post(`/api/admin/${data}/approve/course/${id}`, {
114 | Isapproved: false,
115 | email: info.Email,
116 | Firstname: info.enrolledteacher,
117 | }, {
118 | headers: {
119 | "Content-Type": "application/json",
120 | },
121 | });
122 |
123 | console.log(response);
124 |
125 | if (response.status === 200) {
126 | setCourseReq(courseReq.filter(req => req._id !== id));
127 | alert(response.data.message);
128 | }
129 | } catch (error) {
130 | console.error('Error rejecting course request:', error);
131 | }
132 | };
133 |
134 | // const handleReject = async (id,info) => {
135 | // console.log(id,info);
136 | // try {
137 | // const response = await fetch(`/api/admin/${data}/approve/course/${id}`, {
138 | // method: 'POST',
139 | // headers: {
140 | // "Content-Type": "application/json",
141 | // },
142 | // body: JSON.stringify({ Isapproved: false,
143 | // Email:info.Email,
144 | // Firstname:info.enrolledteacher,
145 | // }),
146 | // });
147 | // console.log(response);
148 | // if (response.ok) {
149 | // setCourseReq(courseReq.filter(req => req._id !== id));
150 | // }
151 | // } catch (error) {
152 | // console.error('Error rejecting course request:', error);
153 | // }
154 | // };
155 |
156 |
157 |
158 |
159 |
160 | return (
161 |
162 | {/* Navbar */}
163 |
164 |
165 |
166 |
167 |
navigator(`/admin/${data}`)} className="text-lg sm:text-xl md:text-2xl lg:text-3xl text-blue-700 font-bold font-mono ml-2">
168 | ◀ Back
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
navigator('/')} className="bg-blue-500 text-white px-4 py-2 rounded-md">
179 | Logout
180 |
181 |
182 |
183 |
184 | {courseReq.length > 0 && (
185 |
186 |
187 | {courseReq.map((req, index) => (
188 |
189 |
{req.coursename.toUpperCase()}
190 |
{req.description}
191 |
192 |
Enrolled Teacher :
193 |
{req.enrolledteacher.Firstname} {req.enrolledteacher.Lastname}
194 |
195 |
196 |
197 |
Timing:
198 |
199 | {req.schedule.map((scheduleItem, idx) => (
200 |
201 |
Day: {formatDay(scheduleItem.day)}
202 |
Start Time: {formatTime(scheduleItem.starttime)}
203 |
End Time: {formatTime(scheduleItem.endtime)}
204 |
205 | ))}
206 |
207 |
208 |
209 |
Approval Status:
210 |
211 | {req.isapproved ? 'Approved' : 'Pending'}
212 |
213 |
214 |
215 | handleAccept(req._id,{Email:req.enrolledteacher.Email,enrolledteacher:req.enrolledteacher.Firstname})}>Approve
216 | handleReject(req._id,{Email:req.enrolledteacher.Email,enrolledteacher:req.enrolledteacher.Firstname})}>Reject
217 |
218 |
219 | ))}
220 |
221 |
222 | )}
223 |
224 | );
225 | };
226 |
227 | export default Course;
228 |
229 |
--------------------------------------------------------------------------------
/frontend/src/Pages/Dashboard/TeacherDashboard/DashboardTeacher.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import { useParams } from "react-router-dom";
3 | import Withdrawal from "./Withdrawal";
4 | import { TbMessage2Star } from "react-icons/tb";
5 |
6 | function DashboardTeacher() {
7 | const { ID } = useParams();
8 | const [data, setdata] = useState([]);
9 | const [courses, setCourses] = useState([]);
10 | const [error, setError] = useState([]);
11 | const [popup, setPopup] = useState(false);
12 | const [notification, setNotification] = useState(false);
13 | const [amount, setAmount] = useState(0);
14 | const [subjectForm, setsubjectForm] = useState('Math');
15 | const [Tdec, setTeacherDetails] = useState(null);
16 | const [starCount, setStar] = useState(5);
17 |
18 | const [formPopup, setFormPopup] = useState(false);
19 |
20 | const price = {
21 | math: 700,
22 | physics: 800,
23 | computer: 1000,
24 | chemistry: 600,
25 | biology: 500,
26 | };
27 |
28 | const daysOfWeek = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
29 |
30 | useEffect(() => {
31 | const getData = async () => {
32 | try {
33 | const response = await fetch(`/api/Teacher/TeacherDocument/${ID}`, {
34 | method: "GET",
35 | headers: {
36 | "Content-Type": "application/json",
37 | },
38 | });
39 |
40 | if (!response.ok) {
41 | throw new Error("Failed to fetch data");
42 | }
43 |
44 | const user = await response.json();
45 | setdata(user.data);
46 | // console.log(user.data);
47 | } catch (error) {
48 | setError(error.message);
49 | }
50 | };
51 | getData();
52 | }, []);
53 |
54 | useEffect(()=>{
55 | const getData = async()=>{
56 | const Data = await fetch('/api/teacher/teacherdocuments',{
57 | method: 'POST',
58 | credentials: "include",
59 | headers: {
60 | "Content-Type": "application/json",
61 | },
62 | body: JSON.stringify({teacherID : data.Teacherdetails}),
63 | })
64 | const res = await Data.json();
65 | // console.log(res.data);
66 | setTeacherDetails(res.data);
67 | }
68 |
69 | getData();
70 | },[courses])
71 |
72 | useEffect(() => {
73 | const getAmount = async () => {
74 | try {
75 | const response = await fetch(`/api/payment/teacher/${ID}/balance`, {
76 | method: "POST",
77 | headers: {
78 | "Content-Type": "application/json",
79 | },
80 | });
81 |
82 | if (!response.ok) {
83 | throw new Error("Failed to fetch data");
84 | }
85 |
86 | const user = await response.json();
87 | setAmount(user.data.newTeacher.Balance);
88 | // console.log(user)
89 | } catch (error) {
90 | // setError(error.message)
91 | console.log(error);
92 | }
93 | };
94 | getAmount();
95 | }, [amount, popup]);
96 |
97 | useEffect(() => {
98 | const getCourses = async () => {
99 | try {
100 | const response = await fetch(`/api/course/Teacher/${ID}/enrolled`, {
101 | method: "GET",
102 | headers: {
103 | "Content-Type": "application/json",
104 | },
105 | });
106 |
107 | if (!response.ok) {
108 | throw new Error("Failed to fetch data");
109 | }
110 |
111 | const res = await response.json();
112 | setCourses(res.data);
113 | console.log(res.data);
114 | } catch (error) {
115 | setError(error.message);
116 | }
117 | };
118 | getCourses();
119 | }, []);
120 |
121 | return (
122 | <>
123 |
124 |
125 | {/*
Amount: Rs. {amount}
*/}
126 |
127 | Details
128 |
129 |
setPopup(true)}
131 | className="bg-[#1671D8] p-3 rounded-md cursor-pointer"
132 | >
133 | Remuneration
134 |
135 | {/*
setNotification(prev => !prev)}>
136 | Notifications
137 |
138 |
*/}
139 |
140 |
141 |
142 |
143 |
Name: {data.Firstname} {data.Lastname}
144 | {/*
Name: {data.Firstname} {data.Lastname} {'⭐'.repeat(starCount)}
*/}
145 |
Email: {data.Email}
146 |
Phone: {Tdec?.Phone}
147 |
Address: {Tdec?.Address}
148 |
Experience: {Tdec?.Experience} years
149 |
150 |
151 |
152 |
Courses
153 | {courses &&
154 | courses.filter((course) => course.isapproved)
155 | .map((course) => (
156 |
161 | {course.coursename} :{" "}
162 |
163 | {" [ "}{course.schedule.map(days => `${daysOfWeek[days.day]} ${Math.floor(days.starttime/60)}:${(days.starttime%60 === 0 ? "00":days.starttime%60)} - ${Math.floor(days.endtime/60)}:${(days.endtime%60 === 0 ? "00" : days.endtime%60)}`).join(', ')}{" ] "}
164 |
165 |
166 | {" => "}
167 | Rs. {price[course.coursename]} per student / per month
168 |
169 |
170 | ))}
171 |
172 |
173 |
174 | {/* {notification && (
175 | show all notifications
176 | example
177 |
178 |
course : Math
179 |
Timing : sun,Mon,tue
180 |
status : pending
181 |
message : sbcxbbdjbd
182 |
183 | )} */}
184 |
185 |
186 |
187 | {popup &&
setPopup(false)} TA={amount} />}
188 |
189 | {formPopup && (
190 |
191 |
192 | {/*
✖️
*/}
193 |
194 |
Teacher Feedback Form
195 |
We highly appreciate your involvement. Please help us improve by filling out this teacher feedback form. Thank you!
196 |
197 |
198 | Full Name
199 |
200 | Course Name
201 |
202 |
203 | {/* */}
204 |
205 | Number of Years Teaching ?
206 |
207 |
208 |
209 |
210 |
Do you have suggestions on what we can do to provide you with a better service?
211 |
212 |
213 |
214 |
215 | Submit Form
216 |
217 |
218 |
219 |
220 | )}
221 |
222 | >
223 | );
224 | }
225 |
226 | export default DashboardTeacher;
227 |
--------------------------------------------------------------------------------
/frontend/src/Pages/Components/Admin/Admin.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from "react";
2 | import { IoIosNotificationsOutline } from "react-icons/io";
3 | import { NavLink, useNavigate, useParams } from "react-router-dom";
4 | import logo from '../../Images/logo.svg'
5 | import Course from "./Course";
6 | import axios from "axios";
7 |
8 | const Admin = () => {
9 | const { data } = useParams();
10 | const navigator = useNavigate();
11 |
12 |
13 | const [StudentData, setStudentData] = useState([]);
14 | const [TeacherData, setTeacherData] = useState([]);
15 | const [adminID, setAdminID] = useState(null);
16 | const [error, setErrors] = useState("");
17 | const [allmsg, setAllMsg] = useState(null);
18 | const [open, setOpen] = useState(false);
19 |
20 |
21 | useEffect(()=>{
22 | const getAllMsg = async () => {
23 | try {
24 | const response = await fetch(`/api/admin/messages/all`, {
25 | method: "GET",
26 | headers: {
27 | "Content-Type": "application/json",
28 | },
29 | });
30 |
31 | const data = await response.json();
32 | setAllMsg(data.data)
33 |
34 | } catch (err) {
35 | console.log(err.message);
36 | }
37 | };
38 | getAllMsg();
39 | },[])
40 |
41 | const Approval = async(ID, type, approve)=>{
42 | try {
43 | const data = {
44 | Isapproved : approve
45 | }
46 |
47 | const response = await fetch(`/api/admin/${adminID}/approve/${type}/${ID}`, {
48 | method: 'POST',
49 | headers: {
50 | "Content-Type": "application/json",
51 | },
52 | body: JSON.stringify(data),
53 | });
54 |
55 |
56 | if(type == "student"){
57 | setStudentData(pre => pre.filter((pre) => pre._id !== ID));
58 |
59 | }else if(type == "teacher"){
60 | setTeacherData(pre => pre.filter((pre) => pre._id !== ID));
61 |
62 | }
63 |
64 | } catch (error) {
65 | setErrors(error.message);
66 | }
67 | }
68 |
69 | const docDetails = async (type, ID) =>{
70 | navigator(`/VarifyDoc/${type}/${adminID}/${ID}`);
71 | }
72 |
73 |
74 | useEffect(() => {
75 | const getData = async () => {
76 | try {
77 | const response = await fetch(`/api/admin/${data}/approve`, {
78 | method: "POST",
79 | headers: {
80 | "Content-Type": "application/json",
81 | },
82 | });
83 |
84 | if (!response.ok) {
85 | throw new Error("Failed to fetch data");
86 | } else {
87 | const result = await response.json();
88 |
89 | setStudentData(result.data.studentsforApproval);
90 | setTeacherData(result.data.teachersforApproval);
91 | setAdminID(result.data.admin._id);
92 | }
93 | } catch (err) {
94 | console.log(err.message);
95 | }
96 | };
97 | getData();
98 | }, []);
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 | return (
113 |
114 | {/* Navbar */}
115 |
116 |
117 |
118 |
123 |
124 | Shiksharthee
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
navigator('/')} className="bg-blue-500 text-white px-4 py-2 rounded-md">
134 | Logout
135 |
136 |
137 |
138 |
139 | {/* Main Section */}
140 |
141 |
142 | All New Request
143 |
144 |
145 |
setOpen(prev => !prev)} className=" absolute right-10 top-[6.5rem] text-center cursor-pointer">
146 |
Messages
147 |
148 |
149 |
navigator(`/admin/course/${data}`)} className=" absolute right-52 top-[6.5rem] text-center cursor-pointer">
150 |
Course Requests
151 |
152 |
153 | {open && (
154 |
155 | {allmsg.map((msg,index) => (
156 |
157 |
Name : {msg.name}
158 |
Email : {msg.email}
159 |
Message : {msg.message}
160 |
161 | ))}
162 |
163 |
164 | )}
165 |
166 |
167 |
168 |
169 |
170 |
Student Request
171 | {
172 | StudentData.length > 0 ? StudentData.map((student) => (
173 | student.Isapproved === "pending" && (
174 |
docDetails("student", student._id)}
177 | className="flex justify-around items-center mt-8 p-8 bg-blue-gray-600 rounded-md cursor-pointer"
178 | >
179 |
180 | {student.Firstname + " " + student.Lastname}
181 |
182 |
Status: {student.Isapproved}
183 |
184 | )
185 | )) : null
186 | }
187 |
188 |
189 |
190 |
Teacher Request
191 | {
192 | TeacherData.length > 0 ? TeacherData.map((teacher) => (
193 | teacher.Isapproved === "pending" && (
194 |
docDetails("teacher", teacher._id)}
197 | className="flex justify-around items-center mt-8 p-8 bg-blue-gray-600 rounded-md cursor-pointer"
198 | >
199 |
200 | {teacher.Firstname + " " + teacher.Lastname}
201 |
202 |
Status: {teacher.Isapproved}
203 |
204 | )
205 | )) : null
206 | }
207 |
208 |
209 |
210 |
Rejected Request
211 | {
212 | TeacherData.length > 0 ? TeacherData.map((teacher) => (
213 | teacher.Isapproved === "rejected" && (
214 |
docDetails("teacher", teacher._id)}
217 | className="flex justify-around items-center mt-8 p-8 bg-blue-gray-600 rounded-md cursor-pointer"
218 | >
219 |
220 | {teacher.Firstname + " " + teacher.Lastname}
221 |
222 |
Msg: {teacher.Remarks}
223 |
224 | )
225 | )) : null
226 | }
227 | {
228 | StudentData.length > 0 ? StudentData.map((student) => (
229 | student.Isapproved === "rejected" && (
230 |
docDetails("student", student._id)}
233 | className="flex justify-around items-center mt-8 p-8 bg-blue-gray-600 rounded-md cursor-pointer"
234 | >
235 |
236 | {student.Firstname + " " + student.Lastname}
237 |
238 |
Msg: {student.Remarks}
239 |
240 | )
241 | )) : null
242 | }
243 |
244 |
245 |
246 |
247 |
248 | );
249 | };
250 |
251 | export default Admin;
--------------------------------------------------------------------------------
/frontend/src/Pages/Components/Admin/VarifyDoc.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import { useNavigate, useParams } from 'react-router-dom';
3 |
4 | function VarifyDoc() {
5 | const { type, adminID, ID } = useParams();
6 | const [data, setData] = useState(null);
7 | const navigator = useNavigate();
8 | const [value, setValue] = useState("");
9 |
10 | const handleMessage = (event) => {
11 | setValue(event.target.value);
12 | };
13 |
14 | const Approval = async(id, type, approve, email)=>{
15 | try {
16 | const data = {
17 | Isapproved : approve,
18 | remarks : value,
19 | email: email,
20 | }
21 |
22 | const response = await fetch(`/api/admin/${adminID}/approve/${type}/${id}`, {
23 | method: 'POST',
24 | headers: {
25 | "Content-Type": "application/json",
26 | },
27 | body: JSON.stringify(data),
28 | });
29 |
30 | navigator(`/admin/${adminID}`);
31 |
32 | } catch (error) {
33 | console.log(error.message);
34 | }
35 | }
36 |
37 | useEffect(() => {
38 | const getData = async () => {
39 | try {
40 | const docData = await fetch(`/api/admin/${adminID}/documents/${type}/${ID}`);
41 | const response = await docData.json();
42 | setData(response.data);
43 | console.log(response.data);
44 | } catch (err) {
45 | console.log(err.message);
46 | }
47 | };
48 | getData();
49 | }, []);
50 |
51 | return (
52 | <>
53 |
54 |
55 |
navigator(`/admin/${adminID}`)} className="text-lg sm:text-xl md:text-2xl lg:text-3xl text-blue-700 font-bold font-mono ml-2">
56 | ◀ Back
57 |
58 |
59 |
Document Details
60 |
61 | navigator('/')} className="bg-blue-500 text-white px-4 py-2 rounded-md">
62 | Logout
63 |
64 |
65 |
66 |
67 | {type === "student" && data && data.theStudent && (
68 | <>
69 |
70 |
Full Name : {data.theStudent.Firstname} {data.theStudent.Lastname}
71 |
Phone No : {data.studentDocs.Phone}
72 |
Highest Education : {data.studentDocs.Highesteducation}
73 |
Address : {data.studentDocs.Address}
74 |
75 |
76 |
77 |
78 |
79 |
10th Marksheet : {data.studentDocs.SecondaryMarks}%
80 |
81 |
82 |
83 |
12th Marksheet : {data.studentDocs.HigherMarks}%
84 |
85 |
86 |
87 |
Aadhar Card
88 |
89 |
90 |
91 |
92 |
Approval(data.theStudent._id, "student", "approved",data.theStudent.Email)}>
93 | Approve !
94 |
95 |
Approval(data.theStudent._id, "student", "rejected",data.theStudent.Email)}>
96 | Reject !
97 |
98 |
99 |
Approval(data.theStudent._id, "student", "reupload", data.theStudent.Email)}>
100 | Reupload !
101 |
102 |
103 |
104 |
105 | >
106 | )}
107 |
108 | {type === "teacher" && data && data.theTeacher && (
109 | <>
110 |
111 |
Full Name : {data.theTeacher.Firstname} {data.theTeacher.Lastname}
112 |
Phone No : {data.teacherDocs.Phone}
113 |
Experience : {data.teacherDocs.Experience} years
114 |
Address : {data.teacherDocs.Address}
115 |
116 |
117 |
118 |
119 |
120 |
10th Marksheet : {data.teacherDocs.SecondaryMarks}%
121 |
122 |
123 |
124 |
12th Marksheet : {data.teacherDocs.HigherMarks}%
125 |
126 |
127 |
128 |
U.G. Marksheet : {data.teacherDocs.UGmarks}
129 |
130 |
131 |
132 |
P.G. Marksheet : {data.teacherDocs.PGmarks}
133 |
134 |
135 |
136 |
Aadhar Card
137 |
138 |
139 |
140 |
141 |
142 |
Approval(data.theTeacher._id, "teacher", "approved",data.theTeacher.Email)}>
143 | Approve !
144 |
145 |
Approval(data.theTeacher._id, "teacher", "rejected",data.theTeacher.Email)}>
146 | Reject !
147 |
148 |
149 |
Approval(data.theTeacher._id, "teacher", "reupload", data.theTeacher.Email)}>
150 | Reupload !
151 |
152 |
153 |
154 |
155 | >
156 | )}
157 | >
158 | );
159 | }
160 |
161 | export default VarifyDoc;
162 |
--------------------------------------------------------------------------------