├── .gitignore ├── README.md ├── backend ├── config │ └── db.js ├── controllers │ ├── courseController.js │ └── teacherController.js ├── middleware │ ├── authMiddleware.js │ └── errorMiddleware.js ├── models │ ├── courseModel.js │ ├── examModel.js │ ├── questionModel.js │ ├── resultModel.js │ ├── studentModel.js │ └── teacherModel.js ├── routes │ ├── courseRoutes.js │ └── teacherRoutes.js ├── server.js └── utils │ └── generateToken.js ├── frontend ├── README.md ├── package-lock.json ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt └── src │ ├── App.css │ ├── App.jsx │ ├── actions │ ├── courseActions.js │ └── teacherActions.js │ ├── assets │ └── img │ │ ├── banner-bg-01.jpg │ │ ├── banner-bg-02.jpg │ │ ├── banner-bg-03.jpg │ │ ├── banner-img-01.svg │ │ ├── banner-img-02.svg │ │ ├── lists.svg │ │ └── workspace.jpg │ ├── components │ ├── Alert.jsx │ ├── Banner.jsx │ ├── CourseBox.jsx │ ├── CourseForTeacher.jsx │ ├── FeatureBox.jsx │ ├── Features.jsx │ ├── Footer.jsx │ ├── Mission.jsx │ ├── Navbar.jsx │ ├── QsnForTeacher.jsx │ ├── Spinner.jsx │ ├── TeacherNavs.jsx │ ├── TeacherTabs.jsx │ ├── TestBox.jsx │ └── TestForTeacher.jsx │ ├── constants │ ├── courseConstants.js │ └── teacherConstants.js │ ├── index.css │ ├── index.js │ ├── reducers │ ├── courseReducers.js │ └── teacherReducers.js │ ├── reportWebVitals.js │ ├── screens │ ├── About.jsx │ ├── AddQuestion.jsx │ ├── Contact.jsx │ ├── Courses.jsx │ ├── Home.jsx │ ├── Notices.jsx │ ├── StudentDash.jsx │ ├── StudentLogin.jsx │ ├── StudentRegister.jsx │ ├── TeacherCourses.jsx │ ├── TeacherDash.jsx │ ├── TeacherLogin.jsx │ ├── TeacherRegister.jsx │ ├── TeacherTests.jsx │ └── Tests.jsx │ └── store.js ├── package-lock.json └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | node_modules/ 6 | /.pnp 7 | .pnp.js 8 | 9 | # testing 10 | /coverage 11 | 12 | # production 13 | /build 14 | 15 | # misc 16 | .DS_Store 17 | .env 18 | .env.local 19 | .env.development.local 20 | .env.test.local 21 | .env.production.local 22 | 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LMS with MERN stack and Redux 2 | # Usage 3 | --- 4 | ## ES Modules in Node 5 | We use ECMAScript Modules in the backend in this project. Be sure to have at least Node v14.6+ or you will need to add the "--experimental-modules" flag. 6 | 7 | ## Env Variables 8 | Create a .env file in then root and add the following 9 | NODE_ENV = development 10 | PORT = 8000 11 | MONGO_URI = your mongodb uri 12 | JWT_SECRET = 'abc123' 13 | 14 | ## Install Dependencies (frontend & backend) 15 | npm install 16 | cd frontend 17 | npm install 18 | 19 | ## Run 20 | #### Run frontend (:3000) & backend (:8000) 21 | npm run dev 22 | 23 | #### Run backend only 24 | npm run server 25 | -------------------------------------------------------------------------------- /backend/config/db.js: -------------------------------------------------------------------------------- 1 | import mongoose from 'mongoose' 2 | 3 | const connectDB = async () => { 4 | try { 5 | const conn = await mongoose.connect(process.env.MONGO_URI, { 6 | useUnifiedTopology: true, 7 | useNewUrlParser: true, 8 | useCreateIndex: true, 9 | }) 10 | 11 | console.log(`MongoDB Connect: ${conn.connection.host}`) 12 | } 13 | catch (error) { 14 | console.error(`Error: ${error.message}`) 15 | process.exit(1) 16 | } 17 | } 18 | 19 | export default connectDB; -------------------------------------------------------------------------------- /backend/controllers/courseController.js: -------------------------------------------------------------------------------- 1 | import asyncHandler from 'express-async-handler'; 2 | import Course from '../models/courseModel.js' 3 | 4 | // @desc Create a course 5 | // @route POST /api/course/create 6 | // @access Private 7 | 8 | const createCourse = asyncHandler(async (req, res) => { 9 | 10 | const { course_name, 11 | course_outline, 12 | total_units } = req.body 13 | 14 | const course = new Course({ 15 | course_name: course_name, 16 | course_outline: course_outline, 17 | total_units: total_units, 18 | created_by: req.user._id 19 | }) 20 | 21 | try { 22 | const createdCourse = await course.save() 23 | res.status(201).json(createdCourse) 24 | } 25 | catch (error) { 26 | res.status(400) 27 | throw new Error('Unable to create course') 28 | } 29 | }) 30 | 31 | //@desc Fetch all courses 32 | //@route GET /api/course/all 33 | //@access Public 34 | const getCourses = asyncHandler(async(req,res) => { 35 | const courses = await Course.find({}) 36 | 37 | res.json(courses); 38 | }) 39 | 40 | //@desc Fetch specific courses 41 | //@route GET /api/course/specific 42 | //@access Private 43 | const getSpecificCourses = asyncHandler(async(req,res) => { 44 | const courses = await Course.find({}) 45 | 46 | const courseData = courses.filter(course => { 47 | if(course.created_by.equals(req.user._id)) { 48 | return course 49 | } 50 | }) 51 | 52 | res.status(200).send(courseData) 53 | 54 | }) 55 | 56 | // @desc Update a course 57 | // @route POST /api/course/update/:id 58 | // @access Private 59 | 60 | const updateCourse = asyncHandler(async (req, res) => { 61 | 62 | const { course_name, 63 | course_outline, 64 | total_units } = req.body 65 | 66 | const course = await Course.findById(req.params.id) 67 | 68 | if(course) { 69 | course.course_name = course_name 70 | course.course_outline = course_outline 71 | course.total_units = total_units 72 | 73 | const updatedCourse = await course.save() 74 | res.json(updatedCourse) 75 | } 76 | else { 77 | res.status(404) 78 | throw new Error('Course not found') 79 | } 80 | }) 81 | 82 | // @desc Delete a course 83 | // @route POST /api/course/delete/:id 84 | // @access Private 85 | 86 | const deleteCourse = asyncHandler(async (req, res) => { 87 | 88 | const id = req.params.id 89 | 90 | Course.findOneAndRemove({_id: id}, function(err) { 91 | if(err) { 92 | res.status(404) 93 | throw new Error('Course not found') 94 | } 95 | else { 96 | res.json({"message": "The course has been deleted"}) 97 | } 98 | }) 99 | }) 100 | 101 | 102 | export { createCourse, getCourses, getSpecificCourses, updateCourse, deleteCourse } -------------------------------------------------------------------------------- /backend/controllers/teacherController.js: -------------------------------------------------------------------------------- 1 | import asyncHandler from 'express-async-handler'; 2 | import Teacher from '../models/teacherModel.js' 3 | import bcrypt from 'bcryptjs' 4 | import generateToken from '../utils/generateToken.js' 5 | 6 | 7 | 8 | //@desc Auth user & get token 9 | //@route POST /api/teacher/login 10 | //@access Public 11 | const authUser = asyncHandler(async(req,res) => { 12 | const { tchr_email, password } = req.body 13 | const user = await Teacher.findOne({tchr_email}) 14 | 15 | if(user) { 16 | const { hashPassword } = user 17 | const verified = bcrypt.compareSync(password, hashPassword); 18 | if(verified) { 19 | res.status(201).json({ 20 | _id: user._id, 21 | tchr_name: user.tchr_name, 22 | tchr_email: user.tchr_email, 23 | user_type: user.user_type, 24 | tchr_mobile: user.tchr_mobile, 25 | tchr_address: user.tchr_address, 26 | tchr_pic: user.tchr_pic, 27 | token: generateToken(user._id) 28 | }) 29 | } 30 | else { 31 | res.status(400) 32 | throw new Error('Incorrect password') 33 | } 34 | } 35 | else { 36 | res.status(404) 37 | throw new Error('User not found') 38 | } 39 | }) 40 | 41 | //@desc Register a new teacher 42 | //@route POST /api/teacher/register 43 | //@access Public 44 | const registerUser = asyncHandler(async(req,res) => { 45 | const { tchr_name, 46 | tchr_email, 47 | password, 48 | tchr_mobile, 49 | tchr_address, 50 | tchr_pic 51 | } = req.body 52 | 53 | const userExits = await Teacher.findOne({tchr_email}) 54 | 55 | if(userExits) { 56 | res.status(400) 57 | throw new Error('Teacher already exits') 58 | } 59 | 60 | const salt = bcrypt.genSaltSync(10); 61 | const hashPassword = bcrypt.hashSync(password, salt); 62 | const user_type = "teacher" 63 | 64 | const user = await Teacher.create({ 65 | tchr_name, 66 | tchr_email, 67 | hashPassword, 68 | user_type, 69 | tchr_mobile, 70 | tchr_address, 71 | tchr_pic 72 | }) 73 | 74 | if(user) { 75 | res.status(201).json({ 76 | _id: user._id, 77 | tchr_name: user.tchr_name, 78 | tchr_email: user.tchr_email, 79 | user_type: user.user_type, 80 | tchr_mobile: user.tchr_mobile, 81 | tchr_address: user.tchr_address, 82 | tchr_pic: user.tchr_pic, 83 | token: generateToken(user._id) 84 | }) 85 | } 86 | else { 87 | res.status(400) 88 | throw new Error('Invalid user data') 89 | } 90 | }) 91 | 92 | export { authUser, registerUser } -------------------------------------------------------------------------------- /backend/middleware/authMiddleware.js: -------------------------------------------------------------------------------- 1 | import jwt from 'jsonwebtoken' 2 | import Teacher from '../models/teacherModel.js' 3 | import asyncHandler from 'express-async-handler'; 4 | 5 | const protectTeacher = asyncHandler(async(req,res,next) => { 6 | //console.log(req.headers.authorization) 7 | let token 8 | if(req.headers.authorization && req.headers.authorization.startsWith('Bearer')) 9 | { 10 | console.log('token found') 11 | try { 12 | token = req.headers.authorization.split(' ')[1] 13 | const decoded = jwt.verify(token, process.env.JWT_SECRET) 14 | //console.log(decoded) 15 | req.user = await Teacher.findById(decoded.id).select('-password') 16 | next() 17 | } 18 | catch (error) { 19 | console.error(error) 20 | res.status(401) 21 | throw new Error('Not authorized token failed') 22 | } 23 | } 24 | 25 | else 26 | { 27 | res.status(401) 28 | throw new Error('Not authorized, no token') 29 | } 30 | }) 31 | 32 | export default protectTeacher -------------------------------------------------------------------------------- /backend/middleware/errorMiddleware.js: -------------------------------------------------------------------------------- 1 | const notFound = (req,res,next) => { 2 | const error = new Error(`Not found - ${req.originalUrl}`); 3 | res.status(404); 4 | next(error); 5 | } 6 | 7 | const errorHandler = (err,req,res,next) => { 8 | const statusCode = res.statusCode === 200 ? 500 : res.statusCode; 9 | res.status(statusCode); 10 | res.json({ 11 | message: err.message, 12 | stack: process.env.NODE_ENV === 'production' ? null : err.stack, 13 | }); 14 | } 15 | 16 | export {notFound, errorHandler} -------------------------------------------------------------------------------- /backend/models/courseModel.js: -------------------------------------------------------------------------------- 1 | import mongoose from 'mongoose' 2 | 3 | const courseSchema = mongoose.Schema({ 4 | course_name: { 5 | type: String, 6 | required: true 7 | }, 8 | course_outline: { 9 | type: String, 10 | required: true, 11 | }, 12 | total_units: { 13 | type: Number, 14 | required: true, 15 | default: 1 16 | }, 17 | total_students: { 18 | type: Number, 19 | required: true, 20 | default: 0 21 | }, 22 | created_by: { 23 | type: mongoose.Schema.Types.ObjectId, 24 | required: true, 25 | ref: 'Teacher' 26 | }, 27 | active: { 28 | type: Boolean, 29 | required: true, 30 | default: 1 31 | }, 32 | cKey: { 33 | type: Boolean, 34 | required: true, 35 | default: 1 36 | } 37 | }, { 38 | timestamps: true 39 | }) 40 | 41 | const Course = mongoose.model('Course', courseSchema) 42 | export default Course; -------------------------------------------------------------------------------- /backend/models/examModel.js: -------------------------------------------------------------------------------- 1 | import mongoose from 'mongoose' 2 | 3 | const examSchema = mongoose.Schema({ 4 | exam_name: { 5 | type: String, 6 | required: true, 7 | unique: true 8 | }, 9 | no_of_questions: { 10 | type: Number, 11 | required: true 12 | }, 13 | total_marks: { 14 | type: Number, 15 | required: true 16 | }, 17 | total_time: { 18 | type: Number, 19 | required: true 20 | }, 21 | created_by: { 22 | type: mongoose.Schema.Types.ObjectId, 23 | required: true, 24 | ref: 'Teacher' 25 | }, 26 | course: { 27 | type: mongoose.Schema.Types.ObjectId, 28 | required: true, 29 | ref: 'Course' 30 | }, 31 | active: { 32 | type: Boolean, 33 | required: true, 34 | default: 1 35 | }, 36 | cKey: { 37 | type: Boolean, 38 | required: true, 39 | default: 1 40 | } 41 | }, { 42 | timestamps: true 43 | }) 44 | 45 | const Exam = mongoose.model('Exam', examSchema) 46 | export default Exam; -------------------------------------------------------------------------------- /backend/models/questionModel.js: -------------------------------------------------------------------------------- 1 | import mongoose from 'mongoose' 2 | 3 | const questionSchema = mongoose.Schema({ 4 | exam: { 5 | type: mongoose.Schema.Types.ObjectId, 6 | required: true, 7 | ref: 'Exam' 8 | }, 9 | question_text: { 10 | type: String, 11 | required: true, 12 | unique: true 13 | }, 14 | mark: { 15 | type: Number, 16 | required: true 17 | }, 18 | option1: { 19 | type: String, 20 | required: true 21 | }, 22 | option2: { 23 | type: String, 24 | required: true 25 | }, 26 | option3: { 27 | type: String, 28 | required: true 29 | }, 30 | option4: { 31 | type: String, 32 | required: true 33 | }, 34 | answer: { 35 | type: String, 36 | required: true 37 | }, 38 | active: { 39 | type: Boolean, 40 | required: true, 41 | default: 1 42 | }, 43 | cKey: { 44 | type: Boolean, 45 | required: true, 46 | default: 1 47 | } 48 | }, { 49 | timestamps: true 50 | }) 51 | 52 | const Question = mongoose.model('Question', questionSchema) 53 | export default Question; -------------------------------------------------------------------------------- /backend/models/resultModel.js: -------------------------------------------------------------------------------- 1 | import mongoose from 'mongoose' 2 | 3 | const resultSchema = mongoose.Schema({ 4 | student: { 5 | type: mongoose.Schema.Types.ObjectId, 6 | required: true, 7 | ref: 'Student' 8 | }, 9 | exam: { 10 | type: mongoose.Schema.Types.ObjectId, 11 | required: true, 12 | ref: 'Exam' 13 | }, 14 | marks_obtained: { 15 | type: Number, 16 | required: true, 17 | }, 18 | active: { 19 | type: Boolean, 20 | required: true, 21 | default: 1 22 | }, 23 | cKey: { 24 | type: Boolean, 25 | required: true, 26 | default: 1 27 | } 28 | }, { 29 | timestamps: true 30 | }) 31 | 32 | const Result = mongoose.model('Result', resultSchema) 33 | export default Result; -------------------------------------------------------------------------------- /backend/models/studentModel.js: -------------------------------------------------------------------------------- 1 | import mongoose from 'mongoose' 2 | 3 | const studentSchema = mongoose.Schema({ 4 | stud_name: { 5 | type: String, 6 | required: true 7 | }, 8 | stud_email: { 9 | type: String, 10 | required: true, 11 | unique: true 12 | }, 13 | password: { 14 | type: String, 15 | required: true 16 | }, 17 | stud_mobile: { 18 | type: Number, 19 | required: true 20 | }, 21 | stud_address: { 22 | type: String, 23 | required: true 24 | }, 25 | stud_pic: { 26 | type: String, 27 | required: false 28 | }, 29 | course: { 30 | type: Array, 31 | required: true 32 | }, 33 | exam: { 34 | type: Array, 35 | required: true 36 | }, 37 | active: { 38 | type: Boolean, 39 | required: true, 40 | default: 1 41 | }, 42 | cKey: { 43 | type: Boolean, 44 | required: true, 45 | default: 1 46 | } 47 | }, { 48 | timestamps: true 49 | }) 50 | 51 | const Student = mongoose.model('Student', studentSchema) 52 | export default Student; -------------------------------------------------------------------------------- /backend/models/teacherModel.js: -------------------------------------------------------------------------------- 1 | import mongoose from 'mongoose' 2 | 3 | const teacherSchema = mongoose.Schema({ 4 | tchr_name: { 5 | type: String, 6 | required: true 7 | }, 8 | tchr_email: { 9 | type: String, 10 | required: true, 11 | unique: true 12 | }, 13 | hashPassword: { 14 | type: String, 15 | required: true 16 | }, 17 | user_type: { 18 | type: String, 19 | required: true 20 | }, 21 | tchr_mobile: { 22 | type: Number, 23 | required: true 24 | }, 25 | tchr_address: { 26 | type: String, 27 | required: true 28 | }, 29 | tchr_pic: { 30 | type: String, 31 | required: false 32 | }, 33 | active: { 34 | type: Boolean, 35 | required: true, 36 | default: 1 37 | }, 38 | cKey: { 39 | type: Boolean, 40 | required: true, 41 | default: 1 42 | } 43 | }, { 44 | timestamps: true 45 | }) 46 | 47 | const Teacher = mongoose.model('Teacher', teacherSchema) 48 | export default Teacher; -------------------------------------------------------------------------------- /backend/routes/courseRoutes.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import { createCourse, getCourses, getSpecificCourses, updateCourse, deleteCourse } from '../controllers/courseController.js' 3 | import protectTeacher from '../middleware/authMiddleware.js' 4 | 5 | const router = express.Router() 6 | 7 | router.route('/create').post(protectTeacher, createCourse) 8 | router.route('/specific').get(protectTeacher, getSpecificCourses) 9 | router.route('/update/:id').put(protectTeacher, updateCourse) 10 | router.route('/delete/:id').delete(protectTeacher, deleteCourse) 11 | router.get('/all', getCourses) 12 | 13 | export default router; -------------------------------------------------------------------------------- /backend/routes/teacherRoutes.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import { authUser, registerUser } from '../controllers/teacherController.js' 3 | 4 | const router = express.Router() 5 | 6 | router.post('/login', authUser) 7 | router.post('/register', registerUser) 8 | 9 | export default router; -------------------------------------------------------------------------------- /backend/server.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import dotenv from 'dotenv'; 3 | import connectDB from './config/db.js'; 4 | import teacherRoutes from './routes/teacherRoutes.js'; 5 | import courseRoutes from './routes/courseRoutes.js'; 6 | 7 | dotenv.config() 8 | 9 | connectDB() 10 | 11 | const app = express() 12 | 13 | app.use(express.json()); //middleware 14 | 15 | app.get('/', (req,res) => { 16 | res.send("Let's goo!!"); 17 | }); 18 | 19 | app.use('/api/teacher', teacherRoutes) 20 | app.use('/api/course', courseRoutes) 21 | 22 | const port = 8000 || process.env.PORT; 23 | 24 | app.listen(port, () => { 25 | console.log(`Listening to port ${port}`); 26 | }); -------------------------------------------------------------------------------- /backend/utils/generateToken.js: -------------------------------------------------------------------------------- 1 | import jwt from 'jsonwebtoken' 2 | 3 | const generateToken = (id) => { 4 | return jwt.sign({ id }, process.env.JWT_SECRET, { 5 | expiresIn: '30d' 6 | }) 7 | } 8 | 9 | export default generateToken -------------------------------------------------------------------------------- /frontend/README.md: -------------------------------------------------------------------------------- 1 | # Getting Started with Create React App 2 | 3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 4 | 5 | ## Available Scripts 6 | 7 | In the project directory, you can run: 8 | 9 | ### `npm start` 10 | 11 | Runs the app in the development mode.\ 12 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 13 | 14 | The page will reload if you make edits.\ 15 | You will also see any lint errors in the console. 16 | 17 | ### `npm test` 18 | 19 | Launches the test runner in the interactive watch mode.\ 20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 21 | 22 | ### `npm run build` 23 | 24 | Builds the app for production to the `build` folder.\ 25 | It correctly bundles React in production mode and optimizes the build for the best performance. 26 | 27 | The build is minified and the filenames include the hashes.\ 28 | Your app is ready to be deployed! 29 | 30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 31 | 32 | ### `npm run eject` 33 | 34 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!** 35 | 36 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 37 | 38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. 39 | 40 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. 41 | 42 | ## Learn More 43 | 44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 45 | 46 | To learn React, check out the [React documentation](https://reactjs.org/). 47 | 48 | ### Code Splitting 49 | 50 | This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting) 51 | 52 | ### Analyzing the Bundle Size 53 | 54 | This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size) 55 | 56 | ### Making a Progressive Web App 57 | 58 | This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app) 59 | 60 | ### Advanced Configuration 61 | 62 | This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration) 63 | 64 | ### Deployment 65 | 66 | This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment) 67 | 68 | ### `npm run build` fails to minify 69 | 70 | This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify) 71 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend", 3 | "proxy": "http://127.0.0.1:8000", 4 | "version": "0.1.0", 5 | "private": true, 6 | "dependencies": { 7 | "@testing-library/jest-dom": "^5.12.0", 8 | "@testing-library/react": "^11.2.7", 9 | "@testing-library/user-event": "^12.8.3", 10 | "axios": "^0.21.1", 11 | "bootstrap": "^5.0.1", 12 | "bootstrap-icons": "^1.5.0", 13 | "react": "^17.0.2", 14 | "react-dom": "^17.0.2", 15 | "react-redux": "^7.2.4", 16 | "react-router-dom": "^5.2.0", 17 | "react-scripts": "4.0.3", 18 | "redux": "^4.1.0", 19 | "redux-devtools-extension": "^2.13.9", 20 | "redux-thunk": "^2.3.0", 21 | "web-vitals": "^1.1.2" 22 | }, 23 | "scripts": { 24 | "start": "react-scripts start", 25 | "build": "react-scripts build", 26 | "test": "react-scripts test", 27 | "eject": "react-scripts eject" 28 | }, 29 | "eslintConfig": { 30 | "extends": [ 31 | "react-app", 32 | "react-app/jest" 33 | ] 34 | }, 35 | "browserslist": { 36 | "production": [ 37 | ">0.2%", 38 | "not dead", 39 | "not op_mini all" 40 | ], 41 | "development": [ 42 | "last 1 chrome version", 43 | "last 1 firefox version", 44 | "last 1 safari version" 45 | ] 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Parthava/learning-management-system/f15b3e8ad8de554187bae265724585f8ef4b7cfe/frontend/public/favicon.ico -------------------------------------------------------------------------------- /frontend/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 14 | LEMNOS 15 | 16 | 17 | 18 |
19 | 20 | 21 | -------------------------------------------------------------------------------- /frontend/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Parthava/learning-management-system/f15b3e8ad8de554187bae265724585f8ef4b7cfe/frontend/public/logo192.png -------------------------------------------------------------------------------- /frontend/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Parthava/learning-management-system/f15b3e8ad8de554187bae265724585f8ef4b7cfe/frontend/public/logo512.png -------------------------------------------------------------------------------- /frontend/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /frontend/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /frontend/src/App.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | box-sizing: border-box; 5 | } 6 | 7 | .app { 8 | text-align: left; 9 | } 10 | 11 | html {font-family: 'Open Sans', sans-serif;} 12 | 13 | /* Typography */ 14 | .light-300 { 15 | font-family: 'Open Sans', sans-serif !important; 16 | font-weight: 300; 17 | } 18 | .regular-400 { 19 | font-family: 'Open Sans', sans-serif !important; 20 | font-weight: 400; 21 | } 22 | .semi-bold-600 { 23 | font-family: 'Open Sans', sans-serif !important; 24 | font-weight: 600; 25 | } 26 | .typo-space-line::after, 27 | .typo-space-line-center::after { 28 | content: ""; 29 | width: 150px; 30 | display:block; 31 | position: absolute; 32 | border-bottom: 5px solid #6266ea; 33 | padding-top: .5em; 34 | } 35 | .typo-space-line-center::after { 36 | left: 50%; 37 | margin-left: -75px; 38 | } 39 | 40 | /* Home Banner */ 41 | #index_banner { 42 | background-image: url('./assets/img/banner-bg-01.jpg'); 43 | background-position: center center; background-size: cover; 44 | height: 100%; 45 | min-height: 60vh; 46 | width: 100%; 47 | } 48 | #index_banner .carousel-item { 49 | min-height: 60vh; 50 | } 51 | #index_banner .carousel-control-prev i, 52 | #index_banner .carousel-control-next i { 53 | color: #6266ea !important; 54 | text-decoration: none; 55 | font-size: 4em; 56 | } 57 | #index_banner .carousel-inner { 58 | height: 80vh; 59 | } 60 | #index_banner .carousel-indicators li, 61 | #index_banner .carousel-indicators.active { 62 | background-color: #6266ea !important; 63 | } 64 | 65 | /* Mission */ 66 | .mission_wrapper .mission_footer { 67 | max-width: 720px; 68 | } 69 | 70 | /* Features */ 71 | .features.card{ 72 | border-radius: 16px !important; 73 | } 74 | .features .features-vertical { 75 | background: rgb(255,255,255); 76 | background: linear-gradient(0deg, rgba(255,255,255,1) 0%, rgba(255,255,255,0) 33.333%, rgba(97,84,199,1) 66.666%, rgba(255,255,255,0) 99.999%); 77 | background-size: 300% 300%; 78 | background-position: 0% 100%; 79 | transition: .5s ease-out; 80 | cursor: pointer; 81 | } 82 | .features .features-vertical:hover { 83 | background-position: 0% 0%; 84 | transition: .5s ease-out; 85 | } 86 | .features .features-vertical:hover div.features-content { 87 | top: 50%; 88 | margin-top: -2.5em; 89 | position: absolute; 90 | } 91 | .features .features-vertical:hover * { 92 | color: white; 93 | transition: .5s ease-out; 94 | } 95 | 96 | /* Page Banners */ 97 | #page_banner1 { 98 | background-image: url('./assets/img/banner-bg-02.jpg'); 99 | background-position: center center; background-size: cover; 100 | height: 100%; 101 | width: 100%; 102 | } 103 | #page_banner2 { 104 | background-image: url('./assets/img/banner-bg-03.jpg'); 105 | background-position: center center; background-size: cover; 106 | height: 100%; 107 | width: 100%; 108 | } 109 | 110 | /* Courses */ 111 | .course.card { 112 | border-radius: 10px !important; 113 | cursor: pointer; 114 | } 115 | .course .course-vertical { 116 | background: rgb(0,0,0); 117 | background: linear-gradient(0deg, rgba(0,0,0,1) 0%, rgba(0,0,0,0) 33.33%, rgba(255,255,255,1) 66.66%, rgba(255,255,255,0) 99.99%); 118 | background-size: 300% 300%; 119 | background-position: 0% 100%; 120 | transition: .5s ease-out; 121 | } 122 | .course .course-vertical:hover { 123 | background-position: 0% 0%; 124 | transition: .5s ease-out; 125 | } 126 | .course .course-vertical:hover * { 127 | color: #000; 128 | border-color: #000; 129 | transition: .5s ease-out; 130 | } 131 | 132 | 133 | /* Contact */ 134 | .contact_form label { 135 | margin-bottom: 16px; 136 | } 137 | 138 | #message { 139 | height: 150px 140 | } 141 | 142 | .td_figure { 143 | perspective: 1500px; 144 | } 145 | 146 | .td_figure img { 147 | transform: rotateX(45deg) rotateY(20deg) rotateZ(3deg); 148 | transition: all .3s ease; 149 | } 150 | 151 | .td_figure img:hover { 152 | transform: rotate(0); 153 | } 154 | 155 | .sidebar { 156 | margin-left: -4rem; 157 | position: fixed; 158 | opacity: 0.4; 159 | transition: all .3s ease-in-out; 160 | } 161 | 162 | .sidebar:hover { 163 | margin-left: 0; 164 | opacity: 1; 165 | } 166 | 167 | .loader { 168 | height: 7rem; 169 | width: 7rem; 170 | } 171 | 172 | .min__height { 173 | min-height: 80vh; 174 | margin-top: 70px; 175 | } 176 | 177 | 178 | /* Footer */ 179 | footer a.text-light:hover { 180 | color: #bfbfbf !important; 181 | transition-duration: 0.15s; 182 | } -------------------------------------------------------------------------------- /frontend/src/App.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { BrowserRouter, Switch, Route, Redirect } from "react-router-dom"; 3 | import Navbar from "./components/Navbar"; 4 | import Home from "./screens/Home"; 5 | import About from "./screens/About"; 6 | import Courses from "./screens/Courses"; 7 | import Tests from "./screens/Tests"; 8 | import Contact from "./screens/Contact"; 9 | import Notices from "./screens/Notices"; 10 | import TeacherLogin from "./screens/TeacherLogin"; 11 | import TeacherRegister from "./screens/TeacherRegister"; 12 | import TeacherDash from "./screens/TeacherDash"; 13 | import TeacherCourses from "./screens/TeacherCourses"; 14 | import TeacherTests from "./screens/TeacherTests"; 15 | import AddQuestion from "./screens/AddQuestion"; 16 | import StudentLogin from "./screens/StudentLogin"; 17 | import StudentRegister from "./screens/StudentRegister"; 18 | import StudentDash from "./screens/StudentDash"; 19 | import './App.css'; 20 | 21 | function App() { 22 | return ( 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 |
46 | ); 47 | } 48 | 49 | export default App; 50 | -------------------------------------------------------------------------------- /frontend/src/actions/courseActions.js: -------------------------------------------------------------------------------- 1 | import { 2 | COURSE_LIST_REQUEST, 3 | COURSE_LIST_SUCCESS, 4 | COURSE_LIST_FAIL, 5 | SPECIFIC_COURSE_LIST_REQUEST, 6 | SPECIFIC_COURSE_LIST_SUCCESS, 7 | SPECIFIC_COURSE_LIST_FAIL, 8 | COURSE_CREATE_REQUEST, 9 | COURSE_CREATE_SUCCESS, 10 | COURSE_CREATE_FAIL, 11 | COURSE_UPDATE_REQUEST, 12 | COURSE_UPDATE_SUCCESS, 13 | COURSE_UPDATE_FAIL, 14 | COURSE_DELETE_REQUEST, 15 | COURSE_DELETE_SUCCESS, 16 | COURSE_DELETE_FAIL} from '../constants/courseConstants' 17 | 18 | 19 | import axios from 'axios' 20 | 21 | export const listCourses = () => async(dispatch) => { 22 | try { 23 | dispatch({ type: COURSE_LIST_REQUEST}) 24 | 25 | const { data } = await axios.get('/api/course/all') 26 | 27 | dispatch({ 28 | type: COURSE_LIST_SUCCESS, 29 | payload: data, 30 | }) 31 | } 32 | catch (error) { 33 | dispatch ({ 34 | type: COURSE_LIST_FAIL, 35 | payload: error.responce && error.responce.data.message ? 36 | error.responce.data.message : 37 | error.message, 38 | }) 39 | } 40 | } 41 | 42 | export const getSpecificCourses = () => async (dispatch, getState) => { 43 | try { 44 | dispatch({ 45 | type: SPECIFIC_COURSE_LIST_REQUEST, 46 | }) 47 | 48 | const { 49 | teacherLogin: { teacherInfo }, 50 | } = getState() 51 | 52 | const config = { 53 | headers: { 54 | 'Content-Type': 'application/json', 55 | Authorization: `Bearer ${teacherInfo.token}`, 56 | }, 57 | } 58 | 59 | const { data } = await axios.get(`/api/course/specific`, config) 60 | 61 | dispatch({ 62 | type: SPECIFIC_COURSE_LIST_SUCCESS, 63 | payload: data, 64 | }) 65 | 66 | } catch (error) { 67 | const message = 68 | error.response && error.response.data.message 69 | ? error.response.data.message 70 | : error.message 71 | dispatch({ 72 | type: SPECIFIC_COURSE_LIST_FAIL, 73 | payload: message, 74 | }) 75 | } 76 | } 77 | 78 | export const createCourse = (course_name, course_outline, total_units) => async (dispatch, getState) => { 79 | try { 80 | dispatch({ 81 | type: COURSE_CREATE_REQUEST, 82 | }) 83 | 84 | const { 85 | teacherLogin: { teacherInfo }, 86 | } = getState() 87 | 88 | const config = { 89 | headers: { 90 | 'Content-Type': 'application/json', 91 | Authorization: `Bearer ${teacherInfo.token}`, 92 | }, 93 | } 94 | 95 | const { data } = await axios.post(`/api/course/create`, { course_name, course_outline, total_units}, config) 96 | 97 | dispatch({ 98 | type: COURSE_CREATE_SUCCESS, 99 | payload: data, 100 | }) 101 | 102 | } catch (error) { 103 | const message = 104 | error.response && error.response.data.message 105 | ? error.response.data.message 106 | : error.message 107 | dispatch({ 108 | type: COURSE_CREATE_FAIL, 109 | payload: message, 110 | }) 111 | } 112 | } 113 | 114 | export const updateCourse = (course_name, course_outline, total_units, id) => async (dispatch, getState) => { 115 | try { 116 | dispatch({ 117 | type: COURSE_UPDATE_REQUEST, 118 | }) 119 | 120 | const { 121 | teacherLogin: { teacherInfo }, 122 | } = getState() 123 | 124 | const config = { 125 | headers: { 126 | 'Content-Type': 'application/json', 127 | Authorization: `Bearer ${teacherInfo.token}`, 128 | }, 129 | } 130 | 131 | const { data } = await axios.put(`/api/course/update/${id}`, { course_name, course_outline, total_units}, config) 132 | 133 | dispatch({ 134 | type: COURSE_UPDATE_SUCCESS, 135 | payload: data, 136 | }) 137 | 138 | } catch (error) { 139 | const message = 140 | error.response && error.response.data.message 141 | ? error.response.data.message 142 | : error.message 143 | dispatch({ 144 | type: COURSE_UPDATE_FAIL, 145 | payload: message, 146 | }) 147 | } 148 | } 149 | 150 | export const deleteCourse = (id) => async (dispatch, getState) => { 151 | try { 152 | dispatch({ 153 | type: COURSE_DELETE_REQUEST, 154 | }) 155 | 156 | const { 157 | teacherLogin: { teacherInfo }, 158 | } = getState() 159 | 160 | const config = { 161 | headers: { 162 | 'Content-Type': 'application/json', 163 | Authorization: `Bearer ${teacherInfo.token}`, 164 | }, 165 | } 166 | 167 | const { data } = await axios.delete(`/api/course/delete/${id}`, config) 168 | 169 | dispatch({ 170 | type: COURSE_DELETE_SUCCESS, 171 | payload: data, 172 | }) 173 | 174 | } catch (error) { 175 | const message = 176 | error.response && error.response.data.message 177 | ? error.response.data.message 178 | : error.message 179 | dispatch({ 180 | type: COURSE_DELETE_FAIL, 181 | payload: message, 182 | }) 183 | } 184 | } -------------------------------------------------------------------------------- /frontend/src/actions/teacherActions.js: -------------------------------------------------------------------------------- 1 | import { 2 | TEACHER_LOGIN_REQUEST, 3 | TEACHER_LOGIN_SUCCESS, 4 | TEACHER_LOGIN_FAIL, 5 | TEACHER_LOGOUT, 6 | TEACHER_REGISTER_REQUEST, 7 | TEACHER_REGISTER_SUCCESS, 8 | TEACHER_REGISTER_FAIL } from '../constants/teacherConstants' 9 | 10 | import axios from 'axios' 11 | 12 | export const login = (tchr_email, password) => async (dispatch) => { 13 | try { 14 | dispatch({ 15 | type: TEACHER_LOGIN_REQUEST 16 | }) 17 | 18 | const config = { 19 | headers: { 20 | 'Content-type': 'application/json' 21 | } 22 | } 23 | 24 | const { data } = await axios.post('/api/teacher/login', 25 | { tchr_email, password, config }) 26 | 27 | dispatch({ 28 | type: TEACHER_LOGIN_SUCCESS, 29 | payload: data 30 | }) 31 | 32 | localStorage.setItem('teacherInfo', JSON.stringify(data)) 33 | 34 | } 35 | 36 | catch (error) { 37 | dispatch ({ 38 | type: TEACHER_LOGIN_FAIL, 39 | payload: error.response && error.response.data.message ? 40 | error.response.data.message : 41 | error.message, 42 | }) 43 | } 44 | } 45 | 46 | export const logout = () => (dispatch) => { 47 | localStorage.removeItem('teacherInfo') 48 | dispatch({ 49 | type: TEACHER_LOGOUT 50 | }) 51 | } 52 | 53 | export const register = (tchr_name, tchr_email, password, tchr_mobile, tchr_address, tchr_pic) => async (dispatch) => { 54 | try { 55 | dispatch({ 56 | type: TEACHER_REGISTER_REQUEST 57 | }) 58 | 59 | const config = { 60 | headers: { 61 | 'Content-type': 'application/json' 62 | } 63 | } 64 | 65 | const { data } = await axios.post('/api/teacher/register', 66 | { tchr_name, tchr_email, password, tchr_mobile, tchr_address, tchr_pic }, config) 67 | 68 | dispatch({ 69 | type: TEACHER_REGISTER_SUCCESS, 70 | payload: data 71 | }) 72 | 73 | dispatch({ 74 | type: TEACHER_LOGIN_SUCCESS, 75 | payload: data 76 | }) 77 | 78 | localStorage.setItem('teacherInfo', JSON.stringify(data)) 79 | 80 | } 81 | 82 | catch (error) { 83 | dispatch ({ 84 | type: TEACHER_REGISTER_FAIL, 85 | payload: error.response && error.response.data.message ? 86 | error.response.data.message : 87 | error.message, 88 | }) 89 | } 90 | } -------------------------------------------------------------------------------- /frontend/src/assets/img/banner-bg-01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Parthava/learning-management-system/f15b3e8ad8de554187bae265724585f8ef4b7cfe/frontend/src/assets/img/banner-bg-01.jpg -------------------------------------------------------------------------------- /frontend/src/assets/img/banner-bg-02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Parthava/learning-management-system/f15b3e8ad8de554187bae265724585f8ef4b7cfe/frontend/src/assets/img/banner-bg-02.jpg -------------------------------------------------------------------------------- /frontend/src/assets/img/banner-bg-03.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Parthava/learning-management-system/f15b3e8ad8de554187bae265724585f8ef4b7cfe/frontend/src/assets/img/banner-bg-03.jpg -------------------------------------------------------------------------------- /frontend/src/assets/img/banner-img-01.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/src/assets/img/banner-img-02.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/src/assets/img/lists.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/src/assets/img/workspace.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Parthava/learning-management-system/f15b3e8ad8de554187bae265724585f8ef4b7cfe/frontend/src/assets/img/workspace.jpg -------------------------------------------------------------------------------- /frontend/src/components/Alert.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | function Alert({type, children}) { 4 | return ( 5 |
6 |
7 | {children} 8 | 9 |
10 |
11 | ) 12 | } 13 | 14 | export default Alert -------------------------------------------------------------------------------- /frontend/src/components/Banner.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { NavLink } from "react-router-dom"; 3 | 4 | function Banner() { 5 | return ( 6 |
7 |
8 |
9 |
10 |
    11 |
  1. 12 |
  2. 13 |
  3. 14 |
15 |
16 |
17 |
18 |
19 |

20 | Develop Strategies for 21 |
Unlimited Learning 22 |

23 |

24 | Never stop learning. Never stop exploring. Never stop growing. Never stop living. Never stop smiling. 25 |

26 | 27 | 28 | 29 |
30 |
31 |
32 |
33 |
34 |
35 |

36 | Explore various Courses 37 |

38 |

39 | We are offering a bunch of updated courses to make you market-ready. Start your journey now! 40 |

41 | 42 | 43 | 44 |
45 |
46 |
47 |
48 |
49 |
50 |

51 | Test your limits 52 |

53 |

54 | Attending the lectures alone are not enough. Take a step beyond by testing your knowledge with our updated tests. 55 |

56 | 57 | 58 | 59 |
60 |
61 |
62 |
63 | 64 | 65 | Previous 66 | 67 | 68 | 69 | Next 70 | 71 |
72 |
73 |
74 |
75 | ) 76 | } 77 | 78 | export default Banner 79 | -------------------------------------------------------------------------------- /frontend/src/components/CourseBox.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { NavLink } from "react-router-dom"; 3 | import bck from "../assets/img/workspace.jpg"; 4 | 5 | function CourseBox({courseName, courseOutline}) { 6 | return ( 7 |
8 | 9 | courseImage 10 |
11 |
12 |

{courseName}

13 |

{courseOutline}

14 | Enroll Now 15 |
16 |
17 |
18 |
19 | ) 20 | } 21 | 22 | export default CourseBox 23 | -------------------------------------------------------------------------------- /frontend/src/components/CourseForTeacher.jsx: -------------------------------------------------------------------------------- 1 | import React, {useState, useEffect} from 'react' 2 | import { useDispatch, useSelector } from 'react-redux'; 3 | import { updateCourse, deleteCourse } from '../actions/courseActions'; 4 | import { NavLink } from "react-router-dom"; 5 | 6 | function CourseForTeacher({spcfcourses}) { 7 | 8 | const [courseName, setCourseName] = useState('') 9 | const [courseOutline, setCourseOutline] = useState('') 10 | const [courseUnits, setCourseUnits] = useState('') 11 | const [courseId, setCourseId] = useState('') 12 | 13 | const dispatch = useDispatch() 14 | 15 | const submitHandler = (event) => { 16 | event.preventDefault() 17 | dispatch(updateCourse(courseName, courseOutline, courseUnits, courseId)) 18 | setCourseName('') 19 | setCourseOutline('') 20 | setCourseUnits('') 21 | setCourseId('') 22 | } 23 | 24 | const handleDelete = (id) => { 25 | dispatch(deleteCourse(id)) 26 | } 27 | 28 | return ( 29 |
30 | 31 | {spcfcourses.map(spcfcourse => ( 32 | 33 |
34 |
35 | 36 |
{spcfcourse.course_name}
37 |
38 |
39 |
    40 |
  • {spcfcourse.course_outline}
  • 41 |
  • Total Units: {spcfcourse.total_units}
  • 42 |
  • Students: {spcfcourse.total_students}
  • 43 |
44 |
45 |
46 |
47 |

53 |

54 |
55 |
56 |
57 | 58 | ))} 59 | 60 | 99 | 100 |
101 | ) 102 | } 103 | 104 | export default CourseForTeacher 105 | -------------------------------------------------------------------------------- /frontend/src/components/FeatureBox.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { NavLink } from "react-router-dom"; 3 | import bck from "../assets/img/workspace.jpg"; 4 | 5 | function FeatureBox({title, subtitle}) { 6 | return ( 7 |
8 | 9 | eventImage 10 |
11 |
12 |

{title}

13 |

{subtitle}

14 |
15 |
16 |
17 |
18 | ) 19 | } 20 | 21 | export default FeatureBox 22 | -------------------------------------------------------------------------------- /frontend/src/components/Features.jsx: -------------------------------------------------------------------------------- 1 | import React, {useState, useEffect} from 'react'; 2 | import { useDispatch, useSelector } from 'react-redux'; 3 | import { listCourses } from '../actions/courseActions'; 4 | import CourseBox from '../components/CourseBox'; 5 | import { NavLink } from "react-router-dom"; 6 | import bck from "../assets/img/workspace.jpg"; 7 | 8 | function Features() { 9 | 10 | const dispatch = useDispatch() 11 | 12 | const courseList = useSelector(state => state.courseList) 13 | const { loading, error, courses} = courseList 14 | 15 | useEffect(() => { 16 | dispatch(listCourses()) 17 | 18 | },[dispatch]); 19 | 20 | console.log(courses) 21 | 22 | return ( 23 |
24 |
25 |
26 |
27 |
28 | 29 |
30 |
31 |

Make transformations successful

32 |

Get ready to transform yourself from zero to hero with our courses 33 |
Mockup vector created by macrovector - www.freepik.com

34 |
35 |
36 | 37 | 38 | 39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |

Our Popular Courses

47 |
48 |
49 | {courses.map(course => ( 50 | 51 | 55 | 56 | ))} 57 |
58 |
59 |
60 |
61 | ) 62 | } 63 | 64 | export default Features 65 | -------------------------------------------------------------------------------- /frontend/src/components/Footer.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { NavLink } from "react-router-dom"; 3 | 4 | function Footer() { 5 | return ( 6 |
7 | 135 |
136 | ) 137 | } 138 | 139 | export default Footer 140 | -------------------------------------------------------------------------------- /frontend/src/components/Mission.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | function Mission() { 4 | return ( 5 |
6 |
7 |
8 |
9 |

Our Mission

10 |
11 |

Prepare for the future, now!

12 |
13 |
14 |

15 | You are free to learn anything and gather as much knowledge as you want. 16 |

17 |

18 | Our mission is to spread knowledge as much as our capacity allows. Does not matter whoever, whenever and wherever it is being delivered or received. 19 |

20 |
21 |
22 |
23 | ) 24 | } 25 | 26 | export default Mission 27 | -------------------------------------------------------------------------------- /frontend/src/components/Navbar.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useDispatch, useSelector } from 'react-redux'; 3 | import { logout } from '../actions/teacherActions'; 4 | import { NavLink } from "react-router-dom"; 5 | 6 | function Navbar() { 7 | 8 | const dispatch = useDispatch() 9 | 10 | const teacherLogin = useSelector(state => state.teacherLogin) 11 | const {loading, error, teacherInfo} = teacherLogin 12 | 13 | const logoutHandler = () => { 14 | dispatch(logout()) 15 | } 16 | 17 | return ( 18 |
19 | 83 |
84 | ) 85 | } 86 | 87 | export default Navbar; 88 | -------------------------------------------------------------------------------- /frontend/src/components/QsnForTeacher.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { NavLink } from "react-router-dom"; 3 | 4 | function QsnForTeacher({qsnID, qsnText, mark, option1, option2, option3, option4, answer}) { 5 | return ( 6 |
7 | 8 |
9 |
10 |

#1 ({qsnID})

11 |
Mark: {mark}
12 |
13 |
14 |
    15 |
  • {qsnText}
  • 16 |
  • A) {option1}
  • 17 |
  • B) {option2}
  • 18 |
  • C) {option3}
  • 19 |
  • D) {option4}
  • 20 |
  • Answer: {answer}
  • 21 |
