├── .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 | You need to enable JavaScript to run this app.
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 |
12 |
13 |
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 | Know More
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 | Explore Now
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 | Get Started
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 |
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 |
{ setCourseName(spcfcourse.course_name)
49 | setCourseOutline(spcfcourse.course_outline)
50 | setCourseUnits(spcfcourse.total_units)
51 | setCourseId(spcfcourse._id)
52 | }}>Update
53 |
{ handleDelete(spcfcourse._id) }} exact className="btn rounded-pill px-4 btn-outline-warning">Delete
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 |
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 |
35 |
36 |
37 | View Our Courses
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 |
8 |
9 |
10 |
11 |
12 |
13 | L
14 | E
15 | M
16 | N
17 | O
18 | S
19 |
20 |
21 | It's a Learning management System with online examination facility to help teachers and students.
22 |
23 |
50 |
51 |
52 |
Quick Links
53 |
54 |
55 |
56 | Courses
57 |
58 |
59 |
60 | Tests
61 |
62 |
63 |
64 | Teacher
65 |
66 |
67 |
68 | Student
69 |
70 |
71 |
72 |
73 |
74 |
Latest Courses
75 |
76 |
77 |
78 | Office Automation
79 |
80 |
81 |
82 | Web Development
83 |
84 |
85 |
86 | Data Analysis
87 |
88 |
89 |
90 | Digital Marketing
91 |
92 |
93 |
94 |
95 |
96 |
For Public
97 |
98 |
99 |
100 | Terms of use
101 |
102 |
103 |
104 | Privacy Policy
105 |
106 |
107 |
108 | Contact
109 |
110 |
111 |
112 | 0n3.a77@gmail.com
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 | © Copyright {(new Date().getFullYear())} One.all Company. All Rights Reserved.
124 |
125 |
126 |
127 |
128 | Designed by One.all
129 |
130 |
131 |
132 |
133 |
134 |
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 |
20 |
21 |
22 |
23 | L
24 | E
25 | M
26 | N
27 | O
28 | S
29 |
30 |
31 |
32 |
33 |
81 |
82 |
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 |
Update
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 |
9 |
10 |
11 |
12 | Dashboard
13 |
14 |
15 |
16 |
17 |
18 | Courses
19 |
20 |
21 |
22 |
23 |
24 | Tests
25 |
26 |
27 |
28 |
29 |
30 | Results
31 |
32 |
33 |
34 |
35 |
36 | Profile
37 |
38 |
39 |
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 |
9 |
10 | Courses
11 |
12 |
13 | Tests
14 |
15 |
16 | Results
17 |
18 |
19 | Profile
20 |
21 |
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 |
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 |
Update
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 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
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 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
107 |
117 |
118 |
119 |
120 |
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 |
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 |
43 |
44 | Center Head
45 | email@address.com
46 | 010-020-0340
47 |
48 |
49 |
50 |
55 |
56 | Front Desk
57 | email@address.com
58 | 010-020-0340
59 |
60 |
61 |
62 |
67 |
68 | Address
69 | Assam
70 | India
71 |
72 |
73 |
74 |
117 |
118 |
119 |
120 |
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 |
84 |
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 |
16 |
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 |
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 |
41 |
42 |
43 | View Our Courses
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
Upcoming Events
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
Office Automation
61 |
MS Office Package, Libre Office, Tally ERP9
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
Web Development
72 |
Designing, Frontend, Backend, Fullstack
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
Networking
83 |
TCP IP, OSI Model, Routing, Topologies
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
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 |
19 |
20 |
21 | Courses
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | Tests
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | Results
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | Profile
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
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 | Email*
19 |
20 |
21 |
22 |
23 |
24 | Password*
25 |
26 |
27 |
28 | Login
29 |
30 |
31 |
32 |
33 |
34 | Teacher Login
35 |
36 |
37 | Register Now
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
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 | Your Name*
19 |
20 |
21 |
22 |
23 |
24 | Your Email*
25 |
26 |
27 |
28 |
29 |
30 | Your Phone*
31 |
32 |
33 |
34 |
35 |
36 | Avatar
37 |
38 |
39 |
40 |
41 |
42 | Your Address*
43 |
44 |
45 |
46 |
47 |
48 | Select Course*
49 | One
50 | Two
51 | Three
52 |
53 | Select Course*
54 |
55 |
56 |
57 |
58 |
59 | Password*
60 |
61 |
62 |
63 |
64 |
65 | Confirm Password*
66 |
67 |
68 |
69 | Register
70 |
71 |
72 |
73 |
74 |
75 | Student Login
76 |
77 |
78 | Teacher Login
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
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 | Course Name*
62 |
63 |
64 |
65 |
66 | { setUnits(event.target.value) }} required/>
68 | Units*
69 |
70 |
71 |
72 |
73 | { setOutline(event.target.value) }} required/>
75 | Outline*
76 |
77 |
78 |
79 | Save Course
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 |
102 |
103 | )}
104 |
105 |
106 |
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 |
30 |
31 |
32 | Courses
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | Tests
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | Results
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 | Profile
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
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 | Email*
45 |
46 |
47 |
48 |
49 | { setPassword(event.target.value) }} required/>
51 | Password*
52 |
53 |
54 |
55 | Login
56 |
57 |
58 |
59 |
60 |
61 | Student Login
62 |
63 |
64 | Register Now
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
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 | Your Name*
55 |
56 |
57 |
58 |
59 | { setEmail(event.target.value) }} required/>
61 | Your Email*
62 |
63 |
64 |
65 |
66 | { setPhone(event.target.value) }} required/>
68 | Your Phone*
69 |
70 |
71 |
72 |
73 | { setAvatar(event.target.value) }}/>
75 | Avatar
76 |
77 |
78 |
79 |
80 | { setAddress(event.target.value) }} required/>
82 | Your Address*
83 |
84 |
85 |
86 |
87 | { setPassword(event.target.value) }} required/>
89 | Password*
90 |
91 |
92 |
93 |
94 | { setConfirmPassword(event.target.value) }} required/>
96 | Confirm Password*
97 |
98 |
99 |
100 | Register
101 |
102 |
103 |
104 |
105 |
106 | Teacher Login
107 |
108 |
109 | Student Login
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
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 | Test Name*
21 |
22 |
23 |
24 |
25 |
26 |
27 | Select Course*
28 | One
29 | Two
30 | Three
31 |
32 | Select Course*
33 |
34 |
35 |
36 |
37 |
38 |
39 | Questions*
40 |
41 |
42 |
43 |
44 |
45 |
46 | Total Marks*
47 |
48 |
49 |
50 |
51 |
52 |
53 | Time in Mins*
54 |
55 |
56 |
57 |
58 | Save Test
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
87 |
88 |
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 |
95 |
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 |
--------------------------------------------------------------------------------