22 |
23 |
24 |
25 |

26 |

Delete

27 |
28 |
29 |
30 | 31 | 116 | 117 |
118 | ) 119 | } 120 | 121 | export default QsnForTeacher 122 | -------------------------------------------------------------------------------- /frontend/src/components/Spinner.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | function Spinner() { 4 | return ( 5 |
6 |
7 | Loading... 8 |
9 |
10 | ) 11 | } 12 | 13 | export default Spinner 14 | -------------------------------------------------------------------------------- /frontend/src/components/TeacherNavs.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { NavLink } from "react-router-dom"; 3 | 4 | function TeacherNavs() { 5 | return ( 6 |
7 |
8 | 40 |
41 |
42 | ) 43 | } 44 | 45 | export default TeacherNavs 46 | -------------------------------------------------------------------------------- /frontend/src/components/TeacherTabs.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { NavLink } from "react-router-dom"; 3 | 4 | function TeacherTabs() { 5 | return ( 6 |
7 |
8 | 22 |
23 |
24 | ) 25 | } 26 | 27 | export default TeacherTabs 28 | -------------------------------------------------------------------------------- /frontend/src/components/TestBox.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { NavLink } from "react-router-dom"; 3 | import bck from "../assets/img/workspace.jpg"; 4 | 5 | function TestBox({testID, testName, questions, marks, time, courseName}) { 6 | return ( 7 |
8 | 9 | courseImage 10 |
11 |
12 |

{testName} ({testID})

13 |

For: {courseName}

14 |

Questions: {questions}, Marks: {marks}, Time: {time}mins.

15 | Appear Now 16 |
17 |
18 |
19 |
20 | ) 21 | } 22 | 23 | export default TestBox 24 | -------------------------------------------------------------------------------- /frontend/src/components/TestForTeacher.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { NavLink } from "react-router-dom"; 3 | 4 | function TestForTeacher({testID, testName, questions, marks, time, courseName}) { 5 | return ( 6 |
7 | 8 |
9 |
10 | 11 |
{testName}
12 |
13 |
14 |
    15 |
  • {courseName}
  • 16 |
  • Questions: {questions}
  • 17 |
  • Marks: {marks}, Time: {time}mins.
  • 18 |
  • Add Questions
  • 19 |
20 |
21 |
22 |
23 |

24 |

Delete

25 |
26 |
27 |
28 | 29 | 82 | 83 |
84 | ) 85 | } 86 | 87 | export default TestForTeacher 88 | -------------------------------------------------------------------------------- /frontend/src/constants/courseConstants.js: -------------------------------------------------------------------------------- 1 | export const COURSE_LIST_REQUEST = 'COURSE_LIST_REQUEST' 2 | export const COURSE_LIST_SUCCESS = 'COURSE_LIST_SUCCESS' 3 | export const COURSE_LIST_FAIL = 'COURSE_LIST_FAIL' 4 | 5 | export const SPECIFIC_COURSE_LIST_REQUEST = 'SPECIFIC_COURSE_LIST_REQUEST' 6 | export const SPECIFIC_COURSE_LIST_SUCCESS = 'SPECIFIC_COURSE_LIST_SUCCESS' 7 | export const SPECIFIC_COURSE_LIST_FAIL = 'SPECIFIC_COURSE_LIST_FAIL' 8 | 9 | export const COURSE_CREATE_REQUEST = 'COURSE_CREATE_REQUEST' 10 | export const COURSE_CREATE_SUCCESS = 'COURSE_CREATE_SUCCESS' 11 | export const COURSE_CREATE_FAIL = 'COURSE_CREATE_FAIL' 12 | 13 | export const COURSE_UPDATE_REQUEST = 'COURSE_UPDATE_REQUEST' 14 | export const COURSE_UPDATE_SUCCESS = 'COURSE_UPDATE_SUCCESS' 15 | export const COURSE_UPDATE_FAIL = 'COURSE_UPDATE_FAIL' 16 | 17 | export const COURSE_DELETE_REQUEST = 'COURSE_DELETE_REQUEST' 18 | export const COURSE_DELETE_SUCCESS = 'COURSE_DELETE_SUCCESS' 19 | export const COURSE_DELETE_FAIL = 'COURSE_DELETE_FAIL' -------------------------------------------------------------------------------- /frontend/src/constants/teacherConstants.js: -------------------------------------------------------------------------------- 1 | export const TEACHER_LOGIN_REQUEST = 'TEACHER_LOGIN_REQUEST' 2 | export const TEACHER_LOGIN_SUCCESS = 'TEACHER_LOGIN_SUCCESS' 3 | export const TEACHER_LOGIN_FAIL = 'TEACHER_LOGIN_FAIL' 4 | export const TEACHER_LOGOUT = 'TEACHER_LOGOUT' 5 | 6 | export const TEACHER_REGISTER_REQUEST = 'TEACHER_REGISTER_REQUEST' 7 | export const TEACHER_REGISTER_SUCCESS = 'TEACHER_REGISTER_SUCCESS' 8 | export const TEACHER_REGISTER_FAIL = 'TEACHER_REGISTER_FAIL' -------------------------------------------------------------------------------- /frontend/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /frontend/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import { Provider } from 'react-redux' 4 | import store from './store' 5 | import '../node_modules/bootstrap/dist/css/bootstrap.min.css'; 6 | import '../node_modules/bootstrap-icons/font/bootstrap-icons.css'; 7 | import '../node_modules/bootstrap/dist/js/bootstrap.bundle'; 8 | import './index.css'; 9 | import App from './App'; 10 | import reportWebVitals from './reportWebVitals'; 11 | 12 | ReactDOM.render( 13 | 14 | 15 | , 16 | document.getElementById('root') 17 | ); 18 | 19 | // If you want to start measuring performance in your app, pass a function 20 | // to log results (for example: reportWebVitals(console.log)) 21 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 22 | reportWebVitals(); 23 | -------------------------------------------------------------------------------- /frontend/src/reducers/courseReducers.js: -------------------------------------------------------------------------------- 1 | import { 2 | COURSE_LIST_REQUEST, 3 | COURSE_LIST_SUCCESS, 4 | COURSE_LIST_FAIL, 5 | SPECIFIC_COURSE_LIST_REQUEST, 6 | SPECIFIC_COURSE_LIST_SUCCESS, 7 | SPECIFIC_COURSE_LIST_FAIL, 8 | COURSE_CREATE_REQUEST, 9 | COURSE_CREATE_SUCCESS, 10 | COURSE_CREATE_FAIL, 11 | COURSE_UPDATE_REQUEST, 12 | COURSE_UPDATE_SUCCESS, 13 | COURSE_UPDATE_FAIL, 14 | COURSE_DELETE_REQUEST, 15 | COURSE_DELETE_SUCCESS, 16 | COURSE_DELETE_FAIL} from '../constants/courseConstants' 17 | 18 | export const courseListReducer = (state = { courses: [] }, action) => { 19 | switch (action.type) { 20 | case COURSE_LIST_REQUEST: 21 | return { loading: true, courses: [] } 22 | 23 | case COURSE_LIST_SUCCESS: 24 | return { loading: false, courses: action.payload } 25 | 26 | case COURSE_LIST_FAIL: 27 | return { loading: false, error: action.payload } 28 | 29 | default: 30 | return state 31 | } 32 | } 33 | 34 | export const specificCourseListReducer = (state = { spcfcourses: [] }, action) => { 35 | switch (action.type) { 36 | case SPECIFIC_COURSE_LIST_REQUEST: 37 | return { loading: true, spcfcourses: [] } 38 | 39 | case SPECIFIC_COURSE_LIST_SUCCESS: 40 | return { loading: false, spcfcourses: action.payload } 41 | 42 | case SPECIFIC_COURSE_LIST_FAIL: 43 | return { loading: false, error: action.payload } 44 | 45 | default: 46 | return state 47 | } 48 | } 49 | 50 | export const courseCreateReducer = (state = {}, action) => { 51 | switch (action.type) { 52 | case COURSE_CREATE_REQUEST: 53 | return { 54 | loading: true, 55 | } 56 | case COURSE_CREATE_SUCCESS: 57 | return { 58 | loading: false, 59 | success: true, 60 | course: action.payload, 61 | } 62 | case COURSE_CREATE_FAIL: 63 | return { 64 | loading: false, 65 | error: action.payload, 66 | } 67 | default: 68 | return state 69 | } 70 | } 71 | 72 | export const courseUpdateReducer = (state = {}, action) => { 73 | switch (action.type) { 74 | case COURSE_UPDATE_REQUEST: 75 | return { 76 | loading: true, 77 | } 78 | case COURSE_UPDATE_SUCCESS: 79 | return { 80 | loading: false, 81 | success: true, 82 | updatedCourse: action.payload, 83 | } 84 | case COURSE_UPDATE_FAIL: 85 | return { 86 | loading: false, 87 | error: action.payload, 88 | } 89 | default: 90 | return state 91 | } 92 | } 93 | 94 | export const courseDeleteReducer = (state = {}, action) => { 95 | switch (action.type) { 96 | case COURSE_DELETE_REQUEST: 97 | return { 98 | loading: true, 99 | } 100 | case COURSE_DELETE_SUCCESS: 101 | return { 102 | loading: false, 103 | success: true, 104 | courseDeleted: action.payload, 105 | } 106 | case COURSE_DELETE_FAIL: 107 | return { 108 | loading: false, 109 | error: action.payload, 110 | } 111 | default: 112 | return state 113 | } 114 | } -------------------------------------------------------------------------------- /frontend/src/reducers/teacherReducers.js: -------------------------------------------------------------------------------- 1 | import { 2 | TEACHER_LOGIN_REQUEST, 3 | TEACHER_LOGIN_SUCCESS, 4 | TEACHER_LOGIN_FAIL, 5 | TEACHER_LOGOUT, 6 | TEACHER_REGISTER_REQUEST, 7 | TEACHER_REGISTER_SUCCESS, 8 | TEACHER_REGISTER_FAIL } from '../constants/teacherConstants' 9 | 10 | export const teacherLoginReducer = (state = {}, action) => { 11 | switch (action.type) { 12 | case TEACHER_LOGIN_REQUEST: 13 | return { loading: true } 14 | 15 | case TEACHER_LOGIN_SUCCESS: 16 | return { loading: false, teacherInfo: action.payload } 17 | 18 | case TEACHER_LOGIN_FAIL: 19 | return { loading: false, error: action.payload } 20 | 21 | case TEACHER_LOGOUT: 22 | return {} 23 | 24 | default: 25 | return state 26 | } 27 | } 28 | 29 | export const teacherRegisterReducer = (state = {}, action) => { 30 | switch (action.type) { 31 | case TEACHER_REGISTER_REQUEST: 32 | return { loading: true } 33 | 34 | case TEACHER_REGISTER_SUCCESS: 35 | return { loading: false, teacherInfo: action.payload } 36 | 37 | case TEACHER_REGISTER_FAIL: 38 | return { loading: false, error: action.payload } 39 | 40 | default: 41 | return state 42 | } 43 | } -------------------------------------------------------------------------------- /frontend/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /frontend/src/screens/About.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Footer from '../components/Footer'; 3 | import banner from "../assets/img/banner-img-02.svg"; 4 | 5 | function About() { 6 | return ( 7 |
8 |
9 |
10 |
11 |
12 |

About Us

13 |

14 | We are one of best in the idustry to provide updated learning metarials. We are constantly trying our best 15 | to master the ample technologies to make you market ready as well as to keep us future-ready. 16 |

17 |

18 | Vector illustration credit goes to FreePik website. 19 |

20 |
21 |
22 |
23 | banner 24 |
25 |
26 |
27 |
28 |
29 |
31 | ) 32 | } 33 | 34 | export default About 35 | -------------------------------------------------------------------------------- /frontend/src/screens/AddQuestion.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import TeacherTabs from '../components/TeacherTabs'; 3 | import QsnForTeacher from '../components/QsnForTeacher'; 4 | import Footer from '../components/Footer'; 5 | 6 | function AddQuestion() { 7 | return ( 8 |
9 |
10 |
11 | 12 |
13 |
14 |

Add Question

15 |
16 |
17 | 18 |
19 |
20 | 26 | 27 |
28 |
29 | 30 |
31 |
32 | 33 | 34 |
35 |
36 | 37 |
38 |
39 | 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 | 81 | 82 |
83 |
84 | 85 |
86 | 87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 | 95 |
96 | 97 | 107 | 117 | 118 |
119 |
121 | ) 122 | } 123 | 124 | export default AddQuestion 125 | -------------------------------------------------------------------------------- /frontend/src/screens/Contact.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Footer from '../components/Footer'; 3 | import banner from "../assets/img/banner-img-01.svg"; 4 | 5 | function About() { 6 | return ( 7 |
8 |
9 |
10 |
11 |
12 |

Contact Us!

13 |

We are happy to hear you!

14 |

15 | If you have any query or suggestions for us, please feel free to reach out. We'll get back to you as soon as possible. 16 |

17 |

18 | Vector illustration credit goes to StorySet. 19 |

20 |
21 |
22 |
23 | banner 24 |
25 |
26 |
27 |
28 |
29 |
30 |

Create your success story with us!

31 |

It's never too late to start

32 |

33 | We are here to help you. 34 |

35 |
36 |
37 |
38 |
39 |
40 | 41 |
42 |
43 |
    44 |
  • Center Head
  • 45 |
  • email@address.com
  • 46 |
  • 010-020-0340
  • 47 |
48 |
49 |
50 |
51 |
52 | 53 |
54 |
55 |
    56 |
  • Front Desk
  • 57 |
  • email@address.com
  • 58 |
  • 010-020-0340
  • 59 |
60 |
61 |
62 |
63 |
64 | 65 |
66 |
67 |
    68 |
  • Address
  • 69 |
  • Assam
  • 70 |
  • India
  • 71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 | 79 | 80 |
81 |
82 |
83 |
84 | 85 | 86 |
87 |
88 |
89 |
90 | 91 | 92 |
93 |
94 |
95 |
96 | 97 | 98 |
99 |
100 |
101 |
102 | 103 | 104 |
105 |
106 |
107 |
108 | 109 | 110 |
111 |
112 |
113 | 114 |
115 |
116 |
117 |
118 |
119 |
121 | ) 122 | } 123 | 124 | export default About 125 | -------------------------------------------------------------------------------- /frontend/src/screens/Courses.jsx: -------------------------------------------------------------------------------- 1 | import React, {useState, useEffect} from 'react'; 2 | import { useDispatch, useSelector } from 'react-redux'; 3 | import { listCourses } from '../actions/courseActions'; 4 | import CourseBox from '../components/CourseBox'; 5 | import Footer from '../components/Footer'; 6 | 7 | function Courses() { 8 | 9 | const dispatch = useDispatch() 10 | 11 | const courseList = useSelector(state => state.courseList) 12 | const { loading, error, courses} = courseList 13 | 14 | useEffect(() => { 15 | dispatch(listCourses()) 16 | 17 | },[dispatch]); 18 | 19 | return ( 20 |
21 |
22 |
23 |
24 |
25 |

Courses

26 |

We are offering a wide range of courses

27 |

28 | We are glad to help you achieve your goal. 29 |

30 |

31 | Vector illustration Freepik. 32 |

33 |
34 |
35 |
36 |
37 | 38 |
39 |
40 |
41 |

Browse Courses

42 |
43 |

Choose your path

44 |
45 |
46 |

47 | You are in control of your own fate. Curve your success with us! 48 |

49 |
50 |
51 | 52 |
53 |
54 |
55 |
56 | 57 |
58 |
59 |

Don't just study for certification

60 |

Get ready to become a professional

61 |
62 |
63 | 64 |
65 |
66 |
67 |
68 | 69 |
70 |
71 | 72 | {courses.map(course => ( 73 | 74 | 78 | 79 | ))} 80 | 81 |
82 |
83 |
85 | ) 86 | } 87 | 88 | export default Courses 89 | -------------------------------------------------------------------------------- /frontend/src/screens/Home.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Banner from '../components/Banner'; 3 | // import TeacherTabs from '../components/TeacherTabs'; 4 | import Mission from '../components/Mission'; 5 | import Features from '../components/Features'; 6 | import Footer from '../components/Footer'; 7 | 8 | function Home() { 9 | return ( 10 |
11 | {/* {teacherInfo ? () : ("")} */} 12 | 13 | 14 | 15 |
17 | ) 18 | } 19 | 20 | export default Home 21 | -------------------------------------------------------------------------------- /frontend/src/screens/Notices.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { NavLink } from "react-router-dom"; 3 | import Footer from '../components/Footer'; 4 | import banner from "../assets/img/lists.svg"; 5 | import bck from "../assets/img/workspace.jpg"; 6 | 7 | function About() { 8 | return ( 9 |
10 |
11 |
12 |
13 |
14 |
15 | banner 16 |
17 |
18 |
19 |

Events and Notices

20 |

21 | Keep youself updated. Check out and try to join our various events, seminars, webinars and workshops regularly. 22 |

23 |

24 | Vector illustration credit goes to FreePik website. 25 |

26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | 35 |
36 |
37 |

Make transformations successful

38 |

Get ready to transform yourself from zero to hero with our courses 39 |
Mockup vector created by macrovector - www.freepik.com

40 |
41 |
42 | 43 | 44 | 45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |

Upcoming Events

53 |
54 |
55 |
56 | 57 | eventImage 58 |
59 |
60 |

Office Automation

61 |

MS Office Package, Libre Office, Tally ERP9

62 |
63 |
64 |
65 |
66 |
67 | 68 | eventImage 69 |
70 |
71 |

Web Development

72 |

Designing, Frontend, Backend, Fullstack

73 |
74 |
75 |
76 |
77 |
78 | 79 | eventImage 80 |
81 |
82 |

Networking

83 |

TCP IP, OSI Model, Routing, Topologies

84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
93 | ) 94 | } 95 | 96 | export default About 97 | -------------------------------------------------------------------------------- /frontend/src/screens/StudentDash.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { NavLink } from "react-router-dom"; 3 | import bck from "../assets/img/workspace.jpg"; 4 | import Footer from '../components/Footer'; 5 | 6 | function StudentDash() { 7 | return ( 8 |
9 |
10 |
11 |
12 |
13 |

Student Dashboard

14 | 15 |
16 |
17 | 18 | courseImage 19 |
20 |
21 | Courses 22 |
23 |
24 |
25 |
26 | 27 |
28 | 29 | courseImage 30 |
31 |
32 | Tests 33 |
34 |
35 |
36 |
37 | 38 |
39 | 40 | courseImage 41 |
42 |
43 | Results 44 |
45 |
46 |
47 |
48 | 49 |
50 | 51 | courseImage 52 |
53 |
54 | Profile 55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
66 | ) 67 | } 68 | 69 | export default StudentDash 70 | -------------------------------------------------------------------------------- /frontend/src/screens/StudentLogin.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { NavLink } from "react-router-dom"; 3 | import Footer from '../components/Footer'; 4 | 5 | function StudentLogin() { 6 | return ( 7 |
8 |
9 |
10 |
11 |
12 |

Student Login

13 |
14 |
15 |
16 |
17 | 18 | 19 |
20 |
21 |
22 |
23 | 24 | 25 |
26 |
27 |
28 | 29 |
30 |
31 |
32 |
33 | 34 | 35 | 36 | 37 | 38 | 39 |
40 |
41 |
42 |
43 |
44 |
46 | ) 47 | } 48 | 49 | export default StudentLogin 50 | -------------------------------------------------------------------------------- /frontend/src/screens/StudentRegister.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { NavLink } from "react-router-dom"; 3 | import Footer from '../components/Footer'; 4 | 5 | function StudentRegister() { 6 | return ( 7 |
8 |
9 |
10 |
11 |
12 |

Student Registration

13 |
14 |
15 |
16 |
17 | 18 | 19 |
20 |
21 |
22 |
23 | 24 | 25 |
26 |
27 |
28 |
29 | 30 | 31 |
32 |
33 |
34 |
35 | 36 | 37 |
38 |
39 |
40 |
41 | 42 | 43 |
44 |
45 |
46 |
47 | 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 |
82 |
83 |
84 |
85 |
87 | ) 88 | } 89 | 90 | export default StudentRegister 91 | -------------------------------------------------------------------------------- /frontend/src/screens/TeacherCourses.jsx: -------------------------------------------------------------------------------- 1 | import React, {useState, useEffect} from 'react' 2 | import { useDispatch, useSelector } from 'react-redux'; 3 | import { createCourse, getSpecificCourses } from '../actions/courseActions'; 4 | import Alert from '../components/Alert'; 5 | import Spinner from '../components/Spinner'; 6 | import TeacherNavs from '../components/TeacherNavs'; 7 | import CourseForTeacher from '../components/CourseForTeacher'; 8 | import Footer from '../components/Footer'; 9 | 10 | function TeacherCourses() { 11 | 12 | const [coursename, setCourseName] = useState('') 13 | const [units, setUnits] = useState('') 14 | const [outline, setOutline] = useState('') 15 | 16 | const dispatch = useDispatch() 17 | 18 | const courseCreate = useSelector(state => state.courseCreate) 19 | const {loading, error, course} = courseCreate 20 | 21 | const specificCourseList = useSelector(state => state.specificCourseList) 22 | let {loading:scLoading, error:scError, spcfcourses} = specificCourseList 23 | spcfcourses = spcfcourses.reverse() 24 | 25 | const courseUpdate = useSelector(state => state.courseUpdate) 26 | const {loading:cuLoading, error:cuError, updatedCourse} = courseUpdate 27 | 28 | const courseDelete = useSelector(state => state.courseDelete) 29 | const {loading:dcLoading, error:dcError, courseDeleted} = courseDelete 30 | 31 | //console.log(spcfcourses) 32 | 33 | useEffect(() => { 34 | dispatch(getSpecificCourses()) 35 | }, [dispatch, course, updatedCourse, courseDeleted]) 36 | 37 | const submitHandler = (event) => { 38 | event.preventDefault() 39 | dispatch(createCourse(coursename, outline, units)) 40 | setCourseName('') 41 | setUnits('') 42 | setOutline('') 43 | } 44 | 45 | return ( 46 |
47 |
48 |
49 | 50 |
51 |
52 | {error && {error}} 53 | {course && Course added successfully} 54 |

Manage Courses

55 |
56 |
57 |
58 |
59 | { setCourseName(event.target.value) }} required/> 61 | 62 |
63 |
64 |
65 |
66 | { setUnits(event.target.value) }} required/> 68 | 69 |
70 |
71 |
72 |
73 | { setOutline(event.target.value) }} required/> 75 | 76 |
77 |
78 |
79 | 80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 | 88 | {scLoading ? () : spcfcourses == '' ? ( 89 | 90 |
91 |

92 | 93 | You have not created any course! 94 |

95 |
96 | 97 | ) : ( 98 | 99 |
100 | 101 |
102 | 103 | )} 104 | 105 |
107 | ) 108 | } 109 | 110 | export default TeacherCourses 111 | -------------------------------------------------------------------------------- /frontend/src/screens/TeacherDash.jsx: -------------------------------------------------------------------------------- 1 | import React, {useState, useEffect} from 'react' 2 | import { useSelector } from 'react-redux'; 3 | import { NavLink } from "react-router-dom"; 4 | import bck from "../assets/img/workspace.jpg"; 5 | import Footer from '../components/Footer'; 6 | 7 | function TeacherDash({history}) { 8 | 9 | const teacherLogin = useSelector(state => state.teacherLogin) 10 | const {loading, error, teacherInfo} = teacherLogin 11 | 12 | useEffect(() => { 13 | if(!teacherInfo) { 14 | history.push('/') 15 | } 16 | }, [history, teacherInfo]) 17 | 18 | return ( 19 |
20 |
21 |
22 |
23 |
24 |

Teacher Dashboard

25 | 26 |
27 |
28 | 29 | courseImage 30 |
31 |
32 | Courses 33 |
34 |
35 |
36 |
37 | 38 |
39 | 40 | courseImage 41 |
42 |
43 | Tests 44 |
45 |
46 |
47 |
48 | 49 |
50 | 51 | courseImage 52 |
53 |
54 | Results 55 |
56 |
57 |
58 |
59 | 60 |
61 | 62 | courseImage 63 |
64 |
65 | Profile 66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
77 | ) 78 | } 79 | 80 | export default TeacherDash 81 | -------------------------------------------------------------------------------- /frontend/src/screens/TeacherLogin.jsx: -------------------------------------------------------------------------------- 1 | import React, {useState, useEffect} from 'react' 2 | import { useDispatch, useSelector } from 'react-redux'; 3 | import { login } from '../actions/teacherActions'; 4 | import { NavLink } from "react-router-dom"; 5 | //import Spinner from '../components/Spinner'; 6 | import Alert from '../components/Alert'; 7 | import Footer from '../components/Footer'; 8 | 9 | function TeacherLogin({history}) { 10 | 11 | const [email, setEmail] = useState('') 12 | const [password, setPassword] = useState('') 13 | 14 | const dispatch = useDispatch() 15 | 16 | const teacherLogin = useSelector(state => state.teacherLogin) 17 | const {loading, error, teacherInfo} = teacherLogin 18 | 19 | useEffect(() => { 20 | if(teacherInfo) { 21 | history.push('/teacher_dashboard') 22 | } 23 | }, [history, teacherInfo]) 24 | 25 | const submitHandler = (event) => { 26 | event.preventDefault() 27 | dispatch(login(email, password)) 28 | } 29 | 30 | return ( 31 |
32 |
33 |
34 |
35 |
36 | {error && {error}} 37 |

Teacher Login

38 |
39 |
40 |
41 |
42 | { setEmail(event.target.value) }} required/> 44 | 45 |
46 |
47 |
48 |
49 | { setPassword(event.target.value) }} required/> 51 | 52 |
53 |
54 |
55 | 56 |
57 |
58 |
59 |
60 | 61 | 62 | 63 | 64 | 65 | 66 |
67 |
68 |
69 |
70 |
71 |
73 | ) 74 | } 75 | 76 | export default TeacherLogin 77 | -------------------------------------------------------------------------------- /frontend/src/screens/TeacherRegister.jsx: -------------------------------------------------------------------------------- 1 | import React, {useState, useEffect} from 'react' 2 | import { useDispatch, useSelector } from 'react-redux'; 3 | import { NavLink } from "react-router-dom"; 4 | import { register } from '../actions/teacherActions'; 5 | //import Spinner from '../components/Spinner'; 6 | import Alert from '../components/Alert'; 7 | import Footer from '../components/Footer'; 8 | 9 | function TeacherRegister({history}) { 10 | 11 | const [name, setName] = useState('') 12 | const [email, setEmail] = useState('') 13 | const [phone, setPhone] = useState('') 14 | const [avatar, setAvatar] = useState('') 15 | const [address, setAddress] = useState('') 16 | const [password, setPassword] = useState('') 17 | const [confirmPassword, setConfirmPassword] = useState('') 18 | 19 | const dispatch = useDispatch() 20 | 21 | const teacherRegister = useSelector(state => state.teacherRegister) 22 | const {loading, error, teacherInfo} = teacherRegister 23 | 24 | useEffect(() => { 25 | if(teacherInfo) { 26 | history.push('/teacher_dashboard') 27 | } 28 | }, [history, teacherInfo]) 29 | 30 | const submitHandler = (event) => { 31 | event.preventDefault() 32 | if(password !== confirmPassword) { 33 | alert('Passwords do not match') 34 | } 35 | else { 36 | dispatch(register(name, email, password, phone, address, avatar)) 37 | } 38 | } 39 | 40 | return ( 41 |
42 |
43 |
44 |
45 |
46 | {error && {error}} 47 |

Teacher Registration

48 |
49 |
50 |
51 |
52 | { setName(event.target.value) }} required/> 54 | 55 |
56 |
57 |
58 |
59 | { setEmail(event.target.value) }} required/> 61 | 62 |
63 |
64 |
65 |
66 | { setPhone(event.target.value) }} required/> 68 | 69 |
70 |
71 |
72 |
73 | { setAvatar(event.target.value) }}/> 75 | 76 |
77 |
78 |
79 |
80 | { setAddress(event.target.value) }} required/> 82 | 83 |
84 |
85 |
86 |
87 | { setPassword(event.target.value) }} required/> 89 | 90 |
91 |
92 |
93 |
94 | { setConfirmPassword(event.target.value) }} required/> 96 | 97 |
98 |
99 |
100 | 101 |
102 |
103 |
104 |
105 | 106 | 107 | 108 | 109 | 110 | 111 |
112 |
113 |
114 |
115 |
116 |
118 | ) 119 | } 120 | 121 | export default TeacherRegister 122 | -------------------------------------------------------------------------------- /frontend/src/screens/TeacherTests.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import TeacherTabs from '../components/TeacherTabs'; 3 | import TestForTeacher from '../components/TestForTeacher'; 4 | import Footer from '../components/Footer'; 5 | 6 | function TeacherTests() { 7 | return ( 8 |
9 |
10 |
11 | 12 |
13 |
14 |

Manage Tests

15 |
16 |
17 |
18 |
19 | 20 | 21 |
22 |
23 | 24 |
25 |
26 | 32 | 33 |
34 |
35 | 36 |
37 |
38 | 39 | 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 | 77 | 85 | 86 |
87 |
89 | ) 90 | } 91 | 92 | export default TeacherTests 93 | -------------------------------------------------------------------------------- /frontend/src/screens/Tests.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import TestBox from '../components/TestBox'; 3 | import Footer from '../components/Footer'; 4 | 5 | function Tests() { 6 | return ( 7 |
8 |
9 |
10 |
11 |
12 |

Tests

13 |

Check your improvement by appearing in a test

14 |

15 | Select your course to appear in the test. 16 |

17 |

18 | Vector illustration Freepik. 19 |

20 |
21 |
22 |
23 |
24 | 25 |
26 |
27 |
28 |

Browse Tests

29 |
30 |

Choose your test

31 |
32 |
33 |

34 | There will be multiple choice questions. No negative marking. 35 |

36 |
37 |
38 | 39 |
40 |
41 |
42 |
43 | 44 |
45 |
46 |

Don't just study for certification

47 |

Get ready to become a professional

48 |
49 |
50 | 51 |
52 |
53 |
54 |
55 | 56 |
57 |
58 | 59 | 67 | 75 | 83 | 91 | 92 |
93 |
94 |
96 | ) 97 | } 98 | 99 | export default Tests 100 | -------------------------------------------------------------------------------- /frontend/src/store.js: -------------------------------------------------------------------------------- 1 | import {createStore, combineReducers, applyMiddleware} from 'redux'; 2 | import thunk from 'redux-thunk'; 3 | import {composeWithDevTools} from 'redux-devtools-extension'; 4 | 5 | import { teacherLoginReducer, teacherRegisterReducer } from './reducers/teacherReducers' 6 | import { courseListReducer, courseCreateReducer, 7 | specificCourseListReducer, courseUpdateReducer, 8 | courseDeleteReducer } from './reducers/courseReducers'; 9 | 10 | const reducer = combineReducers({ 11 | teacherLogin: teacherLoginReducer, 12 | teacherRegister: teacherRegisterReducer, 13 | courseList: courseListReducer, 14 | courseCreate: courseCreateReducer, 15 | specificCourseList: specificCourseListReducer, 16 | courseUpdate: courseUpdateReducer, 17 | courseDelete: courseDeleteReducer 18 | }); 19 | 20 | const teacherInfoFromStorage = localStorage.getItem('teacherInfo') 21 | ? JSON.parse(localStorage.getItem('teacherInfo')) 22 | : null 23 | 24 | const initialState = { 25 | teacherLogin: { teacherInfo: teacherInfoFromStorage }, 26 | } 27 | 28 | const middleware = [thunk]; 29 | const store = createStore(reducer, initialState, composeWithDevTools(applyMiddleware(...middleware))) 30 | 31 | export default store; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "online-exam-app", 3 | "version": "1.0.0", 4 | "description": "online exam app", 5 | "main": "server.js", 6 | "type": "module", 7 | "scripts": { 8 | "start": "node backend/server", 9 | "server": "nodemon backend/server", 10 | "client": "npm start --prefix frontend", 11 | "dev": "concurrently \"npm run server\" \"npm run client\"" 12 | }, 13 | "author": "onedotall", 14 | "license": "ISC", 15 | "dependencies": { 16 | "bcryptjs": "^2.4.3", 17 | "dotenv": "^10.0.0", 18 | "express": "^4.17.1", 19 | "express-async-handler": "^1.1.4", 20 | "jsonwebtoken": "^8.5.1", 21 | "mongoose": "^5.12.12" 22 | }, 23 | "devDependencies": { 24 | "concurrently": "^6.2.0", 25 | "nodemon": "^2.0.7" 26 | } 27 | } 28 | --------------------------------------------------------------------------------