├── user-portal-frontend ├── src │ ├── App.css │ ├── components │ │ ├── templates │ │ │ ├── loginForm │ │ │ │ ├── loginForm.css │ │ │ │ └── loginForm.js │ │ │ ├── HeaderAppBar │ │ │ │ └── HeaderAppBar.js │ │ │ ├── TestDetails │ │ │ │ ├── TestDetailsStudent.js │ │ │ │ ├── TestDetails.js │ │ │ │ ├── UpcomingStudentTestsDetails.js │ │ │ │ └── CompletedTestsDetailsStudent.js │ │ │ ├── QuestionDetails │ │ │ │ └── questionDetails.js │ │ │ └── studentRegisterForm │ │ │ │ └── studentRegisterForm.js │ │ ├── atoms │ │ │ ├── LogoutButton │ │ │ │ └── LogoutButton.js │ │ │ ├── Alertbox │ │ │ │ └── AlertBox.js │ │ │ └── SearchBox │ │ │ │ └── QuestionSearchBox.js │ │ ├── molecues │ │ │ ├── TestView │ │ │ │ ├── QuestionList.js │ │ │ │ ├── Timer.js │ │ │ │ ├── TestQuestion.js │ │ │ │ └── TakeTestStudent.js │ │ │ ├── TestTable │ │ │ │ ├── TestTable.js │ │ │ │ ├── CompletedTestTableStudent.js │ │ │ │ ├── UpcomingTestTableStudent.js │ │ │ │ └── TestTableStudent.js │ │ │ ├── QuestionsTable │ │ │ │ └── QuestionTable.js │ │ │ └── ResultView │ │ │ │ └── TestResultStudent.js │ │ └── pages │ │ │ ├── studentRegisterPage │ │ │ └── studentRegisterPage.js │ │ │ ├── loginPage │ │ │ └── loginPage.js │ │ │ ├── TakeTest │ │ │ └── TestPage.js │ │ │ ├── studentHomepage │ │ │ └── studentHomepage.js │ │ │ └── teacherHomepage │ │ │ └── teacherHomepage.js │ ├── helper │ │ ├── Auth.js │ │ ├── common.js │ │ └── Apis.js │ ├── index.css │ ├── reportWebVitals.js │ ├── redux │ │ ├── actions │ │ │ ├── alertAction.js │ │ │ ├── subjectAction.js │ │ │ ├── registerStudentAction.js │ │ │ ├── loginAction.js │ │ │ ├── teacherTestAction.js │ │ │ ├── takeTestAction.js │ │ │ └── studentTestAction.js │ │ ├── reducers │ │ │ ├── login.js │ │ │ ├── subject.js │ │ │ ├── index.js │ │ │ ├── alert.js │ │ │ ├── taketest.js │ │ │ ├── question.js │ │ │ └── test.js │ │ ├── store.js │ │ └── action-types.js │ ├── index.js │ └── App.js ├── public │ ├── robots.txt │ ├── favicon.ico │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── index.html ├── Dockerfile ├── .gitignore ├── package.json └── README.md ├── backend ├── remove │ └── _update-notifier-last-checked ├── public │ └── index.html ├── models │ ├── test.js │ ├── user.js │ ├── admin.js │ ├── subject.js │ ├── question.js │ ├── answersheet.js │ └── testRegistration.js ├── config │ ├── test.json │ ├── docker.json │ └── default.json ├── routes │ ├── login.js │ ├── adminLogin.js │ ├── public.js │ ├── admin.js │ └── user.js ├── Dockerfile ├── index.js ├── schemas │ ├── admin.js │ ├── testRegistration.js │ ├── subject.js │ ├── user.js │ ├── answersheet.js │ ├── question.js │ └── test.js ├── test │ ├── server.test.js │ └── public.test.js ├── services │ ├── tool.js │ ├── student.js │ ├── connection.js │ ├── adminLogin.js │ ├── teacher.js │ ├── login.js │ ├── register.js │ ├── subject.js │ ├── result.js │ └── passportconf.js ├── package.json └── app.js ├── frontend ├── src │ ├── components │ │ ├── basic │ │ │ ├── Homepage │ │ │ │ ├── homepage.css │ │ │ │ ├── main.jpg │ │ │ │ ├── homepage.jpeg │ │ │ │ └── Homepage.js │ │ │ ├── header │ │ │ │ ├── header.css │ │ │ │ └── header.js │ │ │ └── login │ │ │ │ ├── login.js │ │ │ │ └── login.css │ │ ├── StudentRegister │ │ │ ├── StudentRegisterPage │ │ │ │ ├── studentRegister.css │ │ │ │ └── studentRegister.js │ │ │ └── RegisterForm │ │ │ │ ├── registerform.css │ │ │ │ └── registerform.js │ │ └── Dashboard │ │ │ ├── student.jfif │ │ │ ├── subject.jfif │ │ │ ├── teacher.png │ │ │ ├── AddSubject │ │ │ ├── AddSubject.css │ │ │ └── AddSubject.js │ │ │ ├── AddTeacher │ │ │ ├── AddTeacher.css │ │ │ └── AddTeacher.js │ │ │ ├── studentTable │ │ │ ├── studentTable.css │ │ │ └── studentTable.js │ │ │ ├── subjectTable │ │ │ ├── subjectTable.css │ │ │ └── subjectTable.js │ │ │ ├── teacherTable │ │ │ ├── teacherTable.css │ │ │ └── teacherTable.js │ │ │ ├── Card │ │ │ └── card.js │ │ │ └── Main │ │ │ └── dashboradMain.js │ ├── redux │ │ ├── actions │ │ │ ├── registerStudentAction.js │ │ │ ├── dashboardDetails.js │ │ │ ├── studentDetails.js │ │ │ ├── teacherDetails.js │ │ │ ├── subjectDetails.js │ │ │ └── loginAction.js │ │ ├── constants │ │ │ └── action-types.js │ │ └── reducers │ │ │ ├── studentDetails.js │ │ │ ├── subjectDetails.js │ │ │ ├── teacherDetails.js │ │ │ ├── login.js │ │ │ ├── index.js │ │ │ └── counts.js │ ├── setupTests.js │ ├── services │ │ ├── Auth.js │ │ ├── axiosCalls.js │ │ ├── alert.js │ │ └── Apis.js │ ├── index.css │ ├── reportWebVitals.js │ ├── store.js │ ├── index.js │ ├── App.css │ └── App.js ├── public │ ├── favicon.ico │ └── index.html ├── Dockerfile ├── .gitignore ├── package.json └── README.md ├── .gitattributes ├── docker-compose.yaml └── .gitignore /user-portal-frontend/src/App.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backend/remove/_update-notifier-last-checked: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/src/components/basic/Homepage/homepage.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/src/redux/actions/registerStudentAction.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/src/components/StudentRegister/StudentRegisterPage/studentRegister.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /user-portal-frontend/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abdullahthewebbee/Online-exam-portal-dev/HEAD/frontend/public/favicon.ico -------------------------------------------------------------------------------- /backend/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | ERROR 404 4 | 5 | 6 | ERROR 404 7 | 8 | -------------------------------------------------------------------------------- /user-portal-frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abdullahthewebbee/Online-exam-portal-dev/HEAD/user-portal-frontend/public/favicon.ico -------------------------------------------------------------------------------- /user-portal-frontend/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abdullahthewebbee/Online-exam-portal-dev/HEAD/user-portal-frontend/public/logo192.png -------------------------------------------------------------------------------- /user-portal-frontend/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abdullahthewebbee/Online-exam-portal-dev/HEAD/user-portal-frontend/public/logo512.png -------------------------------------------------------------------------------- /frontend/src/components/Dashboard/student.jfif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abdullahthewebbee/Online-exam-portal-dev/HEAD/frontend/src/components/Dashboard/student.jfif -------------------------------------------------------------------------------- /frontend/src/components/Dashboard/subject.jfif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abdullahthewebbee/Online-exam-portal-dev/HEAD/frontend/src/components/Dashboard/subject.jfif -------------------------------------------------------------------------------- /frontend/src/components/Dashboard/teacher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abdullahthewebbee/Online-exam-portal-dev/HEAD/frontend/src/components/Dashboard/teacher.png -------------------------------------------------------------------------------- /frontend/src/components/basic/Homepage/main.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abdullahthewebbee/Online-exam-portal-dev/HEAD/frontend/src/components/basic/Homepage/main.jpg -------------------------------------------------------------------------------- /frontend/src/components/basic/Homepage/homepage.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abdullahthewebbee/Online-exam-portal-dev/HEAD/frontend/src/components/basic/Homepage/homepage.jpeg -------------------------------------------------------------------------------- /frontend/src/components/Dashboard/AddSubject/AddSubject.css: -------------------------------------------------------------------------------- 1 | .form-class { 2 | background-color: white; 3 | padding: 50px 100px; 4 | } 5 | 6 | h2, .linkbtn { 7 | color:black 8 | } 9 | -------------------------------------------------------------------------------- /frontend/src/components/Dashboard/AddTeacher/AddTeacher.css: -------------------------------------------------------------------------------- 1 | .form-class { 2 | background-color: white; 3 | padding: 50px 100px; 4 | } 5 | 6 | h2, .linkbtn { 7 | color:black 8 | } 9 | -------------------------------------------------------------------------------- /backend/models/test.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose') 2 | var testSchema = require('../schemas/test') 3 | 4 | var testModel = mongoose.model('test',testSchema) 5 | 6 | module.exports = testModel -------------------------------------------------------------------------------- /backend/models/user.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | var userSchema = require('../schemas/user'); 3 | 4 | var userModel = mongoose.model('user', userSchema); 5 | 6 | module.exports = userModel; -------------------------------------------------------------------------------- /backend/models/admin.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | var adminSchema = require('../schemas/admin'); 3 | 4 | var adminModel = mongoose.model('admin', adminSchema); 5 | 6 | module.exports = adminModel; -------------------------------------------------------------------------------- /backend/config/test.json: -------------------------------------------------------------------------------- 1 | { 2 | "mongodb": { 3 | "connectionString": "mongodb://localhost:27017/onlineExamPortalTestDB" 4 | }, 5 | "jwt": { 6 | "secret" : "Examination Portal Application test" 7 | } 8 | } -------------------------------------------------------------------------------- /backend/models/subject.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | var subjectSchema = require('../schemas/subject'); 3 | 4 | var subjectModel = mongoose.model('subject',subjectSchema); 5 | 6 | module.exports = subjectModel; -------------------------------------------------------------------------------- /backend/config/docker.json: -------------------------------------------------------------------------------- 1 | { 2 | "mongodb": { 3 | "connectionString": "mongodb://admin:password@mongodb:27017/online-exam-portal", 4 | "authDB":"admin" 5 | }, 6 | "jwt": { 7 | "secret" : "mysecret" 8 | } 9 | } -------------------------------------------------------------------------------- /backend/models/question.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose') 2 | 3 | var questionSchema = require('../schemas/question') 4 | 5 | var questionModel = mongoose.model('question',questionSchema) 6 | 7 | module.exports = questionModel; -------------------------------------------------------------------------------- /backend/routes/login.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | var loginService = require('../services/login'); 5 | 6 | router.post('/',loginService.userLogin); 7 | 8 | module.exports = router; -------------------------------------------------------------------------------- /backend/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:13-alpine 2 | 3 | WORKDIR /usr/backend 4 | 5 | COPY . . 6 | RUN rm -rf node_modules/ package-lock.json 7 | 8 | RUN npm install nodemon 9 | RUN npm install 10 | 11 | 12 | CMD ["sh", "-c", "npm run start"] -------------------------------------------------------------------------------- /frontend/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:13-alpine 2 | 3 | WORKDIR /usr/frontend 4 | 5 | COPY . . 6 | RUN rm -rf node_modules/ package-lock.json 7 | RUN npm install 8 | 9 | RUN npm run build 10 | 11 | 12 | CMD ["sh", "-c", "npm start"] 13 | 14 | -------------------------------------------------------------------------------- /backend/models/answersheet.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose') 2 | 3 | var answersheetSchema = require('../schemas/answersheet') 4 | 5 | var answersheetModel = mongoose.model('answersheet',answersheetSchema) 6 | 7 | module.exports = answersheetModel; -------------------------------------------------------------------------------- /backend/routes/adminLogin.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | 3 | var router = express.Router() 4 | 5 | var adminLoginService = require('../services/adminLogin'); 6 | 7 | router.post('/',adminLoginService.adminLogin); 8 | 9 | module.exports = router; -------------------------------------------------------------------------------- /backend/routes/public.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | var studentRegister = require('../services/register'); 5 | 6 | router.post('/register',studentRegister.studentRegister); 7 | 8 | module.exports = router; -------------------------------------------------------------------------------- /backend/index.js: -------------------------------------------------------------------------------- 1 | const server = require("./app"); 2 | const PORT = process.env.PORT || 5000 3 | 4 | server.listen(PORT,(err)=>{ 5 | if(err){ 6 | console.log(err); 7 | } 8 | console.log(`Server Started. Server listening to port ${PORT}`); 9 | }); 10 | -------------------------------------------------------------------------------- /backend/models/testRegistration.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | var testRegistrationSchema = require('../schemas/testRegistration'); 3 | 4 | var testRegistrationModel = mongoose.model('testRegistration',testRegistrationSchema); 5 | 6 | module.exports = testRegistrationModel; -------------------------------------------------------------------------------- /frontend/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /user-portal-frontend/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:13-alpine 2 | 3 | WORKDIR /usr/user-portal-frontend 4 | 5 | COPY . . 6 | RUN rm -rf node_modules/ package-lock.json 7 | RUN npm install 8 | RUN npm run build 9 | 10 | # Run the application 11 | CMD ["sh","-c", "npm start"] 12 | 13 | -------------------------------------------------------------------------------- /user-portal-frontend/src/components/templates/loginForm/loginForm.css: -------------------------------------------------------------------------------- 1 | .form-class { 2 | margin:20px; 3 | display: inline-block; 4 | text-align : center; 5 | border : 1px solid black; 6 | border-radius: 10px; 7 | padding : 20px; 8 | } 9 | 10 | .form-title { 11 | font-size: 1.7em; 12 | } -------------------------------------------------------------------------------- /frontend/src/components/Dashboard/studentTable/studentTable.css: -------------------------------------------------------------------------------- 1 | th, td { 2 | border : 1px solid black; 3 | } 4 | 5 | .main { 6 | background-color: aliceblue; 7 | color:black; 8 | padding: 20px 100px; 9 | display: inline-block; 10 | } 11 | 12 | .title { 13 | color:black; 14 | text-align: center; 15 | } -------------------------------------------------------------------------------- /frontend/src/components/Dashboard/subjectTable/subjectTable.css: -------------------------------------------------------------------------------- 1 | th, td { 2 | border : 1px solid black; 3 | } 4 | 5 | .main { 6 | background-color: aliceblue; 7 | color:black; 8 | padding: 20px 100px; 9 | display: inline-block; 10 | } 11 | 12 | .title { 13 | color:black; 14 | text-align: center; 15 | } -------------------------------------------------------------------------------- /frontend/src/components/Dashboard/teacherTable/teacherTable.css: -------------------------------------------------------------------------------- 1 | th, td { 2 | border : 1px solid black; 3 | } 4 | 5 | .main { 6 | background-color: aliceblue; 7 | color:black; 8 | padding: 20px 100px; 9 | display: inline-block; 10 | } 11 | 12 | .title { 13 | color:black; 14 | text-align: center; 15 | } -------------------------------------------------------------------------------- /backend/schemas/admin.js: -------------------------------------------------------------------------------- 1 | var mongoose = require("mongoose"); 2 | 3 | var adminSchema = new mongoose.Schema({ 4 | username : { 5 | type : String, 6 | required : true 7 | }, 8 | password : { 9 | type : String, 10 | required : true 11 | } 12 | },{ 13 | timestamps:{} 14 | }) 15 | 16 | module.exports = adminSchema -------------------------------------------------------------------------------- /frontend/src/redux/constants/action-types.js: -------------------------------------------------------------------------------- 1 | export const ActionTypes = { 2 | LOGIN : "LOGIN", 3 | LOGOUT : "LOGOUT", 4 | DASHBOARD_COUNT : "DASHBOARD_COUNT", 5 | GET_ALL_TEACHER : "GET_ALL_TEACHER", 6 | REMOVE_USER : "REMOVE_USER", 7 | UNBLOCK_USER : "UNBLOCK_USER", 8 | GET_ALL_STUDENT : "GET_ALL_STUDENT", 9 | GET_ALL_SUBJECT : "GET_ALL_SUBJECT" 10 | }; -------------------------------------------------------------------------------- /backend/config/default.json: -------------------------------------------------------------------------------- 1 | { 2 | "mongodb": { 3 | "connectionString": "mongodb://localhost:27017/Online-Exam-Portal" 4 | }, 5 | "jwt": { 6 | "secret" : "Examination Portal Application" 7 | }, 8 | "mail-credentials" : { 9 | "userid" :"", 10 | "password" : "" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /backend/test/server.test.js: -------------------------------------------------------------------------------- 1 | const app = require("../app") 2 | const request = require("supertest"); 3 | const config = require('config'); 4 | const mongoose = require('mongoose'); 5 | 6 | test('Test server is starts',async ()=> { 7 | console.log(config.get('mongodb.connectionString')) 8 | await request(app).get('/') 9 | .expect(200); 10 | mongoose.connection.close(); 11 | }) -------------------------------------------------------------------------------- /backend/services/tool.js: -------------------------------------------------------------------------------- 1 | const bcrypt = require('bcrypt'); 2 | const saltRounds = 10; 3 | 4 | var hashPassword = (pass) => { 5 | return (new Promise( (resolve, reject)=> { 6 | bcrypt.hash(pass, saltRounds) 7 | .then((hash)=>{ 8 | resolve(hash); 9 | }) 10 | .catch((err)=>{ 11 | reject(err) 12 | }) 13 | })) 14 | } 15 | 16 | module.exports = {hashPassword}; -------------------------------------------------------------------------------- /frontend/src/components/StudentRegister/StudentRegisterPage/studentRegister.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Registerform from "../RegisterForm/registerform"; 3 | 4 | const StudentRegister = ()=> { 5 | return ( 6 |
7 |
8 | 9 |
10 |
11 | ); 12 | } 13 | 14 | export default StudentRegister; -------------------------------------------------------------------------------- /frontend/src/services/Auth.js: -------------------------------------------------------------------------------- 1 | class Auth { 2 | constructor(){ 3 | this.token = null; 4 | } 5 | 6 | retriveToken = ()=>{ 7 | return localStorage.getItem('Token'); 8 | } 9 | 10 | storeToken = (t)=>{ 11 | localStorage.setItem('Token',t); 12 | } 13 | 14 | deleteToken = ()=>{ 15 | localStorage.removeItem('Token'); 16 | } 17 | 18 | 19 | } 20 | export default new Auth();; 21 | -------------------------------------------------------------------------------- /backend/schemas/testRegistration.js: -------------------------------------------------------------------------------- 1 | var mongoose = require("mongoose"); 2 | 3 | var testRegistrationSchema = new mongoose.Schema({ 4 | user : { 5 | type : mongoose.Schema.Types.ObjectId, 6 | ref : 'userModel' 7 | }, 8 | test : { 9 | type : mongoose.Schema.Types.ObjectId, 10 | ref : 'testModel' 11 | } 12 | },{ 13 | timestamps:{} 14 | }) 15 | 16 | module.exports = testRegistrationSchema -------------------------------------------------------------------------------- /user-portal-frontend/src/helper/Auth.js: -------------------------------------------------------------------------------- 1 | class Auth { 2 | constructor(){ 3 | this.token = null; 4 | } 5 | 6 | retriveToken = ()=>{ 7 | return localStorage.getItem('Token'); 8 | } 9 | 10 | storeToken = (t)=>{ 11 | localStorage.setItem('Token',t); 12 | } 13 | 14 | deleteToken = ()=>{ 15 | localStorage.removeItem('Token'); 16 | } 17 | 18 | 19 | } 20 | export default new Auth();; 21 | -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /user-portal-frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /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/services/axiosCalls.js: -------------------------------------------------------------------------------- 1 | // import apis from "./Apis"; 2 | // import axios from 'axios'; 3 | 4 | // // export const Get = async (url, options) => { 5 | // // console.log('get api call') 6 | // // return (await axios.get(url, options)); 7 | // // } 8 | 9 | // // export const Post =(p)=>{ 10 | // // return axios({ 11 | // // baseURL : apis.BASE, 12 | // // method:'post', 13 | // // ...p, 14 | // // }) 15 | // // } -------------------------------------------------------------------------------- /user-portal-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 | -------------------------------------------------------------------------------- /user-portal-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 | -------------------------------------------------------------------------------- /user-portal-frontend/src/redux/actions/alertAction.js: -------------------------------------------------------------------------------- 1 | import { ActionTypes } from "../action-types" 2 | 3 | 4 | export const setAlert = (details)=>{ 5 | return async(dispatch) => { 6 | dispatch({ 7 | type:ActionTypes.SET_ALERT, 8 | payload : details 9 | }) 10 | } 11 | } 12 | 13 | export const clearAlert = () => { 14 | return async(dispatch) => { 15 | dispatch({ 16 | type:ActionTypes.CLEAR_ALERT, 17 | payload:"" 18 | }) 19 | } 20 | } -------------------------------------------------------------------------------- /backend/schemas/subject.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose') 2 | 3 | var subjectSchema = new mongoose.Schema({ 4 | name : { 5 | type : String, 6 | required : true, 7 | unique : true 8 | }, 9 | status : { 10 | type : Boolean, 11 | required : true, 12 | default : true 13 | }, 14 | createdBy : { 15 | type : mongoose.Schema.Types.ObjectId, 16 | ref : 'adminModel' 17 | } 18 | }, 19 | { 20 | timestamps : {} 21 | }) 22 | 23 | module.exports = subjectSchema -------------------------------------------------------------------------------- /frontend/src/components/StudentRegister/RegisterForm/registerform.css: -------------------------------------------------------------------------------- 1 | .register-box { 2 | position: absolute; 3 | top: 50%; 4 | left: 50%; 5 | width: 400px; 6 | padding: 40px; 7 | transform: translate(-50%, -50%); 8 | background: rgba(0, 0, 0, 0.5); 9 | box-sizing: border-box; 10 | box-shadow: 0 15px 25px rgba(0, 0, 0, 0.6); 11 | border-radius: 10px; 12 | color: white; 13 | } 14 | 15 | h2 { 16 | color: white; 17 | } 18 | 19 | input { 20 | background: rgba(0, 0, 0, 0.5); 21 | } 22 | -------------------------------------------------------------------------------- /frontend/src/redux/reducers/studentDetails.js: -------------------------------------------------------------------------------- 1 | import { ActionTypes } from "../constants/action-types" 2 | 3 | const initialState = { 4 | list : [], 5 | retrived : false 6 | } 7 | export const getAllStudentReducer = (state=initialState, {type,payload})=> { 8 | switch(type) { 9 | case ActionTypes.GET_ALL_STUDENT: 10 | return { 11 | ...state, 12 | list : payload.studentlist, 13 | retrived : true 14 | }; 15 | default: 16 | return state; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /frontend/src/redux/reducers/subjectDetails.js: -------------------------------------------------------------------------------- 1 | import { ActionTypes } from "../constants/action-types" 2 | 3 | const initialState = { 4 | list : [], 5 | retrived : false 6 | } 7 | export const getAllSubjectsReducer = (state=initialState, {type,payload})=> { 8 | switch(type) { 9 | case ActionTypes.GET_ALL_SUBJECT: 10 | return { 11 | ...state, 12 | list : payload.subjectlist, 13 | retrived : true 14 | }; 15 | default: 16 | return state; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /frontend/src/redux/reducers/teacherDetails.js: -------------------------------------------------------------------------------- 1 | import { ActionTypes } from "../constants/action-types" 2 | 3 | const initialState = { 4 | list : [], 5 | retrived : false 6 | } 7 | export const getAllTeachersReducer = (state=initialState, {type,payload})=> { 8 | switch(type) { 9 | case ActionTypes.GET_ALL_TEACHER: 10 | return { 11 | ...state, 12 | list : payload.teacherlist, 13 | retrived : true 14 | }; 15 | default: 16 | return state; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /frontend/src/store.js: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware, compose} from 'redux'; 2 | import thunk from 'redux-thunk'; 3 | import logger from 'redux-logger'; 4 | import rootReducer from './redux/reducers/index'; 5 | 6 | const initialState={}; 7 | const middleware=[thunk,logger]; 8 | const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; 9 | const store = createStore( 10 | rootReducer, 11 | initialState, 12 | composeEnhancers(applyMiddleware(...middleware)) 13 | ); 14 | 15 | export default store; -------------------------------------------------------------------------------- /user-portal-frontend/src/redux/reducers/login.js: -------------------------------------------------------------------------------- 1 | import { ActionTypes } from "../action-types"; 2 | 3 | const initialState = { 4 | isLoggedIn : false, 5 | userDetails : {} 6 | } 7 | 8 | export const loginUserReducer = (state = initialState,{type,payload})=>{ 9 | switch(type) { 10 | case ActionTypes.LOGIN: 11 | return {...state,isLoggedIn:payload.isLoggedIn,userDetails:payload.userDetails}; 12 | case ActionTypes.LOGOUT: 13 | return initialState; 14 | default : 15 | return state; 16 | } 17 | } -------------------------------------------------------------------------------- /user-portal-frontend/src/redux/store.js: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware, compose} from 'redux'; 2 | import thunk from 'redux-thunk'; 3 | import logger from 'redux-logger'; 4 | import rootReducer from './reducers/index'; 5 | 6 | const initialState = {}; 7 | const middleware = [thunk, logger]; 8 | const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; 9 | const store = createStore( 10 | rootReducer, 11 | initialState, 12 | composeEnhancers(applyMiddleware(...middleware)) 13 | ); 14 | 15 | export default store; -------------------------------------------------------------------------------- /frontend/src/redux/reducers/login.js: -------------------------------------------------------------------------------- 1 | import { ActionTypes } from "../constants/action-types"; 2 | 3 | const initialState = { 4 | isLoggedIn : false, 5 | userDetails : {} 6 | } 7 | 8 | export const loginUserReducer = (state = initialState,{type,payload})=>{ 9 | switch(type) { 10 | case ActionTypes.LOGIN: 11 | return {...state,isLoggedIn:payload.isLoggedIn,userDetails:payload.userDetails}; 12 | case ActionTypes.LOGOUT: 13 | return {...state,isLoggedIn:false,userDetails:{}}; 14 | default : 15 | return state; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /user-portal-frontend/src/redux/reducers/subject.js: -------------------------------------------------------------------------------- 1 | import { ActionTypes } from "../action-types"; 2 | 3 | 4 | const initialState = { 5 | list : [], 6 | retrived : false 7 | } 8 | export const getAllSubjectsReducer = (state=initialState, {type,payload})=> { 9 | switch(type) { 10 | case ActionTypes.GET_ALL_SUBJECT: 11 | return { 12 | ...state, 13 | list : payload.subjectlist, 14 | retrived : true 15 | }; 16 | case ActionTypes.LOGOUT: 17 | return initialState; 18 | default: 19 | return state; 20 | } 21 | } 22 | 23 | -------------------------------------------------------------------------------- /frontend/src/components/basic/header/header.css: -------------------------------------------------------------------------------- 1 | .header-container-2 { 2 | position: absolute; 3 | width: 100%; 4 | } 5 | .logo { 6 | width: 80px; 7 | margin-left: 30px; 8 | margin-top: 30px; 9 | } 10 | .header-container .logo, 11 | .header-container .navigation { 12 | display: inline; 13 | } 14 | .navigation { 15 | list-style: none; 16 | position: relative; 17 | right: 50px; 18 | float: right; 19 | top: 50px; 20 | } 21 | .navigation li { 22 | display: inline; 23 | color: aliceblue; 24 | font-size: 17px; 25 | } 26 | .p { 27 | padding-right: 50px; 28 | } 29 | -------------------------------------------------------------------------------- /user-portal-frontend/src/helper/common.js: -------------------------------------------------------------------------------- 1 | 2 | export const getDatePretty = (str) => { 3 | try { 4 | var date = new Date(Date.parse(str)); 5 | return date.getDate()+"-"+(date.getMonth()+1)+"-"+ date.getFullYear() +" "+date.getHours() +"-"+date.getMinutes(); 6 | } catch (err) { 7 | console.log(err); 8 | } 9 | } 10 | 11 | export const getTimePretty= (seconds) => { 12 | try{ 13 | var h = Math.floor(seconds/3600); 14 | var m = Math.floor((seconds%3600)/60); 15 | // var s = (seconds%60); 16 | return h+":"+m; 17 | } catch (err) { 18 | console.log(err); 19 | } 20 | } -------------------------------------------------------------------------------- /frontend/src/redux/reducers/index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from "redux"; 2 | import { dashBoardCountReducer } from "./counts"; 3 | import { loginUserReducer } from "./login"; 4 | import { getAllStudentReducer } from "./studentDetails"; 5 | import { getAllSubjectsReducer } from "./subjectDetails"; 6 | import { getAllTeachersReducer } from "./teacherDetails"; 7 | 8 | export default combineReducers({ 9 | user : loginUserReducer, 10 | dashboardDetails : dashBoardCountReducer, 11 | teachers : getAllTeachersReducer, 12 | students : getAllStudentReducer, 13 | subjects : getAllSubjectsReducer 14 | }); -------------------------------------------------------------------------------- /user-portal-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 | -------------------------------------------------------------------------------- /user-portal-frontend/src/redux/reducers/index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from "redux"; 2 | import { alertReducer } from "./alert"; 3 | import { loginUserReducer } from "./login"; 4 | import { getQuestionReducer } from "./question"; 5 | import { getAllSubjectsReducer } from "./subject"; 6 | import { TakeTestReducer } from "./taketest"; 7 | import { TestReducer } from "./test"; 8 | 9 | export default combineReducers({ 10 | user : loginUserReducer, 11 | alertDetails : alertReducer, 12 | subjectDetails : getAllSubjectsReducer, 13 | questionDetails : getQuestionReducer, 14 | testDetails : TestReducer, 15 | takeTestDetails : TakeTestReducer 16 | }); -------------------------------------------------------------------------------- /frontend/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import reportWebVitals from './reportWebVitals'; 6 | import { Provider } from 'react-redux'; 7 | import store from './store'; 8 | 9 | ReactDOM.render( 10 | <> 11 | 12 | 13 | 14 | , 15 | document.getElementById('root') 16 | ); 17 | 18 | // If you want to start measuring performance in your app, pass a function 19 | // to log results (for example: reportWebVitals(console.log)) 20 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 21 | reportWebVitals(); 22 | -------------------------------------------------------------------------------- /backend/schemas/user.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | 3 | var userSchema = new mongoose.Schema({ 4 | username : { 5 | type : String, 6 | required : true 7 | }, 8 | email : { 9 | type : String, 10 | required : true, 11 | unique : true 12 | }, 13 | usertype : { 14 | type : String, 15 | enum : ['TEACHER', 'STUDENT'], 16 | required : true 17 | }, 18 | password : { 19 | type : String, 20 | required : true 21 | }, 22 | status : { 23 | type : Boolean, 24 | default : true 25 | }, 26 | createdBy : { 27 | type : mongoose.Schema.Types.ObjectId, 28 | ref : 'adminModel' 29 | } 30 | 31 | }, 32 | { 33 | timestamps:{} 34 | }) 35 | 36 | module.exports = userSchema -------------------------------------------------------------------------------- /user-portal-frontend/src/redux/actions/subjectAction.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import apis from "../../helper/Apis" 3 | import Auth from '../../helper/Auth' 4 | import { ActionTypes } from "../action-types"; 5 | 6 | export const getSubjectDetails = () => { 7 | return async(dispatch) => { 8 | axios.get(apis.BASE + apis.GET_ALL_SUBJECT, { 9 | headers : { 10 | 'Authorization':`Bearer ${Auth.retriveToken()}` 11 | } 12 | }).then(response => { 13 | 14 | if(response.data.success) { 15 | dispatch({ 16 | type: ActionTypes.GET_ALL_SUBJECT, 17 | payload : { 18 | subjectlist : response.data.subjects 19 | } 20 | }) 21 | } 22 | }) 23 | } 24 | } -------------------------------------------------------------------------------- /backend/schemas/answersheet.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose') 2 | 3 | var answersheetSchema = new mongoose.Schema({ 4 | test : { 5 | type : mongoose.Schema.Types.ObjectId, 6 | ref : 'testModel', 7 | required : true 8 | }, 9 | student : { 10 | type : mongoose.Schema.Types.ObjectId, 11 | ref : 'userModel', 12 | required : true 13 | }, 14 | score : { 15 | type : Number, 16 | default : 0, 17 | required : true 18 | }, 19 | answers : [{ 20 | type : String 21 | }], 22 | startTime : { 23 | type : Date 24 | }, 25 | completed : { 26 | type : Boolean, 27 | required : true, 28 | default : false 29 | } 30 | },{ 31 | timestamps : {} 32 | }) 33 | 34 | module.exports = answersheetSchema; -------------------------------------------------------------------------------- /backend/services/student.js: -------------------------------------------------------------------------------- 1 | const userModel = require("../models/user") 2 | 3 | 4 | 5 | var getAllStudents = (req, res, next) => { 6 | userModel.find({usertype:"STUDENT"}, (err, users)=>{ 7 | if(err) { 8 | res.status(500).json({ 9 | success:false, 10 | message : 'Internal server error' 11 | }) 12 | } else { 13 | var students = [] 14 | users.forEach((student)=>{ 15 | students.push({ 16 | "id" : student._id, 17 | "name" : student.username, 18 | "status" : student.status 19 | }) 20 | }) 21 | res.json({ 22 | success : true, 23 | students 24 | }) 25 | } 26 | }) 27 | } 28 | 29 | module.exports = { 30 | getAllStudents 31 | } -------------------------------------------------------------------------------- /user-portal-frontend/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { createRoot } from 'react-dom/client'; 3 | import './index.css'; 4 | import App from './App'; 5 | import { Provider } from 'react-redux'; 6 | import reportWebVitals from './reportWebVitals'; 7 | import store from './redux/store'; 8 | 9 | const container = document.getElementById('root'); 10 | const root = createRoot(container); 11 | root.render( 12 | <> 13 | 14 | 15 | 16 | 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 | -------------------------------------------------------------------------------- /user-portal-frontend/src/components/atoms/LogoutButton/LogoutButton.js: -------------------------------------------------------------------------------- 1 | import { Button } from "@material-ui/core"; 2 | import React from "react"; 3 | import { connect } from "react-redux"; 4 | import { logoutUser } from "../../../redux/actions/loginAction"; 5 | 6 | class LogoutButton extends React.Component { 7 | 8 | constructor(props) { 9 | super(props); 10 | this.state = {} 11 | } 12 | 13 | render(){ 14 | return( 15 | ) 22 | } 23 | } 24 | 25 | const mapStatetoProps = state =>({ 26 | user:state.user 27 | }) 28 | 29 | export default connect(mapStatetoProps,{ 30 | logoutUser 31 | })(LogoutButton) -------------------------------------------------------------------------------- /user-portal-frontend/src/redux/reducers/alert.js: -------------------------------------------------------------------------------- 1 | import { ActionTypes } from "../action-types"; 2 | 3 | const initState = { 4 | isAlert:false, 5 | type:"", 6 | title:"", 7 | message : "" 8 | } 9 | 10 | export const alertReducer = (state = initState,{type,payload})=>{ 11 | switch(type) { 12 | case ActionTypes.SET_ALERT: 13 | return {...state,isAlert:true,type:payload.type,title:payload.title,message:payload.message}; 14 | case ActionTypes.CLEAR_ALERT: 15 | return {...state,isAlert:false,type:"",title:"",message:""}; 16 | case ActionTypes.LOGOUT: 17 | return initState; 18 | case ActionTypes.END_TEST: 19 | return {...state,isAlert:true,type:"info",title:"Test Ended",message:""} 20 | default : 21 | return state; 22 | } 23 | } -------------------------------------------------------------------------------- /frontend/src/App.css: -------------------------------------------------------------------------------- 1 | @import "~antd/dist/antd.css"; 2 | .App { 3 | text-align: center; 4 | } 5 | 6 | .App-logo { 7 | height: 40vmin; 8 | pointer-events: none; 9 | } 10 | 11 | @media (prefers-reduced-motion: no-preference) { 12 | .App-logo { 13 | animation: App-logo-spin infinite 20s linear; 14 | } 15 | } 16 | 17 | .App-header { 18 | background-color: #282c34; 19 | min-height: 100vh; 20 | display: flex; 21 | flex-direction: column; 22 | align-items: center; 23 | justify-content: center; 24 | font-size: calc(10px + 2vmin); 25 | color: white; 26 | } 27 | 28 | .App-link { 29 | color: #61dafb; 30 | } 31 | 32 | @keyframes App-logo-spin { 33 | from { 34 | transform: rotate(0deg); 35 | } 36 | to { 37 | transform: rotate(360deg); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /frontend/src/components/basic/Homepage/Homepage.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import './homepage.css'; 3 | import './homepage.jpeg'; 4 | import { Navigate } from 'react-router-dom'; 5 | import { HomepageHeader } from "../header/header"; 6 | import Login from "../login/login"; 7 | import Auth from '../../../services/Auth'; 8 | import logoImg from './main.jpg'; 9 | 10 | function Homepage(){ 11 | console.log("on home page"); 12 | if(Auth.retriveToken() && Auth.retriveToken()!=='undefined'){ 13 | return (); 14 | } 15 | else { 16 | return ( 17 |
18 |
19 | 20 | 21 |
22 |
23 | ); 24 | } 25 | } 26 | 27 | export default Homepage; 28 | -------------------------------------------------------------------------------- /backend/services/connection.js: -------------------------------------------------------------------------------- 1 | var mongoose = require("mongoose"); 2 | var config = require('config'); 3 | const adminService = require("../services/admin"); 4 | 5 | 6 | //database connection 7 | mongoose.Promise = global.Promise; 8 | let options = { 9 | autoIndex: false, 10 | reconnectTries: 100, 11 | reconnectInterval: 500, 12 | poolSize: 10, 13 | bufferMaxEntries: 0, 14 | useNewUrlParser: true, 15 | useFindAndModify : false 16 | }; 17 | 18 | if(process.env.NODE_ENV==="docker") { 19 | options.authSource = config.get('mongodb.authDB') 20 | } 21 | 22 | mongoose.connect(config.get('mongodb.connectionString'),options).then(()=>{ 23 | console.log("connected to mongoDB"); 24 | adminService.addAdminIfNotFound(); 25 | 26 | }).catch((err)=>{ 27 | console.log("Error connecting to database",err); 28 | }) 29 | 30 | 31 | module.exports=mongoose; -------------------------------------------------------------------------------- /backend/schemas/question.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose') 2 | 3 | var questionSchema = new mongoose.Schema({ 4 | body : { 5 | type : String, 6 | required : true 7 | }, 8 | explanation : { 9 | type : String 10 | }, 11 | options : [ { 12 | type : String, 13 | required : true 14 | }], 15 | subject : { 16 | type : mongoose.Schema.Types.ObjectId, 17 | ref : 'subjectModel', 18 | required : true 19 | }, 20 | answer : { 21 | type : String, 22 | required : true 23 | }, 24 | marks : { 25 | type : Number, 26 | requried : true 27 | }, 28 | status : { 29 | type : Boolean, 30 | required : true, 31 | default : true 32 | }, 33 | createdBy : { 34 | type : mongoose.Schema.Types.ObjectId, 35 | ref : 'userModel' 36 | } 37 | }, 38 | { 39 | timestamps: {} 40 | }) 41 | 42 | module.exports = questionSchema; -------------------------------------------------------------------------------- /frontend/src/services/alert.js: -------------------------------------------------------------------------------- 1 | import { Modal } from 'antd'; 2 | 3 | export default function Alert(s='warning',h,b) { 4 | if(s==='success'){ 5 | return ( 6 | Modal.success({ 7 | title: h, 8 | content:(
{b}
), 9 | }) 10 | ) 11 | } 12 | else if(s==='error'){ 13 | return ( 14 | Modal.error({ 15 | title: h, 16 | content:(
{b}
), 17 | }) 18 | ) 19 | } 20 | else if(s==='warning'){ 21 | return ( 22 | Modal.warning({ 23 | title: h, 24 | content:(
{b}
), 25 | }) 26 | ) 27 | } else { 28 | return ( 29 | Modal.info({ 30 | title: h, 31 | content:(
{b}
) 32 | }) 33 | ) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /frontend/src/redux/reducers/counts.js: -------------------------------------------------------------------------------- 1 | import { ActionTypes } from "../constants/action-types" 2 | 3 | const initialState = { 4 | subjectActive: 0, 5 | subjectBlocked: 0, 6 | studentActive : 0, 7 | studentBlocked : 0, 8 | teacherActive : 0, 9 | teacherBlocked : 0, 10 | retrived : false 11 | } 12 | 13 | export const dashBoardCountReducer = (state = initialState, {type,payload})=>{ 14 | switch(type) { 15 | case ActionTypes.DASHBOARD_COUNT: 16 | return { 17 | ...state, 18 | subjectActive : payload.activeSubject, 19 | subjectBlocked:payload.blockedSubject, 20 | studentActive : payload.activeStudent, 21 | studentBlocked : payload.blockedStudent, 22 | teacherActive : payload.activeTeacher, 23 | teacherBlocked : payload.blockedTeacher, 24 | retrived:payload.retrived 25 | }; 26 | default: 27 | return state; 28 | } 29 | } -------------------------------------------------------------------------------- /user-portal-frontend/src/redux/reducers/taketest.js: -------------------------------------------------------------------------------- 1 | import { ActionTypes } from "../action-types"; 2 | 3 | 4 | const initialState = { 5 | isRetrived: false, 6 | test: {}, 7 | answersheet : {}, 8 | questionid : [] 9 | } 10 | 11 | export const TakeTestReducer = (state = initialState,{type, payload}) => { 12 | switch(type) { 13 | case ActionTypes.START_TEST: 14 | return { 15 | ...state, 16 | isRetrived : true, 17 | test : payload.test, 18 | answersheet : payload.answersheet, 19 | questionid : payload.questions 20 | } 21 | case ActionTypes.SELECT_ANSWER: 22 | var newans = state.answersheet; 23 | newans.answers[payload.index] = payload.ans; 24 | return { 25 | ...state, 26 | answersheet : newans 27 | } 28 | case ActionTypes.END_TEST: 29 | return initialState; 30 | default: 31 | return state; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /user-portal-frontend/src/redux/actions/registerStudentAction.js: -------------------------------------------------------------------------------- 1 | import axios from "axios" 2 | import apis from "../../helper/Apis" 3 | import { ActionTypes } from "../action-types"; 4 | 5 | export const registerStudentAction = (details) => { 6 | return async(dispatch) => { 7 | const response = await axios.post(apis.BASE + apis.STUDENT_REGISTER,details); 8 | if(response.data.success) { 9 | dispatch({ 10 | type:ActionTypes.SET_ALERT, 11 | payload : { 12 | isAlert : true, 13 | title : "Success", 14 | type : "success", 15 | message : response.data.message 16 | } 17 | }) 18 | } 19 | else { 20 | dispatch({ 21 | type:ActionTypes.SET_ALERT, 22 | payload : { 23 | isAlert : true, 24 | title : "Error", 25 | type : "error", 26 | message : response.data.message 27 | } 28 | }) 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /frontend/src/App.js: -------------------------------------------------------------------------------- 1 | import './App.css'; 2 | import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'; 3 | import Homepage from './components/basic/Homepage/Homepage'; 4 | import DashboradMain from './components/Dashboard/Main/dashboradMain'; 5 | import StudentRegister from './components/StudentRegister/StudentRegisterPage/studentRegister'; 6 | import AddTeacher from './components/Dashboard/AddTeacher/AddTeacher'; 7 | import AddSubject from './components/Dashboard/AddSubject/AddSubject'; 8 | function App() { 9 | return ( 10 | 11 | 12 | }/> 13 | } /> 14 | } /> 15 | }/> 16 | }/> 17 | 18 | 19 | ); 20 | } 21 | 22 | export default App; 23 | -------------------------------------------------------------------------------- /user-portal-frontend/src/components/atoms/Alertbox/AlertBox.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Alert from '@material-ui/lab/Alert'; 3 | import AlertTitle from '@material-ui/lab/AlertTitle'; 4 | import { connect } from 'react-redux'; 5 | import { clearAlert } from '../../../redux/actions/alertAction'; 6 | 7 | 8 | class AlertBox extends React.Component { 9 | constructor(props) { 10 | super(props); 11 | this.state = {} 12 | } 13 | 14 | render() { 15 | if(this.props.alertDetails.isAlert) { 16 | return( 17 | 18 | {this.props.alertDetails.title} 19 | {this.props.alertDetails.message} 20 | ) 21 | } 22 | else { 23 | 24 | return(
) 25 | } 26 | } 27 | } 28 | 29 | const mapStatetoProps = state => ({ 30 | alertDetails : state.alertDetails 31 | }) 32 | 33 | export default connect(mapStatetoProps,{ 34 | clearAlert 35 | })(AlertBox); -------------------------------------------------------------------------------- /user-portal-frontend/src/App.js: -------------------------------------------------------------------------------- 1 | import './App.css'; 2 | import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'; 3 | import LoginPage from './components/pages/loginPage/loginPage'; 4 | import StudentHomepage from './components/pages/studentHomepage/studentHomepage'; 5 | import TeacherHomepage from './components/pages/teacherHomepage/teacherHomepage'; 6 | import StudentRegisterPage from './components/pages/studentRegisterPage/studentRegisterPage'; 7 | import TestPage from './components/pages/TakeTest/TestPage'; 8 | 9 | function App() { 10 | return ( 11 | 12 | 13 | }/> 14 | }/> 15 | }/> 16 | }/> 17 | }/> 18 | 19 | 20 | ); 21 | } 22 | 23 | export default App; 24 | -------------------------------------------------------------------------------- /frontend/src/components/basic/header/header.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AppBar from '@material-ui/core/AppBar'; 3 | import Toolbar from '@material-ui/core/Toolbar'; 4 | import { createTheme, makeStyles, ThemeProvider } from '@material-ui/core/styles'; 5 | import './header.css'; 6 | 7 | const useStyles = makeStyles({ 8 | logoimg : { 9 | margin:0, 10 | padding:0, 11 | height:50 12 | }, 13 | name :{ 14 | margin : 20, 15 | color:'#07cfda' 16 | } 17 | }) 18 | 19 | const theme = createTheme({ 20 | palette:{ 21 | primary:{ 22 | main:'#00000077' 23 | } 24 | } 25 | }) 26 | 27 | export const HomepageHeader = (props)=>{ 28 | const classes = useStyles(); 29 | return ( 30 | 31 | 32 | 33 | Logo 34 |
35 | {props.title} 36 |
37 |
38 |
39 |
40 | 41 | ); 42 | } -------------------------------------------------------------------------------- /frontend/src/redux/actions/dashboardDetails.js: -------------------------------------------------------------------------------- 1 | import axios from "axios" 2 | import apis from "../../services/Apis" 3 | import { ActionTypes } from "../constants/action-types" 4 | import Auth from "../../services/Auth" 5 | 6 | export const getDashboardCount = () => { 7 | return async(dispatch) => { 8 | axios.get(apis.BASE + apis.GET_DASHBOARD_COUNT, { 9 | headers : { 10 | 'Authorization':`Bearer ${Auth.retriveToken()}` 11 | } 12 | }).then(response => { 13 | if(response.data.success) { 14 | dispatch({ 15 | type: ActionTypes.DASHBOARD_COUNT, 16 | payload : { 17 | activeStudent: response.data.activeStudent, 18 | activeSubject: response.data.activeSubject, 19 | activeTeacher: response.data.activeTeacher, 20 | blockedStudent: response.data.blockedStudent, 21 | blockedSubject: response.data.blockedSubject, 22 | blockedTeacher: response.data.blockedTeacher, 23 | retrived : true 24 | } 25 | }) 26 | } 27 | }) 28 | } 29 | } -------------------------------------------------------------------------------- /frontend/src/services/Apis.js: -------------------------------------------------------------------------------- 1 | const environment = process.env.NODE_ENV; 2 | 3 | let base_local_url = 'http://localhost:3000'; 4 | let base_backend_url = 'http://localhost:5000'; 5 | if(environment==='docker') { 6 | base_local_url = 'http://user-frontend-app:3000'; 7 | base_backend_url = 'http://backend:5000'; 8 | } 9 | 10 | const apis = { 11 | BASE_LOCAL_URL:base_local_url, 12 | BASE : base_backend_url, 13 | LOGIN : "/api/v1/adminlogin/", 14 | REGISTER_USER : "/api/v1/public/register", 15 | GET_ADMIN_DETAILS : "/api/v1/admin/details", 16 | GET_DASHBOARD_COUNT : "/api/v1/admin/getDashboardCount", 17 | GET_TEACHER_DETAILS : "/api/v1/admin/getAllTeachers", 18 | REMOVE_USER : "/api/v1/admin/removeUser", 19 | UNBLOCK_USER : "/api/v1/admin/unblockUser", 20 | GET_STUDENT_DETAILS : "/api/v1/admin/getAllStudent", 21 | GET_SUBJECT_DETAILS : "/api/v1/admin/getAllSubjects", 22 | REMOVE_SUBJECT : "/api/v1/admin/removeSubject", 23 | UNBLOCK_SUBJECT : "/api/v1/admin/unblockSubject", 24 | ADD_TEACHER : "/api/v1/admin/register", 25 | ADD_SUBJECT : "/api/v1/admin/addSubject" 26 | } 27 | 28 | export default apis; -------------------------------------------------------------------------------- /backend/routes/admin.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | var adminService = require('../services/admin'); 5 | var subjectService = require('../services/subject'); 6 | var teacherService = require('../services/teacher'); 7 | var studentService = require('../services/student'); 8 | 9 | router.get('/details',adminService.adminDetails); 10 | 11 | router.post('/register',adminService.teacherRegister); 12 | router.post('/removeUser',adminService.userRemove); 13 | router.post('/unblockUser',adminService.unblockUser); 14 | router.post('/addSubject', adminService.addSubject); 15 | router.post('/removeSubject',adminService.subjectRemove); 16 | router.post('/unblockSubject',adminService.unblockSubject); 17 | 18 | router.get('/getDashboardCount',adminService.getDashboardCount); 19 | router.get('/getAllSubjects',subjectService.getAllSubject); 20 | router.get('/getSubjectCount',subjectService.getStatusCount); 21 | router.get('/getAllTeachers',teacherService.getAllTeacher); 22 | router.get('/getTeacherStatusCount',teacherService.getTeacherStatusCount); 23 | router.get('/getAllStudent',studentService.getAllStudents); 24 | module.exports = router; -------------------------------------------------------------------------------- /user-portal-frontend/src/components/templates/HeaderAppBar/HeaderAppBar.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { withStyles } from "@material-ui/core/styles"; 3 | import { connect } from "react-redux"; 4 | import { AppBar, Toolbar, Typography } from "@material-ui/core"; 5 | 6 | 7 | const useStyles = (theme)=> ({ 8 | 9 | }) 10 | 11 | class HeaderAppBar extends React.Component { 12 | constructor(props){ 13 | super(props); 14 | this.state = {} 15 | } 16 | 17 | render() { 18 | return( 19 |
20 | 23 | 24 | 25 | {this.props.title} 26 | 27 | 28 | welcome, {this.props.user.userDetails.username} !! 29 | 30 | 31 | 32 |
33 |
34 | ) 35 | } 36 | } 37 | 38 | const mapStatetoProps = state => ({ 39 | user : state.user 40 | }) 41 | 42 | export default withStyles(useStyles)(connect(mapStatetoProps,{})(HeaderAppBar)); -------------------------------------------------------------------------------- /user-portal-frontend/src/redux/action-types.js: -------------------------------------------------------------------------------- 1 | export const ActionTypes = { 2 | LOGIN : "LOGIN", 3 | LOGOUT : "LOGOUT", 4 | SET_ALERT : "SET_ALERT", 5 | CLEAR_ALERT : "CLEAR_ALERT", 6 | GET_USER_DETAILS : "GET_USER_DETAILS", 7 | STUDENT_REGISTER : "STUDENT_REGISTER", 8 | GET_ALL_SUBJECT : "GET_ALL_SUBJECTS", 9 | SEARCH_QUESTION : "SEARCH_QUESTION", 10 | CHANGE_QUESTION_STATUS : "CHANGE_QUESTION_STATUS", 11 | VIEW_QUESTION : 'VIEW_QUESTION', 12 | GET_BACK_TO_SEARCH : "GET_BACK_TO_SEARCH", 13 | UPDATE_QUESTION : "UPDATE_QUESTION", 14 | GET_ALL_TESTS : "GET_ALL_TESTS", 15 | GET_ALL_TESTS_STUDENT : "GET_ALL_TESTS_STUDENT", 16 | CHANGE_TEST_REGISTER : "CHANGE_TEST_REGISTER", 17 | GET_UPCOMING_TESTS_STUDENT : "GET_UPCOMING_TESTS_STUDENT", 18 | VIEW_TEST_DETAILS : "VIEW_TEST_DETAILS", 19 | START_TEST : "START_TEST", 20 | SELECT_ANSWER : "SELECT_ANSWER", 21 | END_TEST : "END_TEST", 22 | GET_ALL_COMPLETED_TEST_STUDENT:"GET_ALL_COMPLETED_TEST_STUDENT", 23 | GET_TEST_RESULT_STUDENT : "GET_TEST_RESULT_STUDENT", 24 | GET_RESULT_QUESTIONS_STUDENTS : "GET_RESULT_QUESTIONS_STUDENTS", 25 | GET_TEST_DETAILS_TEACHER : "GET_TEST_DETAILS_TEACHER", 26 | Go_BACK_ALL_TEST_TEACHER : "GO_BACK_ALL_TEST_TEACHER" 27 | }; -------------------------------------------------------------------------------- /frontend/src/components/Dashboard/Card/card.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/core" 2 | 3 | const useStyles = makeStyles({ 4 | card_main:{ 5 | background:'white', 6 | display:'inline-block', 7 | padding:'20px 0px 10px 10px', 8 | margin:30, 9 | borderRadius:10 10 | }, 11 | name:{ 12 | marginBottom:20, 13 | marginLeft:10, 14 | fontSize:15, 15 | fontWeight:500 16 | }, 17 | d:{ 18 | display:'flex', 19 | color:'darkblue' 20 | }, 21 | d1:{ 22 | paddingTop:10, 23 | fontSize:40, 24 | fontWeight:700 25 | }, 26 | d2:{ 27 | paddingTop:30, 28 | fontSize:20, 29 | fontWeight:600 30 | }, 31 | img:{ 32 | marginLeft:30, 33 | width:120, 34 | height:100 35 | } 36 | }) 37 | 38 | export const MainCard = (props)=>{ 39 | const classes = useStyles(); 40 | return( 41 |
42 |
43 | {props.title} 44 |
45 |
46 | {props.value} 47 | /{props.total} 48 | 49 |
50 |
51 | ) 52 | } -------------------------------------------------------------------------------- /user-portal-frontend/src/redux/reducers/question.js: -------------------------------------------------------------------------------- 1 | import { ActionTypes } from "../action-types"; 2 | 3 | 4 | const initialState = { 5 | list : [], 6 | searched : false, 7 | question : {} 8 | } 9 | 10 | 11 | export const getQuestionReducer = (state=initialState, {type,payload})=> { 12 | switch(type) { 13 | case ActionTypes.SEARCH_QUESTION: 14 | return { 15 | ...state, 16 | list : payload.questionList, 17 | searched : true, 18 | answer : -1, 19 | question : {} 20 | }; 21 | case ActionTypes.CHANGE_QUESTION_STATUS: 22 | var newlist = state.list.map((q)=>( q._id===payload.id ? 23 | { 24 | _id:payload.id, 25 | status:payload.status, 26 | body:q.body 27 | } :q )); 28 | return { 29 | ...state, 30 | list : newlist, 31 | searched : true 32 | } 33 | case ActionTypes.VIEW_QUESTION: 34 | return { 35 | ...state, 36 | question : payload, 37 | searched : false 38 | } 39 | case ActionTypes.GET_BACK_TO_SEARCH: 40 | return { 41 | ...state, 42 | searched : true 43 | } 44 | case ActionTypes.LOGOUT: 45 | return initialState; 46 | default: 47 | return state; 48 | } 49 | } -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "admin-portal-frontend", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@material-ui/core": "^4.12.4", 7 | "@material-ui/icons": "^4.11.3", 8 | "@testing-library/jest-dom": "^5.16.2", 9 | "@testing-library/react": "^12.1.3", 10 | "@testing-library/user-event": "^13.5.0", 11 | "antd": "^4.18.9", 12 | "axios": "^0.26.0", 13 | "react": "^17.0.2", 14 | "react-dom": "^17.0.2", 15 | "react-redux": "^7.2.6", 16 | "react-router-dom": "^6.2.2", 17 | "react-scripts": "5.0.0", 18 | "redux": "^4.1.2", 19 | "redux-logger": "^3.0.6", 20 | "redux-thunk": "^2.4.1", 21 | "web-vitals": "^2.1.4" 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/src/redux/actions/studentDetails.js: -------------------------------------------------------------------------------- 1 | import axios from "axios" 2 | import apis from "../../services/Apis" 3 | import { ActionTypes } from "../constants/action-types" 4 | import Auth from "../../services/Auth" 5 | 6 | 7 | export const getStudentDetails = () => { 8 | return async(dispatch) => { 9 | axios.get(apis.BASE + apis.GET_STUDENT_DETAILS, { 10 | headers : { 11 | 'Authorization':`Bearer ${Auth.retriveToken()}` 12 | } 13 | }).then(response => { 14 | 15 | if(response.data.success) { 16 | dispatch({ 17 | type: ActionTypes.GET_ALL_STUDENT, 18 | payload : { 19 | studentlist : response.data.students 20 | } 21 | }) 22 | } 23 | }) 24 | } 25 | } 26 | 27 | export const StudentToggleStatus = (status,id,callback) => { 28 | var apisuffix = status ? apis.REMOVE_USER : apis.UNBLOCK_USER; 29 | return async() => { 30 | await axios.post(apis.BASE + apisuffix,{ 31 | _id : id 32 | },{ 33 | headers:{ 34 | 'Authorization':`Bearer ${Auth.retriveToken()}` 35 | } 36 | }).then(response => { 37 | if(response.data.success) { 38 | callback(); 39 | } else { 40 | console.log(response.data); 41 | } 42 | }) 43 | } 44 | 45 | } -------------------------------------------------------------------------------- /frontend/src/redux/actions/teacherDetails.js: -------------------------------------------------------------------------------- 1 | import axios from "axios" 2 | import apis from "../../services/Apis" 3 | import { ActionTypes } from "../constants/action-types" 4 | import Auth from "../../services/Auth" 5 | 6 | 7 | export const getTeacherDetails = () => { 8 | return async(dispatch) => { 9 | axios.get(apis.BASE + apis.GET_TEACHER_DETAILS, { 10 | headers : { 11 | 'Authorization':`Bearer ${Auth.retriveToken()}` 12 | } 13 | }).then(response => { 14 | 15 | if(response.data.success) { 16 | dispatch({ 17 | type: ActionTypes.GET_ALL_TEACHER, 18 | payload : { 19 | teacherlist : response.data.teachers 20 | } 21 | }) 22 | } 23 | }) 24 | } 25 | } 26 | 27 | export const TeacherToggleStatus = (status,id,callback) => { 28 | var apisuffix = status ? apis.REMOVE_USER : apis.UNBLOCK_USER; 29 | return async() => { 30 | await axios.post(apis.BASE + apisuffix,{ 31 | _id : id 32 | },{ 33 | headers:{ 34 | 'Authorization':`Bearer ${Auth.retriveToken()}` 35 | } 36 | }).then(response => { 37 | if(response.data.success) { 38 | callback(); 39 | } else { 40 | console.log(response.data); 41 | } 42 | }) 43 | } 44 | 45 | } -------------------------------------------------------------------------------- /frontend/src/redux/actions/subjectDetails.js: -------------------------------------------------------------------------------- 1 | import axios from "axios" 2 | import apis from "../../services/Apis" 3 | import { ActionTypes } from "../constants/action-types" 4 | import Auth from "../../services/Auth" 5 | 6 | 7 | export const getSubjectDetails = () => { 8 | return async(dispatch) => { 9 | axios.get(apis.BASE + apis.GET_SUBJECT_DETAILS, { 10 | headers : { 11 | 'Authorization':`Bearer ${Auth.retriveToken()}` 12 | } 13 | }).then(response => { 14 | 15 | if(response.data.success) { 16 | dispatch({ 17 | type: ActionTypes.GET_ALL_SUBJECT, 18 | payload : { 19 | subjectlist : response.data.subjects 20 | } 21 | }) 22 | } 23 | }) 24 | } 25 | } 26 | 27 | export const SubjectToggleStatus = (status,id,callback) => { 28 | var apisuffix = status ? apis.REMOVE_SUBJECT : apis.UNBLOCK_SUBJECT; 29 | return async() => { 30 | await axios.post(apis.BASE + apisuffix,{ 31 | _id : id 32 | },{ 33 | headers:{ 34 | 'Authorization':`Bearer ${Auth.retriveToken()}` 35 | } 36 | }).then(response => { 37 | if(response.data.success) { 38 | console.log(response.data); 39 | callback(); 40 | } else { 41 | console.log(response.data); 42 | } 43 | }) 44 | } 45 | 46 | } -------------------------------------------------------------------------------- /backend/schemas/test.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose') 2 | 3 | var testSchema = new mongoose.Schema({ 4 | title : { 5 | type : String, 6 | required : true 7 | }, 8 | subjects : [{ 9 | type : mongoose.Schema.Types.ObjectId, 10 | ref : 'subjectModel' 11 | }], 12 | questions : [{ 13 | type : mongoose.Schema.Types.ObjectId, 14 | ref : 'questionModel' 15 | }], 16 | maxmarks : { 17 | type : Number, 18 | required : true 19 | }, 20 | queTypes : [{ 21 | type : Number 22 | }], 23 | startTime : { 24 | type : Date 25 | }, 26 | endTime : { 27 | type : Date, 28 | required : true 29 | }, 30 | duration : { 31 | type : Number, 32 | required : true 33 | }, 34 | regStartTime : { 35 | type : Date, 36 | required : true 37 | }, 38 | regEndTime : { 39 | type : Date, 40 | required : true 41 | }, 42 | resultTime : { 43 | type : Date, 44 | required : true 45 | }, 46 | status : { 47 | type : String, 48 | enum : ['CREATED','REGISTRATION_STARTED','REGISTRATION_COMPLETE','TEST_STARTED','TEST_COMPLETE','RESULT_DECLARED','CANCELLED'], 49 | default : 'CREATED' 50 | }, 51 | createdBy : { 52 | type : mongoose.Schema.Types.ObjectId, 53 | ref : 'userModel', 54 | required : true 55 | } 56 | }, 57 | { 58 | timestamps : {} 59 | }) 60 | 61 | module.exports = testSchema -------------------------------------------------------------------------------- /user-portal-frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "user-portal-frontend", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@material-ui/core": "^4.12.4", 7 | "@material-ui/icons": "^4.11.3", 8 | "@material-ui/lab": "^4.0.0-alpha.61", 9 | "@material-ui/pickers": "^3.3.10", 10 | "@testing-library/jest-dom": "^5.16.5", 11 | "@testing-library/react": "^13.3.0", 12 | "@testing-library/user-event": "^13.5.0", 13 | "axios": "^0.27.2", 14 | "react": "^18.2.0", 15 | "react-dom": "^18.2.0", 16 | "react-redux": "^8.0.2", 17 | "react-router-dom": "^6.3.0", 18 | "react-scripts": "5.0.1", 19 | "redux": "^4.2.0", 20 | "redux-logger": "^3.0.6", 21 | "redux-thunk": "^2.4.1", 22 | "web-vitals": "^2.1.4" 23 | }, 24 | "scripts": { 25 | "start": "react-scripts start", 26 | "build": "react-scripts build", 27 | "test": "react-scripts test", 28 | "eject": "react-scripts eject" 29 | }, 30 | "eslintConfig": { 31 | "extends": [ 32 | "react-app", 33 | "react-app/jest" 34 | ] 35 | }, 36 | "browserslist": { 37 | "production": [ 38 | ">0.2%", 39 | "not dead", 40 | "not op_mini all" 41 | ], 42 | "development": [ 43 | "last 1 chrome version", 44 | "last 1 firefox version", 45 | "last 1 safari version" 46 | ] 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /user-portal-frontend/src/components/templates/TestDetails/TestDetailsStudent.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { withStyles } from "@material-ui/core/styles"; 3 | import { connect } from "react-redux"; 4 | import { getAllTestStudentAction } from "../../../redux/actions/studentTestAction"; 5 | import TestTableStudent from "../../molecues/TestTable/TestTableStudent"; 6 | 7 | const useStyles = (theme)=> ({ 8 | testDetails : { 9 | margin:'20px', 10 | display: 'inline-block', 11 | textAlign : 'center', 12 | }, 13 | testTitle : { 14 | fontSize : '1.7em', 15 | textAlign : 'center', 16 | margin : '20px' 17 | } 18 | }) 19 | 20 | class TestDetailsStudent extends React.Component { 21 | constructor(props){ 22 | super(props); 23 | this.state = {} 24 | } 25 | 26 | render() { 27 | if(this.props.testDetails.retrived) { 28 | return(
29 |
All Tests
30 | 31 |
) 32 | } 33 | else { 34 | this.props.getAllTestStudentAction(); 35 | return (
) 36 | } 37 | } 38 | } 39 | 40 | const mapStatetoProps = state => ({ 41 | user : state.user, 42 | testDetails : state.testDetails 43 | }) 44 | 45 | export default withStyles(useStyles)(connect(mapStatetoProps,{ 46 | getAllTestStudentAction 47 | })(TestDetailsStudent)); -------------------------------------------------------------------------------- /backend/services/adminLogin.js: -------------------------------------------------------------------------------- 1 | const jwt = require('jsonwebtoken') 2 | 3 | var config = require('config') 4 | 5 | var passport = require('./passportconf') 6 | 7 | var adminLogin = (req, res, next) => { 8 | 9 | req.check('username', 'Invalid username').notEmpty() 10 | req.check('password', 'Invalid password').isLength({min:4, max:20}); 11 | 12 | var errors = req.validationErrors() 13 | 14 | if(errors) { 15 | res.json({ 16 | success : false, 17 | message : 'Invalid inputs', 18 | errors : errors 19 | }) 20 | } 21 | else { 22 | passport.authenticate('admin-login', {session : false}, (err, admin, info)=> { 23 | if(err || !admin) { 24 | res.json(info); 25 | } 26 | else { 27 | req.login({_id:admin._id}, {session : false}, (err)=>{ 28 | if(err) { 29 | res.json({ 30 | success : false, 31 | message : 'server error' 32 | }) 33 | } 34 | 35 | var token = jwt.sign({_id: admin._id}, config.get('jwt.secret'), {expiresIn: '1d'}); 36 | res.json({ 37 | success : true, 38 | message : 'login successful', 39 | admin : { 40 | username : admin.username, 41 | _id : admin._id 42 | }, 43 | token : token 44 | }) 45 | }) 46 | } 47 | })(req, res, next); 48 | } 49 | } 50 | 51 | module.exports = { adminLogin }; -------------------------------------------------------------------------------- /backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Chintan Golakiya", 3 | "bugs": { 4 | "url": "https://github.com//backend/issues" 5 | }, 6 | "dependencies": { 7 | "app-root-path": "3.1.0", 8 | "bcrypt": "5.1.0", 9 | "bcryptjs": "2.4.3", 10 | "body-parser": "1.20.2", 11 | "config": "3.3.9", 12 | "cors": "2.8.5", 13 | "exceljs": "4.3.0", 14 | "express": "^4.17.1", 15 | "express-fileupload": "^1.1.5", 16 | "express-session": "^1.17.3", 17 | "express-validator": "^5.3.1", 18 | "helmet": "6.1.5", 19 | "http-errors": "2.0.0", 20 | "mongoose": "5.6.0", 21 | "morgan": "1.10.0", 22 | "multer": "1.4.5-lts.1", 23 | "nodemailer": "6.9.1", 24 | "passport": "0.6.0", 25 | "passport-jwt": "4.0.1", 26 | "passport-local": "1.0.0", 27 | "xlsx": "0.18.5" 28 | }, 29 | "description": "backend logic of the project using Mongo DB, Node js", 30 | "devDependencies": { 31 | "jest": "29.5.0", 32 | "supertest": "6.3.3" 33 | }, 34 | "homepage": "https://github.com//backend#readme", 35 | "jest": { 36 | "testPathIgnorePatterns": [ 37 | "models", 38 | "routes", 39 | "schemas", 40 | "services" 41 | ] 42 | }, 43 | "license": "MIT", 44 | "main": "index.js", 45 | "name": "examportalbackend", 46 | "repository": { 47 | "type": "git", 48 | "url": "git+https://github.com//backend.git" 49 | }, 50 | "scripts": { 51 | "start": "nodemon", 52 | "test": "jest" 53 | }, 54 | "version": "1.0.0" 55 | } 56 | -------------------------------------------------------------------------------- /user-portal-frontend/src/components/molecues/TestView/QuestionList.js: -------------------------------------------------------------------------------- 1 | 2 | import { withStyles } from "@material-ui/styles"; 3 | import React from "react"; 4 | import CheckCircleIcon from '@material-ui/icons/CheckCircle'; 5 | import RadioButtonUncheckedIcon from '@material-ui/icons/RadioButtonUnchecked'; 6 | 7 | const useStyles = (theme) => ({ 8 | main : { 9 | padding : "20px", 10 | border : "1px solid", 11 | borderRadius : "20px" 12 | }, 13 | li : { 14 | padding : "5px", 15 | height: "30px", 16 | display : "inline-block", 17 | border : "1px solid", 18 | width : "60px", 19 | textAlign : "left" 20 | } 21 | }) 22 | 23 | class QuestionList extends React.Component { 24 | constructor(props) { 25 | super(props); 26 | this.state = { 27 | answers:this.props.answers 28 | } 29 | } 30 | 31 | render() { 32 | if(this.state.answers !== undefined){ 33 | if(this.state.answers.length > 0 ) { 34 | return (
35 | {this.state.answers.map((q,index) => ( 36 |
(this.props.callback(index,this.props.obj))} className={this.props.classes.li}> 37 |   {q!==null ? :} 38 |   {index+1} 39 |
40 | ))} 41 |
) 42 | } 43 | } else { 44 | return (
No questions in test
); 45 | } 46 | } 47 | } 48 | 49 | export default withStyles(useStyles)(QuestionList); 50 | 51 | -------------------------------------------------------------------------------- /backend/services/teacher.js: -------------------------------------------------------------------------------- 1 | const userModel = require("../models/user") 2 | 3 | 4 | 5 | var getAllTeacher = (req, res, next) => { 6 | userModel.find({usertype:"TEACHER"}, (err, users)=>{ 7 | if(err) { 8 | res.status(500).json({ 9 | success:false, 10 | message : 'Internal server error' 11 | }) 12 | } else { 13 | var teachers = [] 14 | users.forEach((teacher)=>{ 15 | teachers.push({ 16 | "id" : teacher._id, 17 | "name" : teacher.username, 18 | "status" : teacher.status 19 | }) 20 | }) 21 | res.json({ 22 | success : true, 23 | teachers 24 | }) 25 | } 26 | }) 27 | } 28 | 29 | var getTeacherStatusCount = (req,res,next) => { 30 | userModel.aggregate( 31 | [ 32 | {$match:{usertype:"TEACHER"}}, 33 | {$group: {_id:"$status",count:{$sum:1}} } 34 | ] 35 | ) 36 | .then((result)=>{ 37 | var trueCount = 0 38 | var falseCount = 0 39 | result.forEach((x)=>{ 40 | if(x._id == true) { 41 | trueCount = x.count 42 | } 43 | if(x._id == false) { 44 | falseCount = x.count 45 | } 46 | }) 47 | res.json({ 48 | success:true, 49 | active : trueCount, 50 | blocked : falseCount 51 | }) 52 | }) 53 | .catch((err)=>{ 54 | console.log(err) 55 | res.status(500).json({ 56 | success:false, 57 | message:'Internal server error' 58 | }) 59 | }) 60 | } 61 | 62 | module.exports = { 63 | getTeacherStatusCount, 64 | getAllTeacher 65 | } -------------------------------------------------------------------------------- /user-portal-frontend/src/helper/Apis.js: -------------------------------------------------------------------------------- 1 | const environment = process.env.NODE_ENV; 2 | let base_local_url = 'http://localhost:3001'; 3 | let base_backend_url = 'http://localhost:5000'; 4 | if(environment==='docker') { 5 | base_local_url = 'http://user-frontend-app:3001'; 6 | base_backend_url = 'http://backend:5000'; 7 | } 8 | 9 | const apis = { 10 | BASE_LOCAL_URL:base_local_url, 11 | BASE : base_backend_url, 12 | LOGIN : "/api/v1/login", 13 | GET_USER_DETAILS: "/api/v1/user/details", 14 | STUDENT_REGISTER: "/api/v1/public/register", 15 | GET_ALL_SUBJECT: "/api/v1/user/getAllSubjects", 16 | ADD_QUESTION : '/api/v1/user/addQuestion', 17 | SEARCH_QUESTION : '/api/v1/user/searchQuestion', 18 | CHANGE_QUESTION_STATUS : '/api/v1/user/changeQuestionStatus', 19 | GET_QUESTION_BY_ID:'/api/v1/user/getQuestionAnswer', 20 | GET_ANSWER : '/api/v1/user/getAnswer', 21 | UPDATE_QUESTION : '/api/v1/user/updateQuestion', 22 | CREATE_QUESTION : '/api/v1/user/createTest', 23 | GET_ALL_TEST : '/api/v1/user/getAllTest', 24 | GET_ALL_TEST_STUDNET:'/api/v1/user/getAllTestStudent', 25 | STUDENT_TEST_REGISTER : '/api/v1/user/testRegistration', 26 | GET_UPCOMING_TESTS_STUDENT : '/api/v1/user/getUpcomingTests', 27 | GET_TEST_DETAILS_BY_ID : '/api/v1/user/getTestById', 28 | START_TEST : '/api/v1/user/startTest', 29 | GET_QUESTIONS_TAKETEST : '/api/v1/user/getQuenStarttime', 30 | SAVE_ANSWER : '/api/v1/user/saveAnswer', 31 | END_TEST : '/api/v1/user/endTest', 32 | GET_ALL_COMPLETED_TEST_STUDENT : '/api/v1/user/getAllCompletedTest', 33 | GET_TEST_RESULT_STUDENT:'/api/v1/user/getResultMainDetailsByTestId', 34 | GET_RESULT_QUESTIONS_STUDENTS : '/api/v1/user/getQuestionAnswerByIds' 35 | } 36 | 37 | export default apis; -------------------------------------------------------------------------------- /backend/test/public.test.js: -------------------------------------------------------------------------------- 1 | const app = require("../app") 2 | const request = require("supertest"); 3 | const mongoose = require('mongoose'); 4 | const userModel = require('../models/user'); 5 | 6 | test('Student Register',async () => { 7 | await userModel.findOneAndRemove({'email':'testemail@ep.com'}); 8 | await request(app).post('/api/v1/public/register').send({ 9 | 'username':'testuser', 10 | 'email':'testemail@ep.com', 11 | 'password':'randomvalid' 12 | }).expect(200) 13 | .then(res=>( 14 | expect(res.body).toEqual({ 15 | success : true, 16 | message : 'Profile created successfully!' 17 | }))) 18 | 19 | }); 20 | 21 | test('Already added email in Studnet Register', async ()=> { 22 | await request(app).post('/api/v1/public/register').send({ 23 | 'username':'testuser', 24 | 'email':'testemail@ep.com', 25 | 'password':'randomvalid' 26 | }).expect(400) 27 | .then(res=>( 28 | expect(res.body).toEqual({ 29 | success : false, 30 | message : 'This email is already exists!' 31 | }))) 32 | mongoose.connection.close(); 33 | }) 34 | 35 | test('small password in student register', async ()=> { 36 | await request(app).post('/api/v1/public/register').send({ 37 | 'username':'testuser', 38 | 'email':'testemail@ep.com', 39 | 'password':'ran' 40 | }).expect(400) 41 | .then(res=>{ 42 | 43 | expect(res.body).toEqual(expect.objectContaining({ 44 | success : false, 45 | message : 'Invalid inputs' 46 | })) 47 | expect(res.body.errors).toHaveLength(1); 48 | expect(res.body.errors[0]).toEqual(expect.objectContaining({ 49 | param : 'password', 50 | msg : 'Invalid Password' 51 | })) 52 | }) 53 | mongoose.connection.close(); 54 | }) -------------------------------------------------------------------------------- /user-portal-frontend/src/components/templates/TestDetails/TestDetails.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { withStyles } from "@material-ui/core/styles"; 3 | import { connect } from "react-redux"; 4 | import { getAllTestAction } from "../../../redux/actions/teacherTestAction"; 5 | import TestTable from "../../molecues/TestTable/TestTable"; 6 | import { Button } from "@material-ui/core"; 7 | import { goBackToAllTest } from "../../../redux/actions/teacherTestAction"; 8 | import ViewTest from "../CreateTestForm/ViewTest"; 9 | 10 | const useStyles = (theme)=> ({ 11 | testDetails : { 12 | margin:'20px', 13 | display: 'inline-block', 14 | textAlign : 'center', 15 | }, 16 | testTitle : { 17 | fontSize : '1.7em', 18 | textAlign : 'center', 19 | margin : '20px' 20 | } 21 | }) 22 | 23 | class TestDetails extends React.Component { 24 | constructor(props){ 25 | super(props); 26 | this.state = { 27 | } 28 | } 29 | 30 | render() { 31 | if(this.props.testDetails.searched === true) { 32 | return(
33 | 34 | 35 |
) 36 | } 37 | if(this.props.testDetails.retrived === false) { 38 | this.props.getAllTestAction(); 39 | return (
) 40 | } else { 41 | return(
42 |
All Tests
43 | 44 |
) 45 | } 46 | } 47 | } 48 | 49 | const mapStatetoProps = state => ({ 50 | user : state.user, 51 | testDetails : state.testDetails 52 | }) 53 | 54 | export default withStyles(useStyles)(connect(mapStatetoProps,{ 55 | getAllTestAction, 56 | goBackToAllTest 57 | })(TestDetails)); -------------------------------------------------------------------------------- /user-portal-frontend/src/components/templates/TestDetails/UpcomingStudentTestsDetails.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { withStyles } from "@material-ui/core/styles"; 3 | import { connect } from "react-redux"; 4 | import { getUpcomingTestsStudentAction } from "../../../redux/actions/studentTestAction"; 5 | import UpcomingTestTableStudent from "../../molecues/TestTable/UpcomingTestTableStudent"; 6 | import TakeTestStudent from "../../molecues/TestView/TakeTestStudent"; 7 | 8 | const useStyles = (theme)=> ({ 9 | testDetails : { 10 | margin:'20px', 11 | display: 'inline-block', 12 | textAlign : 'center', 13 | }, 14 | testTitle : { 15 | fontSize : '1.7em', 16 | textAlign : 'center', 17 | margin : '20px' 18 | } 19 | }) 20 | 21 | class UpcomingStudentTestsDetails extends React.Component { 22 | constructor(props){ 23 | super(props); 24 | this.state = {} 25 | } 26 | 27 | render() { 28 | if(this.props.testDetails.upcomingTestRetrived === true) { 29 | return(
30 |
Upcoming Tests
31 | 32 |
) 33 | } else if (this.props.testDetails.viewTestRetrived){ 34 | return(
35 |
Test
36 | 37 |
) 38 | } 39 | else { 40 | 41 | this.props.getUpcomingTestsStudentAction(); 42 | return (
) 43 | } 44 | } 45 | } 46 | 47 | const mapStatetoProps = state => ({ 48 | user : state.user, 49 | testDetails : state.testDetails 50 | }) 51 | 52 | export default withStyles(useStyles)(connect(mapStatetoProps,{ 53 | getUpcomingTestsStudentAction 54 | })(UpcomingStudentTestsDetails)); -------------------------------------------------------------------------------- /user-portal-frontend/src/components/templates/TestDetails/CompletedTestsDetailsStudent.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { withStyles } from "@material-ui/core/styles"; 3 | import { connect } from "react-redux"; 4 | import { getCompletedTestsStudentAction } from "../../../redux/actions/studentTestAction"; 5 | import CompletedTestTableStudent from "../../molecues/TestTable/CompletedTestTableStudent"; 6 | import TestResultStudent from "../../molecues/ResultView/TestResultStudent"; 7 | 8 | const useStyles = (theme)=> ({ 9 | testDetails : { 10 | margin:'20px', 11 | display: 'inline-block', 12 | textAlign : 'center', 13 | }, 14 | testTitle : { 15 | fontSize : '1.7em', 16 | textAlign : 'center', 17 | margin : '20px' 18 | } 19 | }) 20 | 21 | class CompletedTestsDetailsStudent extends React.Component { 22 | constructor(props){ 23 | super(props); 24 | this.state = {} 25 | } 26 | 27 | render() { 28 | if(this.props.testDetails.completedTestRetrived === true) { 29 | return(
30 |
Completed Tests
31 | 32 |
) 33 | } else if (this.props.testDetails.viewTestResult === true){ 34 | return(
35 |
Test Result
36 | 37 |
) 38 | } 39 | else { 40 | 41 | this.props.getCompletedTestsStudentAction(); 42 | return (
) 43 | } 44 | } 45 | } 46 | 47 | const mapStatetoProps = state => ({ 48 | user : state.user, 49 | testDetails : state.testDetails 50 | }) 51 | 52 | export default withStyles(useStyles)(connect(mapStatetoProps,{ 53 | getCompletedTestsStudentAction 54 | })(CompletedTestsDetailsStudent)); -------------------------------------------------------------------------------- /frontend/src/redux/actions/loginAction.js: -------------------------------------------------------------------------------- 1 | import axios from "axios" 2 | import apis from "../../services/Apis"; 3 | import { ActionTypes } from "../constants/action-types"; 4 | import Auth from "../../services/Auth"; 5 | import Alert from "../../services/alert"; 6 | import { Navigate } from "react-router-dom"; 7 | 8 | export const loginUser = (details) => { 9 | return async(dispatch, getState) => { 10 | const response = await axios.post(apis.BASE+ apis.LOGIN,details); 11 | if(response.data.success) { 12 | Auth.storeToken(response.data.token); 13 | dispatch( { 14 | type: ActionTypes.LOGIN, 15 | payload : { 16 | isLoggedIn : true, 17 | userDetails : response.data.admin 18 | } 19 | }) 20 | } else { 21 | Auth.deleteToken(); 22 | return Alert('error','invalid data',response.data.message); 23 | } 24 | } 25 | } 26 | 27 | export const getAdminDetails = () => { 28 | return async(dispatch) => { 29 | axios.get(apis.BASE+apis.GET_ADMIN_DETAILS, { 30 | headers:{ 31 | 'Authorization':`Bearer ${Auth.retriveToken()}` 32 | } 33 | }).then(response => { 34 | 35 | if(response.data.success) { 36 | dispatch({ 37 | type:ActionTypes.LOGIN, 38 | payload: { 39 | isLoggedIn : true, 40 | userDetails : response.data.user 41 | } 42 | }) 43 | } else { 44 | Auth.deleteToken(); 45 | return (); 46 | } 47 | 48 | }).catch(err => { 49 | console.log(err); 50 | Auth.deleteToken(); 51 | return (); 52 | }) 53 | 54 | } 55 | } 56 | 57 | export const logoutUser = ()=> dispatch =>{ 58 | Auth.deleteToken(); 59 | dispatch({ 60 | type : ActionTypes.LOGOUT, 61 | payload : 'Manual Logout' 62 | }) 63 | } -------------------------------------------------------------------------------- /frontend/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /user-portal-frontend/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /frontend/src/components/Dashboard/studentTable/studentTable.js: -------------------------------------------------------------------------------- 1 | import { connect } from "react-redux" 2 | import React from "react"; 3 | import {getStudentDetails, StudentToggleStatus} from "../../../redux/actions/studentDetails"; 4 | import './studentTable.css' 5 | 6 | 7 | class StudentTable extends React.Component { 8 | constructor(props) { 9 | super(props) 10 | this.state = {} 11 | } 12 | 13 | handleStatusChange(status, id) { 14 | this.props.StudentToggleStatus(status,id,this.props.getStudentDetails); 15 | } 16 | 17 | buttonTextBasedOnStatus(status) { 18 | if(status === true) { 19 | return("block"); 20 | } else { 21 | return("unblock"); 22 | } 23 | } 24 | 25 | render(){ 26 | if(this.props.students.retrived===false) { 27 | this.props.getStudentDetails(); 28 | return (
Collecting data
); 29 | } 30 | 31 | return ( 32 |
33 |

Students

34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | {this.props.students.list.map((val,key)=>{ 44 | return ( 45 | 46 | 47 | 48 | 49 | 50 | ) 51 | })} 52 | 53 |
NameStatusAction
{val.name}{val.status.toString()}
54 |
) 55 | } 56 | } 57 | 58 | const mapStateToProps = state => ({ 59 | students : state.students 60 | }); 61 | 62 | export default connect(mapStateToProps,{ 63 | getStudentDetails, 64 | StudentToggleStatus 65 | })(StudentTable); -------------------------------------------------------------------------------- /frontend/src/components/Dashboard/teacherTable/teacherTable.js: -------------------------------------------------------------------------------- 1 | import { connect } from "react-redux" 2 | import React from "react"; 3 | import {getTeacherDetails, TeacherToggleStatus} from "../../../redux/actions/teacherDetails"; 4 | import './teacherTable.css' 5 | 6 | 7 | class TeacherTable extends React.Component { 8 | constructor(props) { 9 | super(props) 10 | this.state = {} 11 | } 12 | 13 | handleStatusChange(status, id) { 14 | this.props.TeacherToggleStatus(status,id,this.props.getTeacherDetails); 15 | } 16 | 17 | buttonTextBasedOnStatus(status) { 18 | if(status === true) { 19 | return("block"); 20 | } else { 21 | return("unblock"); 22 | } 23 | } 24 | 25 | render(){ 26 | if(this.props.teachers.retrived===false) { 27 | this.props.getTeacherDetails(); 28 | return (
Collecting data
); 29 | } 30 | 31 | return ( 32 |
33 |

Teachers

34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | {this.props.teachers.list.map((val,key)=>{ 44 | return ( 45 | 46 | 47 | 48 | 49 | 50 | ) 51 | })} 52 | 53 |
NameStatusAction
{val.name}{val.status.toString()}
54 |
) 55 | } 56 | } 57 | 58 | const mapStateToProps = state => ({ 59 | teachers : state.teachers 60 | }); 61 | 62 | export default connect(mapStateToProps,{ 63 | getTeacherDetails, 64 | TeacherToggleStatus 65 | })(TeacherTable); -------------------------------------------------------------------------------- /backend/services/login.js: -------------------------------------------------------------------------------- 1 | const jwt = require('jsonwebtoken'); 2 | 3 | var config = require('config'); 4 | 5 | var passport = require('./passportconf'); 6 | 7 | var userLogin = (req, res, next) => { 8 | 9 | req.check('email','Invalid email address').isEmail().notEmpty(); 10 | req.check('password','Invalid password').isLength({min:4, max:20}); 11 | 12 | var errors = req.validationErrors(); 13 | 14 | if(errors) { 15 | res.json({ 16 | success : false, 17 | message : 'Invalid inputs', 18 | errors : errors 19 | }) 20 | } 21 | else { 22 | passport.authenticate('login', {session: false}, (err, user, info)=>{ 23 | if(err || !user) { 24 | res.json(info); 25 | } 26 | else { 27 | req.login({_id:user._id}, {session : false}, (err)=>{ 28 | if(err) { 29 | res.json({ 30 | success : false, 31 | message : 'server error' 32 | }); 33 | } 34 | 35 | var token = jwt.sign({_id:user._id},config.get('jwt.secret'),{expiresIn:'1d'}); 36 | res.json({ 37 | success: true, 38 | message : 'login successful', 39 | user : { 40 | username : user.username, 41 | type : user.usertype, 42 | _id : user._id, 43 | email : user.email 44 | }, 45 | token : token 46 | }) 47 | }) 48 | } 49 | })(req,res,next); 50 | } 51 | } 52 | 53 | var userDetails = (req,res,next) => { 54 | if(req.user) { 55 | res.json({ 56 | success:true, 57 | user : { 58 | username : req.user.username, 59 | type : req.user.usertype, 60 | _id : req.user._id, 61 | email : req.user.email 62 | } 63 | }); 64 | } 65 | else { 66 | res.json({ 67 | success: false, 68 | user: {} 69 | }); 70 | } 71 | } 72 | 73 | module.exports = { userLogin, userDetails }; -------------------------------------------------------------------------------- /docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: '1' 2 | 3 | services: 4 | backend: 5 | container_name: backend-app 6 | image: backend-app:1.0 7 | environment: 8 | - NODE_ENV=docker 9 | build: 10 | context: backend 11 | dockerfile: Dockerfile 12 | ports: 13 | - 5000:5000 14 | links: 15 | - mongodb 16 | 17 | 18 | mongodb: 19 | image: mongo 20 | ports: 21 | - 27018:27017 22 | environment: 23 | - MONGO_INITDB_ROOT_USERNAME=admin 24 | - MONGO_INITDB_ROOT_PASSWORD=password 25 | volumes: 26 | - mongo-data:/data/db 27 | healthcheck: 28 | test: echo 'db.runCommand("ping").ok' | mongo mongo:27017/test --quiet 29 | interval: 10s 30 | timeout: 10s 31 | retries: 5 32 | start_period: 40s 33 | 34 | mongo-express: 35 | image: mongo-express 36 | restart: always 37 | ports: 38 | - 8080:8081 39 | environment: 40 | - ME_CONFIG_MONGODB_ADMINUSERNAME=admin 41 | - ME_CONFIG_MONGODB_ADMINPASSWORD=password 42 | - ME_CONFIG_MONGODB_SERVER=mongodb 43 | depends_on: 44 | - "mongodb" 45 | 46 | 47 | frontend: 48 | container_name: frontend-app 49 | image: frontend-app:1.0 50 | environment: 51 | - NODE_ENV=docker 52 | - REACT_APP_API_BASE_URL=http://backend:5000 53 | 54 | build: 55 | context: frontend 56 | dockerfile: Dockerfile 57 | ports: 58 | - 3100:3000 59 | depends_on: 60 | - backend 61 | 62 | user-frontend: 63 | container_name: user-frontend-app 64 | image: user-frontend-app:1.0 65 | environment: 66 | - NODE_ENV=docker 67 | - REACT_APP_API_BASE_URL=http://backend:5000 68 | build: 69 | context: user-portal-frontend 70 | dockerfile: Dockerfile 71 | 72 | ports: 73 | 74 | - 3200:3000 75 | 76 | 77 | depends_on: 78 | - backend 79 | - frontend 80 | 81 | volumes: 82 | mongo-data: 83 | driver: local -------------------------------------------------------------------------------- /frontend/src/components/Dashboard/subjectTable/subjectTable.js: -------------------------------------------------------------------------------- 1 | import { connect } from "react-redux" 2 | import React from "react"; 3 | import {getSubjectDetails, SubjectToggleStatus} from "../../../redux/actions/subjectDetails"; 4 | import './subjectTable.css' 5 | 6 | 7 | class SubjectTable extends React.Component { 8 | constructor(props) { 9 | super(props) 10 | this.state = {} 11 | } 12 | 13 | handleStatusChange(status, id) { 14 | this.props.SubjectToggleStatus(status,id,this.props.getSubjectDetails); 15 | } 16 | 17 | buttonTextBasedOnStatus(status) { 18 | if(status === true) { 19 | return("block"); 20 | } else { 21 | return("unblock"); 22 | } 23 | } 24 | 25 | render(){ 26 | if(this.props.subjects.retrived===false) { 27 | this.props.getSubjectDetails(); 28 | return (
Collecting data
); 29 | } 30 | 31 | return ( 32 |
33 |

Subjects

34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | {this.props.subjects.list.map((val,key)=>{ 44 | return ( 45 | 46 | 47 | 48 | 49 | 50 | 51 | ) 52 | })} 53 | 54 |
NameStatusAction
{val.subject}{val.status.toString()}
55 |
) 56 | } 57 | } 58 | 59 | const mapStateToProps = state => ({ 60 | subjects : state.subjects 61 | }); 62 | 63 | export default connect(mapStateToProps,{ 64 | getSubjectDetails, 65 | SubjectToggleStatus 66 | })(SubjectTable); -------------------------------------------------------------------------------- /user-portal-frontend/src/components/pages/studentRegisterPage/studentRegisterPage.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AlertBox from '../../atoms/Alertbox/AlertBox'; 3 | import StudentRegisterForm from '../../templates/studentRegisterForm/studentRegisterForm'; 4 | import { Button } from '@material-ui/core'; 5 | import { AppBar, Toolbar, Typography, withStyles } from '@material-ui/core'; 6 | import { Navigate } from 'react-router-dom'; 7 | 8 | const useStyles = (theme) => ({ 9 | addHeight : theme.mixins.toolbar, 10 | title : { 11 | flexGrow : 1 12 | }, 13 | main : { 14 | textAlign : 'center', 15 | paddingTop : '5%' 16 | } 17 | }) 18 | 19 | class StudentRegisterPage extends React.Component { 20 | constructor(props) { 21 | super(props); 22 | this.state = { 23 | gotoHome: false 24 | } 25 | } 26 | 27 | onHomeClick() { 28 | this.setState({ 29 | ...this.state, 30 | gotoHome : true 31 | }) 32 | } 33 | 34 | render() { 35 | if(this.state.gotoHome) { 36 | return () 37 | } 38 | return( 39 |
40 | 44 | 45 | 46 | Student Register 47 | 48 | 49 | 50 | 51 | 52 | 53 |
54 |
55 | 56 | 57 |
58 |
59 | ) 60 | } 61 | } 62 | 63 | 64 | export default withStyles(useStyles)(StudentRegisterPage); 65 | 66 | -------------------------------------------------------------------------------- /user-portal-frontend/src/redux/actions/loginAction.js: -------------------------------------------------------------------------------- 1 | import apis from "../../helper/Apis"; 2 | import { ActionTypes } from "../action-types"; 3 | import Auth from "../../helper/Auth"; 4 | import axios from "axios"; 5 | import { Navigate } from "react-router-dom"; 6 | 7 | export const loginRequestAction = (details) => { 8 | return async(dispatch) => { 9 | const response = await axios.post(apis.BASE+ apis.LOGIN,details); 10 | if(response.data.success) { 11 | Auth.storeToken(response.data.token); 12 | dispatch( { 13 | type: ActionTypes.LOGIN, 14 | payload : { 15 | isLoggedIn : true, 16 | userDetails : response.data.user 17 | } 18 | }) 19 | } else { 20 | Auth.deleteToken(); 21 | dispatch({ 22 | type:ActionTypes.SET_ALERT, 23 | payload : { 24 | isAlert : true, 25 | title : "Login Failed", 26 | type : "error", 27 | message : response.data.message 28 | } 29 | }) 30 | } 31 | } 32 | } 33 | 34 | 35 | export const logoutUser = ()=> dispatch =>{ 36 | Auth.deleteToken(); 37 | dispatch({ 38 | type : ActionTypes.LOGOUT, 39 | payload : 'Manual Logout' 40 | }) 41 | return () 42 | } 43 | 44 | export const getUserDetails = () => { 45 | return async(dispatch) => { 46 | axios.get(apis.BASE+apis.GET_USER_DETAILS, { 47 | headers:{ 48 | 'Authorization':`Bearer ${Auth.retriveToken()}` 49 | } 50 | }).then(response => { 51 | 52 | if(response.data.success) { 53 | dispatch({ 54 | type:ActionTypes.LOGIN, 55 | payload: { 56 | isLoggedIn : true, 57 | userDetails : response.data.user 58 | } 59 | }) 60 | } else { 61 | Auth.deleteToken(); 62 | return (); 63 | } 64 | 65 | }).catch(err=> { 66 | Auth.deleteToken(); 67 | return (); 68 | }) 69 | 70 | } 71 | } -------------------------------------------------------------------------------- /user-portal-frontend/src/components/molecues/TestView/Timer.js: -------------------------------------------------------------------------------- 1 | import { withStyles } from "@material-ui/styles"; 2 | import React from "react"; 3 | import { connect } from "react-redux"; 4 | import { saveAnswerAction } from "../../../redux/actions/takeTestAction"; 5 | import { endTestAction } from "../../../redux/actions/takeTestAction"; 6 | const useStyles = (theme)=>({ 7 | 8 | }) 9 | 10 | var x = null; 11 | 12 | class Timer extends React.Component { 13 | constructor(props) { 14 | super(props); 15 | console.log(props.time); 16 | this.state = { 17 | h : parseInt(props.time/3600000), 18 | m : parseInt(((props.time/1000)%3600)/60), 19 | s : parseInt((props.time/1000)%60), 20 | startCountDown : false 21 | } 22 | } 23 | 24 | 25 | countdown() { 26 | if(x !== null) { 27 | clearInterval(x); 28 | } 29 | x = setInterval(()=>{ 30 | var h = this.state.h; 31 | var m = this.state.m; 32 | var s = this.state.s - 1; 33 | if(s < 0) { 34 | this.props.saveAnswerAction(); 35 | s = 59; 36 | m = m - 1; 37 | if(m < 0) { 38 | m = 59; 39 | h = h - 1; 40 | if(h < 0 ){ 41 | this.props.endTestAction(); 42 | this.setState({h:0,m:0,s:0,startCountDown : true}); 43 | console.log("test ended"); 44 | clearInterval(x); 45 | return; 46 | } 47 | } 48 | } 49 | this.setState({h:h,m:m,s:s, startCountDown: true}); 50 | },1000); 51 | } 52 | 53 | render() { 54 | if(this.props.time === undefined) { 55 | console.log("time not found"); 56 | return (
); 57 | } 58 | if(this.state.startCountDown===false) { 59 | this.countdown(); 60 | } 61 | return (
{this.state.h}:{this.state.m}:{this.state.s}
) 62 | } 63 | 64 | } 65 | 66 | const mapStatetoProps = state => ({ 67 | 68 | }) 69 | 70 | export default withStyles(useStyles)(connect(mapStatetoProps,{ 71 | saveAnswerAction, 72 | endTestAction 73 | 74 | })(Timer)); -------------------------------------------------------------------------------- /backend/routes/user.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | var loginService = require('../services/login'); 5 | var questionService = require('../services/question'); 6 | var subjectService = require('../services/subject'); 7 | var testService = require('../services/test'); 8 | var taketestService = require('../services/taketest'); 9 | var resultService = require('../services/result'); 10 | 11 | router.get('/details',loginService.userDetails); 12 | router.get('/getAllTest',testService.getAllTest); 13 | router.post('/getTestById',testService.getTestDetailsFromId); 14 | 15 | // teacher user specific routes 16 | router.post('/addQuestion',questionService.addQuestion); 17 | router.get('/getAllSubjects',subjectService.getAllActiveSubject); 18 | router.post('/searchQuestion',questionService.searchQuestion); 19 | router.post('/updateQuestion',questionService.updateQuestion); 20 | router.post('/getQuestion',questionService.getQuestionById); 21 | router.post('/changeQuestionStatus',questionService.changeQuestionStatus); 22 | router.post('/getAnswer',questionService.getAnsByQuestionId); 23 | router.post('/getQuestionAnswer',questionService.getQuestionAnswerById); 24 | router.post('/createTest',testService.createTest); 25 | 26 | 27 | // student user specific routes 28 | router.post('/testRegistration',testService.testRegistration); 29 | router.get('/getAllTestStudent',testService.getAllTestWithStudentRegisterCheck); 30 | router.get('/getUpcomingTests',testService.getUpcomingTestforStudent); 31 | 32 | 33 | router.post('/startTest',taketestService.startTestForStudent); 34 | router.post('/getQuenStarttime',taketestService.getQuestionsAndSetStartTime); 35 | router.post('/saveAnswer',taketestService.saveAnswer); 36 | router.post('/endTest',taketestService.saveAnswerandEndTest); 37 | 38 | router.get('/getAllCompletedTest',resultService.getAllCompletedTest); 39 | router.post('/getResultMainDetailsByTestId',resultService.getResultMainDetailsByTestId); 40 | router.post('/getQuestionAnswerByIds',questionService.getQuestionAnswerByIds) 41 | module.exports = router; -------------------------------------------------------------------------------- /backend/services/register.js: -------------------------------------------------------------------------------- 1 | var userModel = require('../models/user'); 2 | var tool = require('./tool'); 3 | 4 | var studentRegister = (req,res,next) => { 5 | 6 | req.check('username','Invalid name').notEmpty(); 7 | req.check('email','Invalid Email Address').isEmail().notEmpty(); 8 | req.check('password','Invalid Password').isLength({min: 5, max: 20}); 9 | 10 | var errors = req.validationErrors(); 11 | if(errors) { 12 | res.status(400).json({ 13 | success : false, 14 | message : 'Invalid inputs', 15 | errors : errors 16 | }) 17 | } 18 | else { 19 | var username = req.body.username; 20 | var password = req.body.password; 21 | var email = req.body.email; 22 | 23 | userModel.findOne({'email':email}).then((user)=>{ 24 | //user already exists 25 | if(user) { 26 | res.status(400).json({ 27 | success : false, 28 | message : 'This email is already exists!' 29 | }) 30 | } else { 31 | //add user to database 32 | tool.hashPassword(password) 33 | .then((hash)=> { 34 | var tempdata = new userModel({ 35 | username : username, 36 | password : hash, 37 | email : email, 38 | usertype : 'STUDENT' 39 | }) 40 | tempdata.save() 41 | .then(()=>{ 42 | res.json({ 43 | success : true, 44 | message : 'Profile created successfully!' 45 | }) 46 | }) 47 | .catch((err)=>{ 48 | res.status(500).json({ 49 | success : false, 50 | message : "Unable to register Profile" 51 | }) 52 | }) 53 | }) 54 | .catch((err) => { 55 | res.status(500).json({ 56 | success : false, 57 | message : "Unable to register Profile" 58 | }) 59 | }) 60 | } 61 | }).catch((err)=>{ 62 | res.status(500).json({ 63 | success : false, 64 | message : "Unable to register profile" 65 | }) 66 | }) 67 | } 68 | } 69 | 70 | module.exports = { studentRegister } -------------------------------------------------------------------------------- /user-portal-frontend/src/components/molecues/TestTable/TestTable.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { withStyles } from "@material-ui/styles"; 3 | import { connect } from "react-redux"; 4 | import { TableBody, TableCell, TableRow, Table, TableHead, TableContainer, Paper } from "@material-ui/core"; 5 | import { getTestDetailsFromId } from "../../../redux/actions/teacherTestAction"; 6 | 7 | 8 | const useStyles = (theme)=> ({ 9 | tableBorder:{ 10 | background:'#e7e7e7', 11 | padding:'15px' 12 | }, 13 | tableHeader:{ 14 | background:'#3f51b5', 15 | color:'white' 16 | } 17 | }) 18 | 19 | class TestTable extends React.Component { 20 | constructor(props){ 21 | super(props); 22 | this.state = {} 23 | } 24 | 25 | onTestClick(event,id) { 26 | this.props.getTestDetailsFromId({testid:id}); 27 | } 28 | 29 | render() { 30 | return(
31 | 32 | 33 | 34 | 35 | No. 36 | Test 37 | Status 38 | 39 | 40 | 41 | {this.props.testlist.map((test,index)=>( 42 | 43 | {index+1} 44 | (this.onTestClick(event,test._id))}>{test.title} 45 | {test.status} 46 | 47 | ))} 48 | 49 |
50 | 51 |
52 |
) 53 | } 54 | } 55 | 56 | const mapStatetoProps = state => ({ 57 | testlist : state.testDetails.list 58 | }) 59 | 60 | export default withStyles(useStyles)(connect(mapStatetoProps,{ 61 | getTestDetailsFromId 62 | })(TestTable)); 63 | -------------------------------------------------------------------------------- /backend/services/subject.js: -------------------------------------------------------------------------------- 1 | const subjectModel = require("../models/subject") 2 | 3 | 4 | var getAllSubject = (req, res, next) => { 5 | subjectModel.find({}, (err, sub)=>{ 6 | if(err) { 7 | res.status(500).json({ 8 | success:false, 9 | message : 'Internal server error' 10 | }) 11 | } else { 12 | var subjects = [] 13 | sub.forEach((subject)=>{ 14 | subjects.push({ 15 | "id" : subject._id, 16 | "subject" : subject.name, 17 | "status" : subject.status 18 | }) 19 | }) 20 | res.json({ 21 | success : true, 22 | subjects : subjects 23 | }) 24 | } 25 | }) 26 | } 27 | 28 | var getAllActiveSubject = (req, res, next) => { 29 | subjectModel.find({status:true}, (err, sub)=>{ 30 | if(err) { 31 | res.status(500).json({ 32 | success:false, 33 | message : 'Internal server error' 34 | }) 35 | } else { 36 | var subjects = [] 37 | sub.forEach((subject)=>{ 38 | subjects.push({ 39 | "id" : subject._id, 40 | "subject" : subject.name, 41 | "status" : subject.status 42 | }) 43 | }) 44 | res.json({ 45 | success : true, 46 | subjects : subjects 47 | }) 48 | } 49 | }) 50 | } 51 | 52 | var getStatusCount = (req,res,next) => { 53 | subjectModel.aggregate( 54 | [ 55 | {$match:{}}, 56 | {$group: {_id:"$status",count:{$sum:1}} } 57 | ] 58 | ) 59 | .then((result)=>{ 60 | var trueCount = 0 61 | var falseCount = 0 62 | result.forEach((x)=>{ 63 | if(x._id == true) { 64 | trueCount = x.count 65 | } 66 | if(x._id == false) { 67 | falseCount = x.count 68 | } 69 | }) 70 | res.json({ 71 | success:true, 72 | active : trueCount, 73 | blocked : falseCount 74 | }) 75 | }) 76 | .catch((err)=>{ 77 | console.log(err) 78 | res.status(500).json({ 79 | success:false, 80 | message:'Internal server error' 81 | }) 82 | }) 83 | } 84 | 85 | module.exports = { 86 | getAllSubject, 87 | getStatusCount, 88 | getAllActiveSubject 89 | } -------------------------------------------------------------------------------- /user-portal-frontend/src/components/templates/QuestionDetails/questionDetails.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { withStyles } from "@material-ui/core/styles"; 3 | import { connect } from "react-redux"; 4 | import QuestionSearchBox from "../../atoms/SearchBox/QuestionSearchBox"; 5 | import { searchQuestion } from "../../../redux/actions/questionAction"; 6 | import QuestionTable from "../../molecues/QuestionsTable/QuestionTable"; 7 | import ViewnUpdateQuestion from "../ViewnUpdateQuestion/ViewnUpdateQuestion"; 8 | import { Button } from "@material-ui/core"; 9 | import { goBacktoSearch } from "../../../redux/actions/questionAction"; 10 | 11 | const useStyles = (theme)=> ({ 12 | questionDetails : { 13 | margin:'20px', 14 | display: 'inline-block', 15 | textAlign : 'center', 16 | } 17 | }) 18 | 19 | class QuestionDetails extends React.Component { 20 | constructor(props){ 21 | super(props); 22 | this.state = {} 23 | } 24 | 25 | backHandler() { 26 | this.props.goBacktoSearch(); 27 | } 28 | 29 | render(){ 30 | if(this.props.questionDetails.searched === true) { 31 | return(
32 | 33 | 34 |
) 35 | } else if(this.props.questionDetails.question._id !== undefined) { 36 | return(
37 | 38 |
39 | 40 | 41 |
) 42 | } 43 | else { 44 | return(
45 | 46 |
) 47 | } 48 | } 49 | } 50 | 51 | const mapStatetoProps = state => ({ 52 | user : state.user, 53 | questionDetails : state.questionDetails 54 | }) 55 | 56 | export default withStyles(useStyles)(connect(mapStatetoProps,{ 57 | searchQuestion, 58 | goBacktoSearch 59 | })(QuestionDetails)); -------------------------------------------------------------------------------- /frontend/src/components/basic/login/login.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Navigate } from "react-router-dom"; 3 | import './login.css'; 4 | import { connect } from 'react-redux'; 5 | import { loginUser} from '../../../redux/actions/loginAction'; 6 | 7 | 8 | 9 | 10 | class Login extends React.Component { 11 | constructor(props) { 12 | super(props); 13 | this.state = { 14 | username : "", 15 | password : "" 16 | } 17 | } 18 | 19 | usernameInputHandler = (event)=>{ 20 | this.setState({ 21 | ...this.state, 22 | username : event.target.value 23 | }); 24 | } 25 | 26 | passwordInputHandler = (event) => { 27 | this.setState({ 28 | ...this.state, 29 | password : event.target.value 30 | }); 31 | } 32 | 33 | handleSubmit = (event) => { 34 | event.preventDefault(); 35 | this.props.loginUser({username:this.state.username,password:this.state.password}); 36 | } 37 | 38 | render(){ 39 | if(this.props.user.isLoggedIn) { 40 | return (); 41 | } 42 | else { 43 | return ( 44 |
45 |

Admin Login

46 |
47 |
48 | 49 | 50 |
51 |
52 | 53 | 54 |
55 | 62 |
63 |
64 | ); 65 | } 66 | } 67 | } 68 | 69 | 70 | const mapStateToProps = state => ({ 71 | user : state.user 72 | }); 73 | 74 | const mapDispatchToProps = { 75 | loginUser 76 | } 77 | 78 | export default connect(mapStateToProps,mapDispatchToProps)(Login); -------------------------------------------------------------------------------- /frontend/src/components/Dashboard/AddSubject/AddSubject.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { connect } from "react-redux"; 3 | import { Link, Navigate } from "react-router-dom"; 4 | import Alert from "../../../services/alert"; 5 | import Auth from "../../../services/Auth"; 6 | import { getAdminDetails } from "../../../redux/actions/loginAction"; 7 | import "./AddSubject.css" 8 | import axios from "axios"; 9 | import apis from "../../../services/Apis"; 10 | 11 | class AddSubject extends React.Component { 12 | constructor(props) { 13 | super(props) 14 | this.state = { 15 | name : "" 16 | } 17 | } 18 | 19 | nameInputHandler = (event)=>{ 20 | this.setState({ 21 | ...this.state, 22 | name : event.target.value 23 | }); 24 | } 25 | 26 | 27 | 28 | handleSubmit= (event) => { 29 | event.preventDefault(); 30 | 31 | console.log(this.state); 32 | axios.post(apis.BASE + apis.ADD_SUBJECT, { 33 | name : this.state.name 34 | },{ 35 | headers:{ 36 | 'Authorization':`Bearer ${Auth.retriveToken()}` 37 | } 38 | }).then(response => { 39 | console.log(response.data); 40 | if(response.data.success) { 41 | Alert('info','Success',response.data.message); 42 | } else { 43 | Alert('error','Failed',response.data.message); 44 | } 45 | }) 46 | } 47 | 48 | render(){ 49 | if(!Auth.retriveToken() || Auth.retriveToken()==='undefined'){ 50 | return (); 51 | } else if(!this.props.user.isLoggedIn) { 52 | this.props.getAdminDetails(); 53 | return (
) 54 | } 55 | return ( 56 | 57 |
58 |

Add Subject

59 |
60 | 61 | 62 |
63 | 64 | 65 |
66 | 67 |
68 | ) 69 | } 70 | } 71 | 72 | const mapStateToProps = state => ({ 73 | user:state.user 74 | }); 75 | 76 | export default connect(mapStateToProps,{ 77 | getAdminDetails 78 | })(AddSubject); -------------------------------------------------------------------------------- /user-portal-frontend/src/components/atoms/SearchBox/QuestionSearchBox.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { withStyles } from "@material-ui/core/styles"; 3 | import { connect } from "react-redux"; 4 | import InputBase from '@material-ui/core/InputBase'; 5 | import IconButton from '@material-ui/core/IconButton'; 6 | import SearchIcon from '@material-ui/icons/Search'; 7 | import { setAlert } from "../../../redux/actions/alertAction"; 8 | 9 | const useStyles = (theme)=> ({ 10 | input: { 11 | marginLeft: theme.spacing(1), 12 | flex: 1, 13 | minWidth: 400 14 | }, 15 | iconButton: { 16 | padding: 10, 17 | }, 18 | searchbox:{ 19 | border:'2px solid', 20 | borderColor:"#3f51b5", 21 | borderRadius:'30px', 22 | display:'inline-box', 23 | padding:'3px 8px', 24 | margin:10 25 | } 26 | }) 27 | 28 | class QuestionSearchBox extends React.Component { 29 | constructor(props) { 30 | super(props); 31 | this.state = { 32 | query:'' 33 | } 34 | } 35 | 36 | searchInputHandler(event) { 37 | this.setState({ 38 | ...this.state, 39 | query:event.target.value 40 | }) 41 | } 42 | 43 | searchButtonClick(event){ 44 | if(this.state.query === '') { 45 | this.props.setAlert({ 46 | isAlert:true, 47 | type:'error', 48 | title:'Error', 49 | message:'Please give input to search' 50 | }) 51 | } 52 | console.log(this.state); 53 | this.props.searchCallback(this.state); 54 | } 55 | 56 | 57 | render() { 58 | return( 59 |
60 | (this.searchInputHandler(event))} 65 | value={this.state.query} 66 | /> 67 | (this.searchButtonClick(event))} 71 | > 72 | 73 | 74 |
75 | ) 76 | } 77 | } 78 | 79 | const mapStatetoProps = state => ({ 80 | }) 81 | 82 | export default withStyles(useStyles)(connect(mapStatetoProps,{ 83 | setAlert 84 | })(QuestionSearchBox)); -------------------------------------------------------------------------------- /backend/app.js: -------------------------------------------------------------------------------- 1 | 2 | var createError = require('http-errors'); 3 | var express = require('express'); 4 | const session = require('express-session'); 5 | const helmet = require('helmet') 6 | var path = require('path'); 7 | var logger = require('morgan'); 8 | var bodyParser = require('body-parser'); 9 | const expressValidator = require('express-validator'); 10 | var passport = require("./services/passportconf"); 11 | var app = express(); 12 | const cors = require('cors'); 13 | 14 | app.use(helmet()); 15 | app.use(function(req, res, next) { 16 | res.header("Access-Control-Allow-Origin", "*"); 17 | res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, access-control-allow-origin"); 18 | next(); 19 | }); 20 | 21 | 22 | const corsOptions = { 23 | origin: '*' 24 | } 25 | app.use(cors(corsOptions)); 26 | app.use(expressValidator()); 27 | 28 | //database connection 29 | require("./services/connection"); 30 | 31 | //import files 32 | var publicRoutes = require("./routes/public"); 33 | var login = require("./routes/login"); 34 | var adminLogin = require('./routes/adminLogin'); 35 | var admin = require('./routes/admin'); 36 | var user = require('./routes/user'); 37 | 38 | 39 | //configs 40 | app.use(express.static(path.join(__dirname, 'public'))); 41 | app.use(logger('dev')); 42 | app.use(bodyParser.json()); 43 | app.use(bodyParser.urlencoded({ extended: true })); 44 | app.use(session({secret:'express-session secret'})) 45 | 46 | //passport 47 | app.use(passport.initialize()); 48 | app.use(passport.session()); 49 | 50 | //bind routes 51 | app.use('/api/v1/public',publicRoutes); 52 | app.use('/api/v1/login',login); 53 | app.use('/api/v1/adminlogin',adminLogin); 54 | app.use('/api/v1/admin',passport.authenticate('admin-token', {session:false}),admin); 55 | app.use('/api/v1/user',passport.authenticate('user-token', {session:false}),user); 56 | 57 | app.get('*', (req,res) =>{ 58 | res.sendFile(path.join(__dirname+'/public/index.html')); 59 | }); 60 | 61 | //error handlings 62 | app.use(function(req, res, next) { 63 | next(createError(404,"Invalid API. Use the official documentation to get the list of valid APIS.")); 64 | }); 65 | 66 | app.use((err, req, res, next)=>{ 67 | console.log(err); 68 | res.status(err.status).json({ 69 | success : false, 70 | message : err.message 71 | }); 72 | }); 73 | 74 | 75 | module.exports = app; 76 | 77 | -------------------------------------------------------------------------------- /user-portal-frontend/src/components/molecues/TestView/TestQuestion.js: -------------------------------------------------------------------------------- 1 | import { withStyles } from "@material-ui/styles"; 2 | import { connect } from "react-redux"; 3 | import React from "react"; 4 | import { FormControl, FormControlLabel, FormLabel, RadioGroup, Radio } from "@material-ui/core"; 5 | import { selectedOptionAction } from "../../../redux/actions/takeTestAction"; 6 | 7 | 8 | const useStyles = (theme) => ({ 9 | quebody : { 10 | margin : "15px" 11 | }, 12 | options : { 13 | margin : "15px" 14 | } 15 | }) 16 | 17 | class TestQuestion extends React.Component { 18 | constructor(props) { 19 | super(props); 20 | console.log(this.props.question) 21 | this.state = { 22 | selectedOption : this.props.taketest.answersheet.answers[parseInt(this.props.question)] || null 23 | } 24 | } 25 | 26 | optionSelectHandler(event) { 27 | this.props.selectedOptionAction({ 28 | index : this.props.question, 29 | ans : event.target.value 30 | }) 31 | } 32 | 33 | render() { 34 | if(this.props.question!==undefined) { 35 | var que = this.props.taketest.questionid[this.props.question]; 36 | var selectValue = this.props.taketest.answersheet.answers[parseInt(this.props.question)] || null 37 | return(
38 |
{que.body}
39 | 40 | Select Answer 41 | (this.optionSelectHandler(event))}> 42 | } label={que.options[0]} /> 43 | } label={que.options[1]} /> 44 | } label={que.options[2]} /> 45 | } label={que.options[3]} /> 46 | 47 | 48 | 49 |
) 50 | } else { 51 | return
qustion is undefined
52 | } 53 | } 54 | } 55 | 56 | const mapStatetoProps = state => ({ 57 | taketest : state.takeTestDetails 58 | }) 59 | 60 | export default withStyles(useStyles)(connect(mapStatetoProps,{ 61 | selectedOptionAction 62 | })(TestQuestion)); 63 | -------------------------------------------------------------------------------- /frontend/src/components/StudentRegister/RegisterForm/registerform.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Alert from "../../../services/alert"; 3 | import apis from '../../../services/Apis'; 4 | import axios from "axios"; 5 | import { connect } from "react-redux"; 6 | import './registerform.css'; 7 | 8 | class RegisterForm extends React.Component { 9 | constructor(props) { 10 | super(props); 11 | this.state = { 12 | name:"", 13 | email:"", 14 | password:"" 15 | }; 16 | } 17 | 18 | nameInputHandler = (event)=>{ 19 | this.setState({ 20 | ...this.state, 21 | name : event.target.value 22 | }); 23 | } 24 | 25 | emailInputHandler = (event)=>{ 26 | this.setState({ 27 | ...this.state, 28 | email : event.target.value 29 | }); 30 | } 31 | 32 | passwordInputHandler = (event)=>{ 33 | this.setState({ 34 | ...this.state, 35 | password : event.target.value 36 | }); 37 | } 38 | 39 | handleSubmit= async (event) => { 40 | event.preventDefault(); 41 | const details = { 42 | name : this.state.name, 43 | email : this.state.email, 44 | password : this.state.password 45 | } 46 | const response = await axios.post(apis.BASE+ apis.REGISTER_USER,details); 47 | if(response.data.success) { 48 | Alert("info","Suceess",response.data.message); 49 | } else { 50 | Alert("error","Failure",response.data.message); 51 | } 52 | } 53 | 54 | render() { 55 | return ( 56 |
57 |

Student Registration

58 |
59 |
60 | 61 | 62 |
63 |
64 | 65 | 66 |
67 |
68 | 69 | 70 |
71 | 74 |
75 |
76 | ) 77 | } 78 | } 79 | 80 | const mapStateToProps = state => ({ 81 | user : state.user 82 | }); 83 | 84 | const mapDispatchToProps = {} 85 | 86 | export default connect(mapStateToProps,mapDispatchToProps)(RegisterForm); -------------------------------------------------------------------------------- /user-portal-frontend/src/components/molecues/TestTable/CompletedTestTableStudent.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { withStyles } from "@material-ui/styles"; 3 | import { connect } from "react-redux"; 4 | import { TableBody, TableCell, TableRow, Table, TableHead, TableContainer, Paper, Button } from "@material-ui/core"; 5 | import { getTestResultStudent } from "../../../redux/actions/studentTestAction"; 6 | 7 | const useStyles = (theme)=> ({ 8 | tableBorder:{ 9 | background:'#e7e7e7', 10 | padding:'15px' 11 | }, 12 | tableHeader:{ 13 | background:'#3f51b5', 14 | color:'white' 15 | } 16 | }) 17 | 18 | class CompletedTestTableStudent extends React.Component { 19 | constructor(props){ 20 | super(props); 21 | this.state = {} 22 | } 23 | 24 | onTestClick(event,id) { 25 | console.log("view result for test "+id); 26 | this.props.getTestResultStudent({testid:id}); 27 | } 28 | 29 | onTestRegister(event,id) { 30 | this.props.studentTestRegister({testid:id}); 31 | } 32 | 33 | 34 | render() { 35 | return(
36 | 37 | 38 | 39 | 40 | Test 41 | Status 42 | total
marks
43 | View 44 |
45 |
46 | 47 | {this.props.testlist.map((test,index)=>( 48 | 49 | {test.title} 50 | {test.status} 51 | {test.maxmarks} 52 | 53 | 54 | ))} 55 | 56 |
57 | 58 |
59 |
) 60 | } 61 | } 62 | 63 | const mapStatetoProps = state => ({ 64 | testlist : state.testDetails.list 65 | }) 66 | 67 | export default withStyles(useStyles)(connect(mapStatetoProps,{ 68 | getTestResultStudent 69 | })(CompletedTestTableStudent)); 70 | -------------------------------------------------------------------------------- /user-portal-frontend/src/components/pages/loginPage/loginPage.js: -------------------------------------------------------------------------------- 1 | import { Button, withStyles } from '@material-ui/core'; 2 | import React from 'react'; 3 | import { connect } from 'react-redux'; 4 | import { Navigate } from 'react-router-dom'; 5 | import AlertBox from '../../atoms/Alertbox/AlertBox'; 6 | import LoginForm from '../../templates/loginForm/loginForm'; 7 | import Auth from '../../../helper/Auth'; 8 | import { AppBar, Toolbar, Typography } from '@material-ui/core'; 9 | 10 | const useStyles = (theme) => ({ 11 | addHeight : theme.mixins.toolbar, 12 | title : { 13 | flexGrow : 1 14 | }, 15 | main : { 16 | textAlign : 'center', 17 | paddingTop : '5%', 18 | margin : 'auto' 19 | } 20 | }) 21 | 22 | 23 | class LoginPage extends React.Component { 24 | constructor(props) { 25 | super(props); 26 | this.state = { 27 | gotoStudentRegister: false 28 | } 29 | } 30 | 31 | onStudentRegisterClick () { 32 | this.setState({ 33 | ...this.state, 34 | gotoStudentRegister : true 35 | }) 36 | } 37 | 38 | 39 | render(){ 40 | if(this.state.gotoStudentRegister) { 41 | return () 42 | } 43 | if(this.props.user.isLoggedIn) { 44 | if(this.props.user.userDetails.type === 'TEACHER') 45 | return (); 46 | else 47 | return (); 48 | } else if(Auth.retriveToken() && Auth.retriveToken()!=='undefined'){ 49 | 50 | return (); 51 | } 52 | else { 53 | return ( 54 |
55 | 59 | 60 | 61 | Login 62 | 63 | 64 | 65 | 66 | 67 | 68 |
69 |
70 | 71 | 72 |
73 |
74 | ) 75 | } 76 | } 77 | } 78 | 79 | const mapStatetoProps = state=>({ 80 | user : state.user 81 | }); 82 | 83 | export default withStyles(useStyles)(connect(mapStatetoProps,{})(LoginPage)); -------------------------------------------------------------------------------- /user-portal-frontend/src/redux/actions/teacherTestAction.js: -------------------------------------------------------------------------------- 1 | import axios from "axios" 2 | import apis from "../../helper/Apis" 3 | import { ActionTypes } from "../action-types"; 4 | import Auth from "../../helper/Auth" 5 | 6 | export const createTestAction = (details) => { 7 | return async(dispatch)=> { 8 | const response = await axios.post(apis.BASE + apis.CREATE_QUESTION, details, { 9 | headers:{ 10 | 'Authorization':`Bearer ${Auth.retriveToken()}` 11 | } 12 | }) 13 | if(response.data.success) { 14 | dispatch({ 15 | type:ActionTypes.SET_ALERT, 16 | payload : { 17 | isAlert : true, 18 | title : "Success", 19 | type : "success", 20 | message : response.data.message 21 | } 22 | }) 23 | } 24 | else { 25 | dispatch({ 26 | type:ActionTypes.SET_ALERT, 27 | payload : { 28 | isAlert : true, 29 | title : "Submit Error", 30 | type : "error", 31 | message : response.data.message 32 | } 33 | }) 34 | } 35 | } 36 | } 37 | 38 | export const getAllTestAction = () => { 39 | return async(dispatch)=> { 40 | await axios.get(apis.BASE + apis.GET_ALL_TEST, { 41 | headers : { 42 | 'Authorization':`Bearer ${Auth.retriveToken()}` 43 | } 44 | }).then(response => { 45 | if(response.data.success) { 46 | dispatch({ 47 | type: ActionTypes.GET_ALL_TESTS, 48 | payload : { 49 | testlist : response.data.testlist 50 | } 51 | }) 52 | } 53 | }) 54 | } 55 | } 56 | 57 | export const getTestDetailsFromId = (details) => { 58 | return async(dispatch) => { 59 | const response = await axios.post(apis.BASE+ apis.GET_TEST_DETAILS_BY_ID,details,{ 60 | headers:{ 61 | 'Authorization':`Bearer ${Auth.retriveToken()}` 62 | } 63 | }) 64 | if(response.data.success) { 65 | dispatch({ 66 | type:ActionTypes.GET_TEST_DETAILS_TEACHER, 67 | payload : { 68 | test : response.data.test 69 | } 70 | }) 71 | } else { 72 | dispatch({ 73 | type:ActionTypes.SET_ALERT, 74 | payload : { 75 | isAlert : true, 76 | title : "Could not get test details", 77 | type : "error", 78 | message : response.data.message 79 | } 80 | }) 81 | } 82 | } 83 | } 84 | 85 | export const goBackToAllTest = () => { 86 | return (dispatch) => { 87 | dispatch({ 88 | type: ActionTypes.Go_BACK_ALL_TEST_TEACHER, 89 | payload : '' 90 | }) 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /user-portal-frontend/src/components/templates/loginForm/loginForm.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import TextField from "@material-ui/core/TextField"; 3 | import './loginForm.css'; 4 | import Button from "@material-ui/core/Button"; 5 | import { withStyles } from "@material-ui/core/styles"; 6 | import { loginRequestAction } from "../../../redux/actions/loginAction"; 7 | import { connect } from "react-redux"; 8 | 9 | const useStyles = ()=>({ 10 | inputfield : { 11 | display:'block', 12 | margin :'20px' 13 | }, 14 | loginbtn : { 15 | margin : '0px 40px' 16 | } 17 | }) 18 | 19 | class LoginForm extends React.Component { 20 | constructor(props) { 21 | super(props); 22 | this.state = { 23 | email : "", 24 | password : "" 25 | } 26 | } 27 | 28 | emailInputHandler = (event) => { 29 | this.setState({ 30 | ...this.state, 31 | email : event.target.value 32 | }); 33 | } 34 | 35 | passwordInputHandler = (event) => { 36 | this.setState({ 37 | ...this.state, 38 | password : event.target.value 39 | }); 40 | } 41 | 42 | handleSubmit(event) { 43 | event.preventDefault(); 44 | this.props.loginRequestAction(this.state); 45 | } 46 | 47 | render() { 48 | return ( 49 |
(this.handleSubmit(event))}> 50 |
LOGIN
51 | (this.emailInputHandler(event))} 61 | required 62 | /> 63 | (this.passwordInputHandler(event))} 73 | required 74 | /> 75 | 83 | 84 | ) 85 | } 86 | } 87 | 88 | const mapStatetoProps = state => ({ 89 | state : state.user 90 | }) 91 | 92 | export default withStyles(useStyles)(connect(mapStatetoProps,{ 93 | loginRequestAction 94 | })(LoginForm)); 95 | 96 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | .pnpm-debug.log* 9 | 10 | # Diagnostic reports (https://nodejs.org/api/report.html) 11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 12 | 13 | # Runtime data 14 | pids 15 | *.pid 16 | *.seed 17 | *.pid.lock 18 | 19 | # Directory for instrumented libs generated by jscoverage/JSCover 20 | lib-cov 21 | 22 | # Coverage directory used by tools like istanbul 23 | coverage 24 | *.lcov 25 | 26 | # nyc test coverage 27 | .nyc_output 28 | 29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 30 | .grunt 31 | 32 | # Bower dependency directory (https://bower.io/) 33 | bower_components 34 | 35 | # node-waf configuration 36 | .lock-wscript 37 | 38 | # Compiled binary addons (https://nodejs.org/api/addons.html) 39 | build/Release 40 | 41 | # Dependency directories 42 | node_modules/ 43 | jspm_packages/ 44 | 45 | # Snowpack dependency directory (https://snowpack.dev/) 46 | web_modules/ 47 | 48 | # TypeScript cache 49 | *.tsbuildinfo 50 | 51 | # Optional npm cache directory 52 | .npm 53 | 54 | # Optional eslint cache 55 | .eslintcache 56 | 57 | # Optional stylelint cache 58 | .stylelintcache 59 | 60 | # Microbundle cache 61 | .rpt2_cache/ 62 | .rts2_cache_cjs/ 63 | .rts2_cache_es/ 64 | .rts2_cache_umd/ 65 | 66 | # Optional REPL history 67 | .node_repl_history 68 | 69 | # Output of 'npm pack' 70 | *.tgz 71 | 72 | # Yarn Integrity file 73 | .yarn-integrity 74 | 75 | # dotenv environment variable files 76 | .env 77 | .env.development.local 78 | .env.test.local 79 | .env.production.local 80 | .env.local 81 | 82 | # parcel-bundler cache (https://parceljs.org/) 83 | .cache 84 | .parcel-cache 85 | 86 | # Next.js build output 87 | .next 88 | out 89 | 90 | # Nuxt.js build / generate output 91 | .nuxt 92 | dist 93 | 94 | # Gatsby files 95 | .cache/ 96 | # Comment in the public line in if your project uses Gatsby and not Next.js 97 | # https://nextjs.org/blog/next-9-1#public-directory-support 98 | # public 99 | 100 | # vuepress build output 101 | .vuepress/dist 102 | 103 | # vuepress v2.x temp and cache directory 104 | .temp 105 | .cache 106 | 107 | # Serverless directories 108 | .serverless/ 109 | 110 | # FuseBox cache 111 | .fusebox/ 112 | 113 | # DynamoDB Local files 114 | .dynamodb/ 115 | 116 | # TernJS port file 117 | .tern-port 118 | 119 | # Stores VSCode versions used for testing VSCode extensions 120 | .vscode-test 121 | 122 | # yarn v2 123 | .yarn/cache 124 | .yarn/unplugged 125 | .yarn/build-state.yml 126 | .yarn/install-state.gz 127 | .pnp.* 128 | 129 | database.xlsx 130 | notes.docx -------------------------------------------------------------------------------- /user-portal-frontend/src/components/molecues/QuestionsTable/QuestionTable.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { withStyles } from "@material-ui/core/styles"; 3 | import { connect } from "react-redux"; 4 | import { TableBody, TableCell, TableRow, Table, TableHead, TableContainer, Paper, Button } from "@material-ui/core"; 5 | import { changeQuestionStatus, searchQuestionById } from "../../../redux/actions/questionAction"; 6 | 7 | const useStyles = (theme)=> ({ 8 | tableBorder:{ 9 | background:'#e7e7e7', 10 | padding:'15px' 11 | }, 12 | tableHeader:{ 13 | background:'#3f51b5', 14 | color:'white' 15 | } 16 | }) 17 | 18 | 19 | 20 | class QuestionTable extends React.Component { 21 | constructor(props){ 22 | super(props); 23 | this.state = {} 24 | } 25 | 26 | viewQuestion = (id) => { 27 | this.props.searchQuestionById(id); 28 | } 29 | 30 | onQuestionStatusChange = (event,id,status) => { 31 | this.props.changeQuestionStatus({id,status:(!status)}); 32 | } 33 | 34 | render() { 35 | 36 | return( 37 |
38 | 39 | 40 | 41 | 42 | No. 43 | Question 44 | Status 45 | Action 46 | 47 | 48 | 49 | {this.props.questionlist.map((question,index)=>( 50 | 51 | {index+1} 52 | (this.viewQuestion(question._id))}>{question.body} 53 | 56 | {(question.status===true)?'Active':'Blocked'} 57 | 58 | 59 | 65 | 66 | 67 | ))} 68 | 69 |
70 | 71 |
72 |
73 | ) 74 | } 75 | } 76 | 77 | const mapStatetoProps = state => ({ 78 | questionlist : state.questionDetails.list 79 | }) 80 | 81 | export default withStyles(useStyles)(connect(mapStatetoProps,{ 82 | changeQuestionStatus, 83 | searchQuestionById 84 | })(QuestionTable)); -------------------------------------------------------------------------------- /user-portal-frontend/src/components/molecues/TestTable/UpcomingTestTableStudent.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { withStyles } from "@material-ui/styles"; 3 | import { connect } from "react-redux"; 4 | import { TableBody, TableCell, TableRow, Table, TableHead, TableContainer, Paper, Button } from "@material-ui/core"; 5 | import { getDatePretty, getTimePretty } from "../../../helper/common"; 6 | import { getTestById } from "../../../redux/actions/studentTestAction"; 7 | 8 | const useStyles = (theme)=> ({ 9 | tableBorder:{ 10 | background:'#e7e7e7', 11 | padding:'15px' 12 | }, 13 | tableHeader:{ 14 | background:'#3f51b5', 15 | color:'white' 16 | } 17 | }) 18 | 19 | class UpcomingTestTableStudent extends React.Component { 20 | constructor(props){ 21 | super(props); 22 | this.state = {} 23 | } 24 | 25 | onTestClick(event,id) { 26 | this.props.getTestById({testid:id}); 27 | } 28 | 29 | onTestRegister(event,id) { 30 | this.props.studentTestRegister({testid:id}); 31 | } 32 | 33 | 34 | render() { 35 | return(
36 | 37 | 38 | 39 | 40 | Test 41 | Status 42 | total
marks
43 | Duration
(hours)
44 | Test start 45 | Test end 46 | Result 47 | View 48 |
49 |
50 | 51 | {this.props.testlist.map((test,index)=>( 52 | 53 | {test.title} 54 | {test.status} 55 | {test.maxmarks} 56 | {getTimePretty(test.duration)} 57 | {getDatePretty(test.startTime)} 58 | {getDatePretty(test.endTime)} 59 | {getDatePretty(test.resultTime)} 60 | 61 | 62 | ))} 63 | 64 |
65 | 66 |
67 |
) 68 | } 69 | } 70 | 71 | const mapStatetoProps = state => ({ 72 | testlist : state.testDetails.list 73 | }) 74 | 75 | export default withStyles(useStyles)(connect(mapStatetoProps,{ 76 | getTestById 77 | })(UpcomingTestTableStudent)); 78 | -------------------------------------------------------------------------------- /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 your browser. 13 | 14 | The page will reload when you make changes.\ 15 | You may 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 | -------------------------------------------------------------------------------- /user-portal-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 your browser. 13 | 14 | The page will reload when you make changes.\ 15 | You may 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/src/components/Dashboard/AddTeacher/AddTeacher.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { connect } from "react-redux"; 3 | import { Link, Navigate } from "react-router-dom"; 4 | import Alert from "../../../services/alert"; 5 | import Auth from "../../../services/Auth"; 6 | import { getAdminDetails } from "../../../redux/actions/loginAction"; 7 | import "./AddTeacher.css" 8 | import axios from "axios"; 9 | import apis from "../../../services/Apis"; 10 | 11 | class AddTeacher extends React.Component { 12 | constructor(props) { 13 | super(props) 14 | this.state = { 15 | name : "", 16 | email : "", 17 | password : "", 18 | confirmpassword : "" 19 | } 20 | } 21 | 22 | nameInputHandler = (event)=>{ 23 | this.setState({ 24 | ...this.state, 25 | name : event.target.value 26 | }); 27 | } 28 | 29 | emailInputHandler = (event)=> { 30 | this.setState({ 31 | ...this.state, 32 | email : event.target.value 33 | }) 34 | } 35 | 36 | passwordInputHandler = (event)=> { 37 | this.setState({ 38 | ...this.state, 39 | password : event.target.value 40 | }) 41 | } 42 | 43 | confirmInputHandler = (event)=> { 44 | this.setState({ 45 | ...this.state, 46 | confirmpassword : event.target.value 47 | }) 48 | } 49 | 50 | handleSubmit= (event) => { 51 | event.preventDefault(); 52 | if(this.state.confirmpassword !== this.state.password) { 53 | Alert('error','Invalid Input','Confirm Password does not match') 54 | } 55 | console.log(this.state); 56 | axios.post(apis.BASE + apis.ADD_TEACHER, { 57 | username : this.state.name, 58 | email : this.state.email, 59 | password : this.state.password 60 | },{ 61 | headers:{ 62 | 'Authorization':`Bearer ${Auth.retriveToken()}` 63 | } 64 | }).then(response => { 65 | console.log(response.data); 66 | if(response.data.success) { 67 | Alert('info','Success',response.data.message); 68 | } else { 69 | Alert('error','Failed',response.data.message); 70 | } 71 | }) 72 | } 73 | 74 | render(){ 75 | if(!Auth.retriveToken() || Auth.retriveToken()==='undefined'){ 76 | return (); 77 | } else if(!this.props.user.isLoggedIn) { 78 | this.props.getAdminDetails(); 79 | return (
) 80 | } 81 | return ( 82 | 83 |
84 |

Add Teacher

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 | const mapStateToProps = state => ({ 110 | user:state.user 111 | }); 112 | 113 | export default connect(mapStateToProps,{ 114 | getAdminDetails 115 | })(AddTeacher); -------------------------------------------------------------------------------- /user-portal-frontend/src/components/molecues/TestTable/TestTableStudent.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { withStyles } from "@material-ui/styles"; 3 | import { connect } from "react-redux"; 4 | import { TableBody, TableCell, TableRow, Table, TableHead, TableContainer, Paper, Button } from "@material-ui/core"; 5 | import { studentTestRegister } from "../../../redux/actions/studentTestAction"; 6 | import { getDatePretty, getTimePretty } from "../../../helper/common"; 7 | 8 | 9 | const useStyles = (theme)=> ({ 10 | tableBorder:{ 11 | background:'#e7e7e7', 12 | padding:'15px' 13 | }, 14 | tableHeader:{ 15 | background:'#3f51b5', 16 | color:'white' 17 | } 18 | }) 19 | 20 | class TestTableStudent extends React.Component { 21 | constructor(props){ 22 | super(props); 23 | this.state = {} 24 | } 25 | 26 | onTestClick(event,id) { 27 | console.log(id); 28 | } 29 | 30 | onTestRegister(event,id) { 31 | this.props.studentTestRegister({testid:id}); 32 | } 33 | 34 | 35 | render() { 36 | return(
37 | 38 | 39 | 40 | 41 | Test 42 | Status 43 | total
marks
44 | Duration
(hours)
45 | Registration start 46 | Registration end 47 | Test start 48 | Test end 49 | Result 50 | Register 51 |
52 |
53 | 54 | {this.props.testlist.map((test,index)=>( 55 | 56 | {test.title} 57 | {test.status} 58 | {test.maxmarks} 59 | {getTimePretty(test.duration)} 60 | {getDatePretty(test.regStartTime)} 61 | {getDatePretty(test.regEndTime)} 62 | {getDatePretty(test.startTime)} 63 | {getDatePretty(test.endTime)} 64 | {getDatePretty(test.resultTime)} 65 | {test.isRegistered===false?(test.status==='REGISTRATION_STARTED'? () : 'not Registered'):'Registered'} 69 | 70 | ))} 71 | 72 |
73 | 74 |
75 |
) 76 | } 77 | } 78 | 79 | const mapStatetoProps = state => ({ 80 | testlist : state.testDetails.list 81 | }) 82 | 83 | export default withStyles(useStyles)(connect(mapStatetoProps,{ 84 | studentTestRegister 85 | })(TestTableStudent)); 86 | -------------------------------------------------------------------------------- /user-portal-frontend/src/components/molecues/ResultView/TestResultStudent.js: -------------------------------------------------------------------------------- 1 | import { withStyles } from "@material-ui/styles"; 2 | import React from "react"; 3 | import { connect } from "react-redux"; 4 | import { Button } from "@material-ui/core"; 5 | import { getCompletedTestsStudentAction } from "../../../redux/actions/studentTestAction"; 6 | import { TableCell, TableContainer, Table, TableBody, TableRow, Paper } from "@material-ui/core"; 7 | import TestResultViewQuestions from "./TestResultViewQuestions"; 8 | import { setAlert } from "../../../redux/actions/alertAction"; 9 | 10 | const useStyles = (theme)=> ({ 11 | tableBorder:{ 12 | background:'#e7e7e7', 13 | padding:'15px' 14 | }, 15 | tableHeader:{ 16 | background:'#3f51b5', 17 | color:'white' 18 | } 19 | 20 | }) 21 | 22 | class TestResultStudent extends React.Component { 23 | constructor(props){ 24 | super(props); 25 | this.state = { 26 | toggleViewQue : false 27 | } 28 | } 29 | 30 | onViewQuestions(event, result){ 31 | if(result.status !== 'RESULT_DECLARED') { 32 | this.props.setAlert({ 33 | type : 'info', 34 | title:'No Result', 35 | message : 'test result is not declared' 36 | }) 37 | return; 38 | } 39 | this.setState({ 40 | ...this.state, 41 | toggleViewQue : !this.state.toggleViewQue 42 | }) 43 | } 44 | 45 | goBack() { 46 | this.props.getCompletedTestsStudentAction(); 47 | } 48 | 49 | render() { 50 | var test = this.props.test 51 | return (
52 | 53 | 54 | 55 | 56 | Title 57 | {test.title} 58 | 59 | 60 | Student Name 61 | {this.props.user.username} 62 | 63 | 64 | Status 65 | {test.status} 66 | 67 | 68 | Subjects 69 | {test.subjects} 70 | 71 | 72 | Total Marks 73 | {test.maxmarks} 74 | 75 | 76 | Obtained Marks 77 | {test.status === 'RESULT_DECLARED' ? test.score : "Result not declared"} 78 | 79 | 80 | Questions 81 | 85 | 86 | 87 | 88 | 89 | 90 | 91 |
92 | 93 |
94 | { this.state.toggleViewQue === true ? : ""} 95 |
) 96 | } 97 | } 98 | 99 | 100 | const mapStatetoProps = state => ({ 101 | test : state.testDetails.test, 102 | user : state.user.userDetails 103 | }) 104 | 105 | export default withStyles(useStyles)(connect(mapStatetoProps,{ 106 | getCompletedTestsStudentAction, 107 | setAlert 108 | })(TestResultStudent)); 109 | -------------------------------------------------------------------------------- /frontend/src/components/basic/login/login.css: -------------------------------------------------------------------------------- 1 | html { 2 | height: 100%; 3 | } 4 | 5 | body { 6 | margin: 0; 7 | padding: 0; 8 | font-family: "Arial", sans-serif; 9 | background: linear-gradient(#141e30, #243b55); 10 | } 11 | 12 | .login-box { 13 | position: absolute; 14 | top: 50%; 15 | left: 50%; 16 | width: 400px; 17 | padding: 40px; 18 | transform: translate(-50%, -50%); 19 | background: rgba(0, 0, 0, 0.5); 20 | box-sizing: border-box; 21 | box-shadow: 0 15px 25px rgba(0, 0, 0, 0.6); 22 | border-radius: 10px; 23 | } 24 | 25 | .login-box{ 26 | margin: 0 0 30px; 27 | padding: 30px; 28 | color: #f1eee6; 29 | text-align: center; 30 | } 31 | 32 | .login-box-title { 33 | margin-top:30px; 34 | color: #07cfda; 35 | } 36 | 37 | .login-box .user-box { 38 | position: relative; 39 | } 40 | 41 | .login-box .user-box .input-field { 42 | width: 100%; 43 | padding: 10px 0; 44 | font-size: 16px; 45 | color: #f1eee6; 46 | margin-bottom: 30px; 47 | border: none; 48 | border-bottom: 1px solid #f1eee6; 49 | outline: none; 50 | background: transparent; 51 | } 52 | 53 | .login-box .user-box label { 54 | position: absolute; 55 | top: 0; 56 | left: 0; 57 | padding: 10px 0; 58 | font-size: 16px; 59 | color: #f1eee6; 60 | pointer-events: none; 61 | transition: 0.5s; 62 | } 63 | 64 | .login-box .user-box .input-field:focus ~ label, 65 | .login-box .user-box .input-field:valid ~ label { 66 | top: -20px; 67 | left: 0px; 68 | color: #07cfda; 69 | font-size: 12px; 70 | } 71 | 72 | .login-box form .button { 73 | position: relative; 74 | display: inline-block; 75 | padding: 10px 20px; 76 | color: #07cfda; 77 | font-size: 16px; 78 | text-decoration: none; 79 | text-transform: uppercase; 80 | overflow: hidden; 81 | transition: 0.5s; 82 | margin-top: 40px; 83 | letter-spacing: 4px; 84 | } 85 | 86 | .login-box form .button:hover { 87 | background: #07cfdae0; 88 | color: #f1eee6; 89 | border-radius: 5px; 90 | cursor: pointer; 91 | } 92 | 93 | .button { 94 | background-color: transparent; 95 | border: 0; 96 | } 97 | 98 | .login-box .button span { 99 | position: absolute; 100 | display: block; 101 | } 102 | 103 | .login-box .button span:nth-child(1) { 104 | top: 0; 105 | left: -100%; 106 | width: 100%; 107 | height: 2px; 108 | background: linear-gradient(90deg, transparent, #07cfda); 109 | animation: btn-anim1 1s linear infinite; 110 | animation-delay: 0s; 111 | } 112 | 113 | /* top line */ 114 | @keyframes btn-anim1 { 115 | 0% { 116 | left: -100%; 117 | } 118 | 50%, 119 | 100% { 120 | left: 100%; 121 | } 122 | } 123 | 124 | .login-box span:nth-child(2) { 125 | top: -100%; 126 | right: 0; 127 | width: 2px; 128 | height: 100%; 129 | background: linear-gradient(180deg, transparent, #07cfda); 130 | animation: btn-anim2 1s linear infinite; 131 | animation-delay: 0.25s; 132 | } 133 | 134 | /* right line */ 135 | @keyframes btn-anim2 { 136 | 0% { 137 | top: -100%; 138 | } 139 | 50%, 140 | 100% { 141 | top: 100%; 142 | } 143 | } 144 | 145 | .login-box span:nth-child(3) { 146 | bottom: 0; 147 | right: -100%; 148 | width: 100%; 149 | height: 2px; 150 | background: linear-gradient(270deg, transparent, #07cfda); 151 | animation: btn-anim3 1s linear infinite; 152 | animation-delay: 0.75s; 153 | } 154 | 155 | /* bottom line */ 156 | @keyframes btn-anim3 { 157 | 0% { 158 | right: -100%; 159 | } 160 | 50%, 161 | 100% { 162 | right: 100%; 163 | } 164 | } 165 | 166 | .login-box span:nth-child(4) { 167 | bottom: -100%; 168 | left: 0; 169 | width: 2px; 170 | height: 100%; 171 | background: linear-gradient(360deg, transparent, #07cfda); 172 | animation: btn-anim4 1s linear infinite; 173 | animation-delay: 0.5s; 174 | } 175 | 176 | /* left line */ 177 | @keyframes btn-anim4 { 178 | 0% { 179 | bottom: -100%; 180 | } 181 | 50%, 182 | 100% { 183 | bottom: 100%; 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /user-portal-frontend/src/redux/reducers/test.js: -------------------------------------------------------------------------------- 1 | import { ActionTypes } from "../action-types" 2 | 3 | 4 | const initialState = { 5 | list : [], 6 | searched : false, 7 | retrived : false, 8 | upcomingTestRetrived : false, 9 | viewTestRetrived : false, 10 | completedTestRetrived : false, 11 | viewTestResult : false, 12 | test : {} 13 | 14 | } 15 | 16 | export const TestReducer = (state=initialState, {type,payload})=> { 17 | switch(type) { 18 | case ActionTypes.GET_ALL_TESTS: 19 | return { 20 | ...state, 21 | list : payload.testlist, 22 | retrived : true, 23 | upcomingTestRetrived : false, 24 | searched : false, 25 | viewTestRetrived : false, 26 | completedTestRetrived : false, 27 | viewTestResult : false 28 | } 29 | case ActionTypes.CHANGE_TEST_REGISTER: 30 | var newlist = state.list.map((q)=>(q._id===payload.id ? 31 | { 32 | ...q, 33 | isRegistered : true 34 | } :q )); 35 | return { 36 | ...state, 37 | list : newlist, 38 | retrived : true, 39 | upcomingTestRetrived : false, 40 | searched : false, 41 | viewTestRetrived : false, 42 | completedTestRetrived : false, 43 | viewTestResult : false 44 | } 45 | case ActionTypes.GET_UPCOMING_TESTS_STUDENT: 46 | return { 47 | ...state, 48 | list : payload.testlist, 49 | retrived : false, 50 | upcomingTestRetrived : true, 51 | searched : false, 52 | viewTestRetrived : false, 53 | completedTestRetrived : false, 54 | viewTestResult : false 55 | } 56 | case ActionTypes.VIEW_TEST_DETAILS: 57 | return { 58 | ...state, 59 | test : payload.test, 60 | retrived : false, 61 | upcomingTestRetrived : false, 62 | searched : false, 63 | viewTestRetrived : true, 64 | completedTestRetrived : false, 65 | viewTestResult : false 66 | } 67 | case ActionTypes.GET_ALL_COMPLETED_TEST_STUDENT: 68 | return { 69 | ...state, 70 | list : payload.testlist, 71 | retrived : false, 72 | upcomingTestRetrived : false, 73 | searched : false, 74 | viewTestRetrived : false, 75 | completedTestRetrived : true, 76 | viewTestResult : false 77 | } 78 | 79 | case ActionTypes.GET_TEST_RESULT_STUDENT: 80 | return { 81 | ...state, 82 | test : payload.test, 83 | retrived : false, 84 | upcomingTestRetrived : false, 85 | searched : false, 86 | viewTestRetrived : false, 87 | completedTestRetrived : false, 88 | viewTestResult : true 89 | } 90 | case ActionTypes.GET_RESULT_QUESTIONS_STUDENTS: 91 | 92 | var newtest = { 93 | ...state.test, 94 | resultQuestion : payload.questions 95 | } 96 | return { 97 | ...state, 98 | test : newtest, 99 | retrived : false, 100 | upcomingTestRetrived : false, 101 | searched : false, 102 | viewTestRetrived : false, 103 | completedTestRetrived : false, 104 | viewTestResult : true 105 | } 106 | case ActionTypes.GET_TEST_DETAILS_TEACHER: 107 | return { 108 | ...state, 109 | test : payload.test, 110 | retrived : false, 111 | upcomingTestRetrived : false, 112 | searched : true, 113 | viewTestRetrived : false, 114 | completedTestRetrived : false, 115 | viewTestResult : false 116 | } 117 | case ActionTypes.Go_BACK_ALL_TEST_TEACHER: 118 | return { 119 | ...state, 120 | retrived : true, 121 | upcomingTestRetrived : false, 122 | searched : false, 123 | viewTestRetrived : false, 124 | completedTestRetrived : false, 125 | viewTestResult : false 126 | } 127 | case ActionTypes.LOGOUT: 128 | return initialState; 129 | 130 | 131 | default: 132 | return state; 133 | } 134 | } -------------------------------------------------------------------------------- /user-portal-frontend/src/components/molecues/TestView/TakeTestStudent.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { withStyles } from "@material-ui/styles"; 3 | import { connect } from "react-redux"; 4 | import { getUpcomingTestsStudentAction } from "../../../redux/actions/studentTestAction"; 5 | import { Button } from "@material-ui/core"; 6 | import { TableBody, TableCell, TableRow, Table, TableContainer, Paper } from "@material-ui/core"; 7 | import { getDatePretty, getTimePretty } from "../../../helper/common"; 8 | import { setAlert } from "../../../redux/actions/alertAction"; 9 | import { startTestAction } from "../../../redux/actions/takeTestAction"; 10 | import { Navigate } from "react-router-dom"; 11 | 12 | const useStyles = (theme)=> ({ 13 | tableBorder:{ 14 | background:'#e7e7e7', 15 | padding:'15px' 16 | }, 17 | tableHeader:{ 18 | background:'#3f51b5', 19 | color:'white' 20 | } 21 | }) 22 | 23 | class TakeTestStudent extends React.Component { 24 | constructor(props){ 25 | super(props); 26 | this.state = {} 27 | } 28 | 29 | goBack() { 30 | this.props.getUpcomingTestsStudentAction(); 31 | } 32 | 33 | onStartTest(event,test) { 34 | if(test.status==='TEST_STARTED'){ 35 | console.log("start test "+test._id); 36 | this.props.startTestAction({testid:test._id},test); 37 | 38 | } else { 39 | this.props.setAlert({ 40 | isAlert : true, 41 | type : "info", 42 | title : "Not Started" 43 | }) 44 | } 45 | 46 | } 47 | 48 | render() { 49 | if(this.props.isTestStarted) { 50 | console.log('test started'); 51 | return (); 52 | } 53 | var test = this.props.test; 54 | return(
55 | 56 | 57 | 58 | 59 | Title 60 | {test.title} 61 | 62 | 63 | Status 64 | {test.status} 65 | 66 | 67 | Total Marks 68 | {test.maxmarks} 69 | 70 | 71 | Duration 72 | {getTimePretty(test.duration)} 73 | 74 | 75 | Test Start Time 76 | {getDatePretty(test.startTime)} 77 | 78 | 79 | Test End Time 80 | {getDatePretty(test.endTime)} 81 | 82 | 83 | Result Time 84 | {getDatePretty(test.resultTime)} 85 | 86 | 87 | Start Test 88 | 92 | 93 | 94 | 95 | 96 | 97 | 98 |
99 | 100 |
101 | 102 |
) 103 | } 104 | } 105 | 106 | const mapStatetoProps = state => ({ 107 | test : state.testDetails.test, 108 | isTestStarted : state.takeTestDetails.isRetrived 109 | }) 110 | 111 | export default withStyles(useStyles)(connect(mapStatetoProps,{ 112 | getUpcomingTestsStudentAction, 113 | setAlert, 114 | startTestAction 115 | })(TakeTestStudent)) -------------------------------------------------------------------------------- /user-portal-frontend/src/components/pages/TakeTest/TestPage.js: -------------------------------------------------------------------------------- 1 | import { Button, withStyles } from "@material-ui/core"; 2 | import React from "react"; 3 | import { connect } from "react-redux"; 4 | import { Navigate } from "react-router-dom"; 5 | import { AppBar, Toolbar, Typography } from "@material-ui/core"; 6 | import Timer from "../../molecues/TestView/Timer"; 7 | import QuestionList from "../../molecues/TestView/QuestionList"; 8 | import TestQuestion from "../../molecues/TestView/TestQuestion"; 9 | import AlertBox from '../../atoms/Alertbox/AlertBox'; 10 | import { endTestAction } from "../../../redux/actions/takeTestAction"; 11 | 12 | 13 | const useStyles = (theme) => ({ 14 | addHeight : theme.mixins.toolbar, 15 | title : { 16 | flexGrow : 1 17 | }, 18 | flexdiv : { 19 | display:"flex" 20 | }, 21 | quelistdiv : { 22 | width : "18%", 23 | margin : "50px 10px" 24 | }, 25 | questiondiv : { 26 | width : "75%", 27 | marginLeft : "50px", 28 | marginTop : "50px" 29 | }, 30 | endtestbtn : { 31 | marginLeft : "20px" 32 | }, 33 | btns : { 34 | margin : "10px" 35 | } 36 | }) 37 | 38 | class TestPage extends React.Component { 39 | constructor(props) { 40 | super(props); 41 | this.state = { 42 | curIndex:0 43 | } 44 | } 45 | 46 | setCurIndex(x, obj) { 47 | console.log("set index"); 48 | console.log(obj); 49 | obj.setState({ 50 | ...obj.state, 51 | curIndex:x 52 | }) 53 | } 54 | 55 | goToPrev() { 56 | if(this.state.curIndex > 0) { 57 | this.setState({ 58 | ...this.state, 59 | curIndex: this.state.curIndex-1 60 | }) 61 | } 62 | } 63 | 64 | goToNext() { 65 | if(this.state.curIndex + 1 < this.props.taketest.answersheet.answers.length){ 66 | this.setState({ 67 | ...this.state, 68 | curIndex : this.state.curIndex+1 69 | }) 70 | } 71 | } 72 | 73 | endtest() { 74 | this.props.endTestAction(); 75 | } 76 | 77 | render() { 78 | if(this.props.taketest.isRetrived === false) { 79 | return(); 80 | } 81 | var timerTime = this.props.taketest.test.duration*1000 - (Date.now()-Date.parse(this.props.taketest.answersheet.startTime)); 82 | return(
83 |
84 | 88 | 89 | 90 | {this.props.taketest.test.title} 91 | 92 | 93 | Time Remaining   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 |
120 |
) 121 | } 122 | } 123 | 124 | 125 | const mapStatetoProps = state => ({ 126 | user : state.user, 127 | taketest : state.takeTestDetails 128 | }) 129 | export default withStyles(useStyles)(connect(mapStatetoProps,{ 130 | endTestAction 131 | })(TestPage)) ; -------------------------------------------------------------------------------- /user-portal-frontend/src/components/pages/studentHomepage/studentHomepage.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { connect } from "react-redux"; 3 | import LogoutButton from "../../atoms/LogoutButton/LogoutButton"; 4 | import Auth from "../../../helper/Auth"; 5 | import { Navigate } from "react-router-dom"; 6 | import { getUserDetails } from "../../../redux/actions/loginAction"; 7 | import { Drawer, Typography, withStyles, AppBar, Toolbar, List, ListItem, ListItemText } from "@material-ui/core"; 8 | import AlertBox from '../../atoms/Alertbox/AlertBox'; 9 | import TestDetailsStudent from "../../templates/TestDetails/TestDetailsStudent"; 10 | import UpcomingStudentTestsDetails from "../../templates/TestDetails/UpcomingStudentTestsDetails"; 11 | import CompletedTestsDetailsStudent from "../../templates/TestDetails/CompletedTestsDetailsStudent"; 12 | 13 | const drawerWidth = 200 14 | const appbarHeight = 64 15 | 16 | const useStyles = (theme)=>({ 17 | drawer : { 18 | width : drawerWidth, 19 | height : `calc(100% - ${appbarHeight}px)`, 20 | top : appbarHeight 21 | }, 22 | drawerPaper : { 23 | width : drawerWidth, 24 | height : `calc(100% - ${appbarHeight}px)`, 25 | top : appbarHeight 26 | }, 27 | flex : { 28 | display : 'flex' 29 | }, 30 | content : { 31 | margin:'auto' 32 | }, 33 | addHeight : theme.mixins.toolbar, 34 | title : { 35 | flexGrow : 1 36 | }, 37 | appbar : { 38 | height : appbarHeight 39 | } 40 | }) 41 | 42 | class StudentHomepage extends React.Component{ 43 | constructor(props) { 44 | super(props); 45 | this.state = { 46 | content:(
Welcome to Exam portal
), 47 | menuList:[{ 48 | title:'Home', 49 | content:(
Welcome to Exam portal
) 50 | },{ 51 | title : 'View All tests', 52 | content: 53 | },{ 54 | title : 'Upcoming Tests', 55 | content: 56 | },{ 57 | title : 'Completed Tests', 58 | content : 59 | }] 60 | } 61 | } 62 | 63 | onMenuItemClick(content) { 64 | this.setState({ 65 | ...this.state, 66 | content: content 67 | }) 68 | } 69 | 70 | render(){ 71 | if(!Auth.retriveToken() || Auth.retriveToken()==='undefined'){ 72 | return (); 73 | } else if(!this.props.user.isLoggedIn) { 74 | this.props.getUserDetails(); 75 | return (
); 76 | } else if(this.props.user.userDetails.type !== 'STUDENT') { 77 | return (); 78 | } 79 | return( 80 |
81 |
82 | 86 | 87 | 88 | Student Homepage 89 | 90 | 91 | welcome, {this.props.user.userDetails.username} !! 92 | 93 | 94 | 95 |
96 |
97 |
98 | 104 | 105 | {this.state.menuList.map((item,index)=>( 106 | (this.onMenuItemClick(item.content))}> 107 | 108 | 109 | ))} 110 | 111 | 112 | 113 | 114 | 115 |
116 | 117 | 118 | {this.state.content} 119 | 120 |
121 |
122 |
123 | ) 124 | } 125 | } 126 | 127 | const mapStatetoProps = state => ({ 128 | user:state.user 129 | }) 130 | 131 | export default withStyles(useStyles)(connect(mapStatetoProps,{ 132 | getUserDetails 133 | })(StudentHomepage)); -------------------------------------------------------------------------------- /backend/services/result.js: -------------------------------------------------------------------------------- 1 | const answersheetModel = require("../models/answersheet"); 2 | const testModel = require("../models/test"); 3 | const subjectModel = require("../models/subject"); 4 | const testService = require("./test"); 5 | 6 | const getAllCompletedTest = (req,res,next) => { 7 | var creator = req.user || null; 8 | if(creator == null || req.user.usertype != 'STUDENT') { 9 | res.status(401).json({ 10 | success : false, 11 | message : "Permissions not granted!" 12 | }) 13 | } 14 | 15 | answersheetModel.find({student:creator._id, completed:true},{test:1}) 16 | .then(result => { 17 | var testids = result.map(x => (x.test)) 18 | testModel.find({_id:{$in:testids}}).sort({resultTime:-1}) 19 | .then(tests => { 20 | for(var i in tests) { 21 | var correctStatus = testService.getTestStatus(tests[i]); 22 | if(correctStatus !== tests[i].status) { 23 | testService.updateStatus(tests[i],correctStatus); 24 | tests[i].status = correctStatus; 25 | } 26 | } 27 | 28 | res.json({ 29 | success : true, 30 | completedtestlist : tests.map(t => ({ 31 | _id: t._id, 32 | title:t.title, 33 | status : t.status, 34 | maxmarks : t.maxmarks, 35 | subjects : t.subjects 36 | })) 37 | }) 38 | }) 39 | .catch(err => { 40 | console.log(err); 41 | res.json({ 42 | success : false, 43 | message : 'Internal server error in test' 44 | }) 45 | }) 46 | }).catch(err => { 47 | console.log(err); 48 | res.json({ 49 | success : false, 50 | message : 'Internal server error in answersheet' 51 | }) 52 | }) 53 | } 54 | 55 | const getResultMainDetailsByTestId = (req, res, next) => { 56 | var creator = req.user || null; 57 | if(creator == null || req.user.usertype != 'STUDENT') { 58 | res.status(401).json({ 59 | success : false, 60 | message : "Permissions not granted!" 61 | }) 62 | } 63 | 64 | req.check('testid','Test id not found').notEmpty(); 65 | 66 | var errors = req.validationErrors() 67 | if(errors) { 68 | console.log(errors); 69 | res.json({ 70 | success:false, 71 | message: 'Invalid inputs', 72 | errors : errors 73 | }) 74 | return; 75 | } 76 | 77 | answersheetModel.find({student:creator._id, test:req.body.testid, completed:true}) 78 | .then(answersheets => { 79 | if(answersheets[0]) { 80 | testModel.findById({_id: req.body.testid}) 81 | .then(test => { 82 | if(test) { 83 | var correctStatus = testService.getTestStatus(test); 84 | if(correctStatus !== test.status) { 85 | testService.updateStatus(test,correctStatus); 86 | test.status = correctStatus; 87 | } 88 | subjectModel.find({_id:{$in:test.subjects}},{name:1}) 89 | .then(subjects=> { 90 | subs = subjects.map(sub=>(sub.name)) 91 | res.json({ 92 | success : true, 93 | result : { 94 | title : test.title, 95 | status : test.status, 96 | maxmarks : test.maxmarks, 97 | subjects : subs, 98 | score : answersheets[0].score, 99 | questions : test.questions, 100 | answers : answersheets[0].answers 101 | } 102 | }) 103 | }).catch(err=> { 104 | console.log(err); 105 | res.json({ 106 | success : false, 107 | message : 'Internal server error' 108 | }) 109 | }) 110 | } else { 111 | res.json({ 112 | success : false, 113 | message : 'Answer sheet not found' 114 | }) 115 | } 116 | }).catch(err=> { 117 | console.log(err); 118 | res.json({ 119 | success : false, 120 | message : 'Internal server error' 121 | }) 122 | }) 123 | } else { 124 | res.json({ 125 | success : false, 126 | message : 'Answer sheet not found' 127 | }) 128 | } 129 | }) 130 | .catch(err=> { 131 | console.log(err); 132 | res.json({ 133 | success : false, 134 | message : 'Internal server error' 135 | }) 136 | return; 137 | }) 138 | 139 | } 140 | 141 | module.exports = { 142 | getAllCompletedTest, 143 | getResultMainDetailsByTestId 144 | } -------------------------------------------------------------------------------- /frontend/src/components/Dashboard/Main/dashboradMain.js: -------------------------------------------------------------------------------- 1 | import { withStyles} from "@material-ui/core/styles"; 2 | import React from "react"; 3 | import { connect } from "react-redux"; 4 | import { Link, Navigate } from "react-router-dom"; 5 | import { logoutUser, getAdminDetails } from "../../../redux/actions/loginAction"; 6 | import { getDashboardCount } from "../../../redux/actions/dashboardDetails"; 7 | import Auth from "../../../services/Auth"; 8 | import { HomepageHeader } from "../../basic/header/header"; 9 | import logoImg from '../../basic/Homepage/main.jpg' 10 | import { MainCard } from "../Card/card"; 11 | import TeacherImg from '../teacher.png'; 12 | import StudentImg from '../student.jfif'; 13 | import SubjectImg from '../subject.jfif'; 14 | import TeacherTable from "../teacherTable/teacherTable"; 15 | import SubjectTable from "../subjectTable/subjectTable"; 16 | import StudentTable from "../studentTable/studentTable"; 17 | 18 | const useStyles = (theme)=>({ 19 | logout_btn : { 20 | marginLeft : '80%' 21 | }, 22 | headerMargin : { 23 | marginTop : 80 24 | }, 25 | inlineblock : { 26 | display : 'inline-block' 27 | }, 28 | linkbtn : { 29 | color:'black' 30 | } 31 | }) 32 | 33 | class DashboardMain extends React.Component { 34 | constructor(props) { 35 | super(props) 36 | this.state = {} 37 | this.expand = "none" 38 | } 39 | 40 | logout(obj) { 41 | obj.props.logoutUser(); 42 | obj.forceUpdate(); 43 | } 44 | 45 | handleTableExapand(type) { 46 | console.log("handle table") 47 | if(type === this.expand) { 48 | this.expand = "none" 49 | } else { 50 | this.expand = type 51 | } 52 | this.forceUpdate(); 53 | } 54 | 55 | render(){ 56 | console.log(this.props.user); 57 | if(!Auth.retriveToken() || Auth.retriveToken()==='undefined'){ 58 | return (); 59 | } else if(!this.props.user.isLoggedIn) { 60 | this.props.getAdminDetails(); 61 | return (
); 62 | } else { 63 | if(!this.props.dashboardDetails.retrived){ 64 | this.props.getDashboardCount(); 65 | } 66 | let x; 67 | if(this.expand === "Teacher") { 68 | x = ; 69 | } else if (this.expand === "Student") { 70 | x = ; 71 | } else if (this.expand === "Subject") { 72 | x = ; 73 | } 74 | return ( 75 |
76 | 77 |
78 | 79 |
80 | 81 |
82 | 83 |
84 | 85 |
86 | 87 | 88 | 89 |
90 | 91 |
92 | 93 |
94 |
95 | 96 | {x} 97 |
98 | ); 99 | 100 | } 101 | 102 | } 103 | } 104 | 105 | const mapStateToProps = state => ({ 106 | user:state.user, 107 | dashboardDetails:state.dashboardDetails 108 | }); 109 | 110 | export default withStyles(useStyles)(connect(mapStateToProps,{ 111 | logoutUser, 112 | getAdminDetails, 113 | getDashboardCount, 114 | })(DashboardMain)); 115 | -------------------------------------------------------------------------------- /user-portal-frontend/src/components/pages/teacherHomepage/teacherHomepage.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { connect } from "react-redux"; 3 | import LogoutButton from "../../atoms/LogoutButton/LogoutButton"; 4 | import Auth from "../../../helper/Auth"; 5 | import { Navigate } from "react-router-dom"; 6 | import { getUserDetails} from "../../../redux/actions/loginAction"; 7 | import AddQuestionForm from "../../templates/AddQuestionForm/AddQuestionForm"; 8 | import AlertBox from '../../atoms/Alertbox/AlertBox'; 9 | import { Drawer, Typography, withStyles, AppBar, Toolbar, List, ListItem, ListItemText } from "@material-ui/core"; 10 | import QuestionDetails from "../../templates/QuestionDetails/questionDetails"; 11 | import CreateTestForm from "../../templates/CreateTestForm/CreateTestForm"; 12 | import TestDetails from "../../templates/TestDetails/TestDetails"; 13 | 14 | const drawerWidth = 200 15 | const appbarHeight = 64 16 | 17 | const useStyles = (theme)=>({ 18 | drawer : { 19 | width : drawerWidth, 20 | height : `calc(100% - ${appbarHeight}px)`, 21 | top : appbarHeight 22 | }, 23 | drawerPaper : { 24 | width : drawerWidth, 25 | height : `calc(100% - ${appbarHeight}px)`, 26 | top : appbarHeight 27 | }, 28 | flex : { 29 | display : 'flex' 30 | }, 31 | content : { 32 | margin:'auto' 33 | }, 34 | addHeight : theme.mixins.toolbar, 35 | title : { 36 | flexGrow : 1 37 | }, 38 | appbar : { 39 | height : appbarHeight 40 | } 41 | }) 42 | 43 | class TeacherHomepage extends React.Component{ 44 | constructor(props) { 45 | super(props); 46 | this.state = { 47 | content:(
Welcome to Exam portal
), // home - welcome message addquestion - add question form 48 | menuList : [{ 49 | title:'Home', 50 | content:(
Welcome to Exam portal
) 51 | },{ 52 | title: 'Add Question', 53 | content: 54 | },{ 55 | title: 'Questions', 56 | content: 57 | },{ 58 | title: 'Create Test', 59 | content: 60 | },{ 61 | title : 'View Tests', 62 | content : 63 | }] 64 | } 65 | } 66 | 67 | onMenuItemClick(content) { 68 | this.setState({ 69 | ...this.state, 70 | content: content 71 | }) 72 | } 73 | 74 | render(){ 75 | if(!Auth.retriveToken() || Auth.retriveToken()==='undefined'){ 76 | return (); 77 | } else if(!this.props.user.isLoggedIn) { 78 | this.props.getUserDetails(); 79 | return (
); 80 | } else if(this.props.user.userDetails.type !== 'TEACHER') { 81 | return (); 82 | } 83 | return( 84 |
85 |
86 | 90 | 91 | 92 | Teacher Homepage 93 | 94 | 95 | welcome, {this.props.user.userDetails.username} !! 96 | 97 | 98 | 99 |
100 |
101 |
102 | 108 | 109 | {this.state.menuList.map((item,index)=>( 110 | (this.onMenuItemClick(item.content))}> 111 | 112 | 113 | ))} 114 | 115 | 116 | 117 | 118 | 119 |
120 | 121 | 122 | {this.state.content} 123 | 124 |
125 |
126 | 127 | 128 |
129 | ) 130 | } 131 | } 132 | 133 | const mapStatetoProps = state => ({ 134 | user:state.user 135 | }) 136 | 137 | export default withStyles(useStyles) (connect(mapStatetoProps,{ 138 | getUserDetails 139 | })(TeacherHomepage)); -------------------------------------------------------------------------------- /user-portal-frontend/src/redux/actions/takeTestAction.js: -------------------------------------------------------------------------------- 1 | import axios from "axios" 2 | import apis from "../../helper/Apis" 3 | import Auth from "../../helper/Auth" 4 | import { ActionTypes } from "../action-types" 5 | import { Navigate } from "react-router-dom"; 6 | import store from "../store"; 7 | 8 | export const startTestAction = (details, test) => { 9 | 10 | return async(dispatch)=> { 11 | const response = await axios.post(apis.BASE + apis.START_TEST, details, { 12 | headers : { 13 | 'Authorization' : `Bearer ${Auth.retriveToken()}` 14 | } 15 | }).catch(err => { 16 | console.log(err); 17 | return; 18 | }) 19 | if(response.data.success) { 20 | console.log(response.data); 21 | var addStartTime = (response.data.answersheet.startTime === undefined) 22 | const queResponse = await axios.post(apis.BASE + apis.GET_QUESTIONS_TAKETEST, 23 | { 24 | answersheetid:response.data.answersheet._id, 25 | addStartTime:addStartTime, 26 | questionid:response.data.questions 27 | }, { 28 | headers : { 29 | 'Authorization' : `Bearer ${Auth.retriveToken()}` 30 | } 31 | }).catch(err => { 32 | console.log(err); 33 | return; 34 | }) 35 | if(queResponse.data.success) { 36 | if(response.data.answersheet.answers.length > 0) { 37 | dispatch({ 38 | type: ActionTypes.START_TEST, 39 | payload : { 40 | test : test, 41 | answersheet : {...response.data.answersheet,startTime:queResponse.data.startTime}, 42 | questions : queResponse.data.questions 43 | } 44 | }) 45 | } 46 | else { 47 | var emptyAns = new Array(response.data.questions.length).fill(null); 48 | dispatch({ 49 | type: ActionTypes.START_TEST, 50 | payload : { 51 | test : test, 52 | answersheet : {...response.data.answersheet,startTime:queResponse.data.startTime, answers:emptyAns}, 53 | questions : queResponse.data.questions 54 | } 55 | }) 56 | } 57 | } else { 58 | dispatch({ 59 | type:ActionTypes.SET_ALERT, 60 | payload : { 61 | isAlert : true, 62 | title : "Start Test Error", 63 | type : "error", 64 | message : queResponse.data.message 65 | } 66 | }) 67 | } 68 | } 69 | else { 70 | dispatch({ 71 | type:ActionTypes.SET_ALERT, 72 | payload : { 73 | isAlert : true, 74 | title : "Start Test Error", 75 | type : "error", 76 | message : response.data.message 77 | } 78 | }) 79 | } 80 | } 81 | } 82 | 83 | export const selectedOptionAction = (details) => { 84 | return async(dispatch)=>{ 85 | dispatch({ 86 | type:ActionTypes.SELECT_ANSWER, 87 | payload : details 88 | }) 89 | } 90 | } 91 | 92 | export const saveAnswerAction = () => { 93 | return async(dispatch)=> { 94 | console.log(store.getState()); 95 | var answersheet = store.getState().takeTestDetails.answersheet; 96 | console.log(answersheet.answers); 97 | axios.post(apis.BASE + apis.SAVE_ANSWER,{answersheetid:answersheet._id,answers: answersheet.answers},{ 98 | headers : { 99 | 'Authorization' : `Bearer ${Auth.retriveToken()}` 100 | } 101 | }).then(response=> { 102 | console.log(response); 103 | if(response.data.success) { 104 | if(response.data.testDone) { 105 | dispatch({ 106 | type : ActionTypes.END_TEST 107 | }) 108 | } 109 | console.log("saved answer"); 110 | } else { 111 | console.log(response.data); 112 | } 113 | }).catch(err => { 114 | console.log(err); 115 | return; 116 | }) 117 | } 118 | } 119 | 120 | export const endTestAction = () => { 121 | return async(dispatch)=> { 122 | console.log(store.getState()); 123 | var answersheet = store.getState().takeTestDetails.answersheet; 124 | console.log(answersheet.answers); 125 | axios.post(apis.BASE + apis.END_TEST,{answersheetid:answersheet._id,answers: answersheet.answers},{ 126 | headers : { 127 | 'Authorization' : `Bearer ${Auth.retriveToken()}` 128 | } 129 | }).then(response=> { 130 | console.log(response); 131 | if(response.data.success) { 132 | dispatch({ 133 | type : ActionTypes.END_TEST 134 | }) 135 | return () 136 | } 137 | }).catch(err => { 138 | console.log(err); 139 | return; 140 | }) 141 | } 142 | } -------------------------------------------------------------------------------- /user-portal-frontend/src/components/templates/studentRegisterForm/studentRegisterForm.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import TextField from "@material-ui/core/TextField"; 3 | import Button from "@material-ui/core/Button"; 4 | import { withStyles } from "@material-ui/core/styles"; 5 | import { registerStudentAction } from "../../../redux/actions/registerStudentAction"; 6 | import { connect } from "react-redux"; 7 | import { setAlert } from "../../../redux/actions/alertAction"; 8 | 9 | const useStyles = ()=>({ 10 | inputfield : { 11 | display:'block', 12 | margin :'20px' 13 | }, 14 | btn : { 15 | margin : '0px 40px' 16 | }, 17 | formClass : { 18 | margin:'20px', 19 | display: 'inline-block', 20 | textAlign : 'center', 21 | border : '1px solid black', 22 | borderRadius: '10px', 23 | padding : '20px' 24 | }, 25 | 26 | formTitle:{ 27 | fontSize: '1.7em' 28 | } 29 | }) 30 | 31 | class StudentRegisterForm extends React.Component { 32 | constructor(props) { 33 | super(props); 34 | this.state = { 35 | username : "", 36 | email : "", 37 | password : "", 38 | confirmPassword : "" 39 | } 40 | } 41 | 42 | usernameInputHandler = (event) => { 43 | this.setState({ 44 | ...this.state, 45 | username : event.target.value 46 | }); 47 | } 48 | 49 | emailInputHandler = (event) => { 50 | this.setState({ 51 | ...this.state, 52 | email : event.target.value 53 | }); 54 | } 55 | 56 | passwordInputHandler = (event) => { 57 | this.setState({ 58 | ...this.state, 59 | password : event.target.value 60 | }); 61 | } 62 | 63 | confirmpasswordInputHandler = (event) => { 64 | this.setState({ 65 | ...this.state, 66 | confirmPassword : event.target.value 67 | }); 68 | } 69 | 70 | handleSubmit(event) { 71 | event.preventDefault(); 72 | if(this.state.confirmPassword !== this.state.password) { 73 | this.props.setAlert({ 74 | isAlert:false, 75 | type:"error", 76 | title:'Invalid Input', 77 | message : 'Confirm Password does not match', 78 | }) 79 | return; 80 | } 81 | this.props.registerStudentAction({ 82 | username : this.state.username, 83 | email : this.state.email, 84 | password : this.state.password 85 | }); 86 | } 87 | 88 | render() { 89 | return ( 90 |
(this.handleSubmit(event))}> 91 |
Register
92 | (this.usernameInputHandler(event))} 102 | required 103 | /> 104 | (this.emailInputHandler(event))} 114 | required 115 | /> 116 | (this.passwordInputHandler(event))} 126 | required 127 | /> 128 | (this.confirmpasswordInputHandler(event))} 138 | required 139 | /> 140 | 148 | 149 | ) 150 | } 151 | } 152 | 153 | const mapStatetoProps = state => ({ 154 | 155 | }) 156 | 157 | export default withStyles(useStyles)(connect(mapStatetoProps,{ 158 | registerStudentAction, 159 | setAlert 160 | })(StudentRegisterForm)); -------------------------------------------------------------------------------- /backend/services/passportconf.js: -------------------------------------------------------------------------------- 1 | var passport = require('passport'); 2 | var LocalStrategy = require('passport-local').Strategy; 3 | var JwtStrategy = require('passport-jwt').Strategy; 4 | var ExtractJwt = require('passport-jwt').ExtractJwt; 5 | 6 | const bcrypt = require('bcrypt'); 7 | const saltRounds = 10; 8 | 9 | var config = require('config'); 10 | 11 | var userModel = require('../models/user'); 12 | var adminModel = require('../models/admin'); 13 | 14 | var localStrategyOption = { 15 | usernameField: 'email', 16 | passwordField : 'password', 17 | passReqToCallback : true 18 | } 19 | 20 | function localStrategyVerify(req,email, password, done){ 21 | userModel.findOne({'email':email}, (err, user)=>{ 22 | // database server error 23 | if(err) { 24 | return done(err, false, { 25 | success : false, 26 | message : 'server error' 27 | }); 28 | } 29 | 30 | // user not found 31 | if(!user) { 32 | return done(null, false, { 33 | success : false, 34 | message : 'email is not registered' 35 | }) 36 | } else if (user.status == false) { 37 | return done(null, false, { 38 | success : false, 39 | message : 'your account is blocked' 40 | }) 41 | } 42 | else { 43 | //check for password 44 | bcrypt.compare(password, user.password) 45 | .then( (result) => { 46 | if(result) { 47 | return done(null, user, { 48 | success : true, 49 | message : 'logged in successfully' 50 | }); 51 | } else { 52 | return done(null, false, { 53 | success : false, 54 | message : 'invalid password' 55 | }); 56 | } 57 | }) 58 | } 59 | 60 | }) 61 | } 62 | 63 | var localStrategy = new LocalStrategy(localStrategyOption, localStrategyVerify); 64 | 65 | passport.use('login',localStrategy); 66 | 67 | 68 | var jwt_options = { 69 | jwtFromRequest : ExtractJwt.fromAuthHeaderAsBearerToken(), 70 | secretOrKey : config.jwt.secret 71 | } 72 | 73 | function jwtStrategyVerify(jwt_payload, done) { 74 | userModel.findById(jwt_payload._id, (err, user)=> { 75 | // database server error 76 | if(err) { 77 | return done(err, false, { 78 | success : false, 79 | message : 'server error' 80 | }); 81 | } 82 | if (user) { 83 | return done(null, user,{ 84 | success: true, 85 | message: "Successful" 86 | }); 87 | } 88 | else { 89 | return done(null, false,{ 90 | success: false, 91 | message: "Authorization Failed" 92 | }); 93 | } 94 | }); 95 | } 96 | 97 | var jwtStrategy = new JwtStrategy(jwt_options, jwtStrategyVerify); 98 | 99 | passport.use('user-token',jwtStrategy); 100 | 101 | var localStrategyOptionAdmin = { 102 | usernameField : 'username', 103 | passwordField : 'password', 104 | passReqToCallback : true 105 | } 106 | 107 | function localStrategyVerifyAdmin(req, username, password, done) { 108 | adminModel.findOne({'username':username}, (err, admin)=> { 109 | // database server error 110 | if(err) { 111 | return done(err, false, { 112 | success : false, 113 | message : 'server error' 114 | }) 115 | } 116 | 117 | //admin not found 118 | if(!admin) { 119 | return done(null, false, { 120 | success : false, 121 | message : 'user not found' 122 | }) 123 | } else { 124 | //check of password 125 | bcrypt.compare(password, admin.password) 126 | .then((result)=>{ 127 | if(result) { 128 | return done(null, admin, { 129 | success : true, 130 | message : 'logged in successfully' 131 | }) 132 | } 133 | else { 134 | return done(null, false, { 135 | success : false, 136 | message : 'invalid password' 137 | }) 138 | } 139 | }) 140 | } 141 | }) 142 | } 143 | 144 | var localStrategyAdmin = new LocalStrategy(localStrategyOptionAdmin, localStrategyVerifyAdmin); 145 | 146 | passport.use('admin-login',localStrategyAdmin); 147 | 148 | function jwtStrategyVeriryAdmin(jwt_payload, done) { 149 | adminModel.findById(jwt_payload._id, (err, admin)=>{ 150 | //database server error 151 | if(err) { 152 | return done(err, false, { 153 | success : false, 154 | message : 'server error' 155 | }) 156 | } 157 | 158 | if (admin) { 159 | return done(null, admin, { 160 | success : true, 161 | message : 'successful' 162 | }) 163 | } else { 164 | return done(null, false, { 165 | success : false, 166 | message : 'Authorization failed' 167 | }) 168 | } 169 | }) 170 | } 171 | 172 | var jwtStrategyAdmin = new JwtStrategy(jwt_options, jwtStrategyVeriryAdmin); 173 | 174 | passport.use('admin-token', jwtStrategyAdmin); 175 | 176 | module.exports = passport; -------------------------------------------------------------------------------- /user-portal-frontend/src/redux/actions/studentTestAction.js: -------------------------------------------------------------------------------- 1 | import axios from "axios" 2 | import apis from "../../helper/Apis" 3 | import Auth from "../../helper/Auth" 4 | import { ActionTypes } from "../action-types" 5 | 6 | 7 | export const getAllTestStudentAction = () => { 8 | return async(dispatch) => { 9 | const response = await axios.get(apis.BASE + apis.GET_ALL_TEST_STUDNET,{ 10 | headers:{ 11 | 'Authorization':`Bearer ${Auth.retriveToken()}` 12 | } 13 | }); 14 | console.log(response.data); 15 | if(response.data.success) { 16 | 17 | dispatch({ 18 | type : ActionTypes.GET_ALL_TESTS, 19 | payload : { 20 | testlist : response.data.testlist 21 | } 22 | }) 23 | } 24 | } 25 | } 26 | 27 | export const getUpcomingTestsStudentAction = () => { 28 | return async(dispatch) => { 29 | const response = await axios.get(apis.BASE + apis.GET_UPCOMING_TESTS_STUDENT,{ 30 | headers:{ 31 | 'Authorization':`Bearer ${Auth.retriveToken()}` 32 | } 33 | }); 34 | if(response.data.success) { 35 | 36 | dispatch({ 37 | type : ActionTypes.GET_UPCOMING_TESTS_STUDENT, 38 | payload : { 39 | testlist : response.data.upcomingtestlist 40 | } 41 | }) 42 | } 43 | } 44 | } 45 | 46 | export const getCompletedTestsStudentAction = () => { 47 | return async(dispatch) => { 48 | const response = await axios.get(apis.BASE + apis.GET_ALL_COMPLETED_TEST_STUDENT,{ 49 | headers:{ 50 | 'Authorization':`Bearer ${Auth.retriveToken()}` 51 | } 52 | }); 53 | if(response.data.success) { 54 | dispatch({ 55 | type : ActionTypes.GET_ALL_COMPLETED_TEST_STUDENT, 56 | payload : { 57 | testlist : response.data.completedtestlist 58 | } 59 | }) 60 | } 61 | } 62 | } 63 | 64 | export const studentTestRegister = (details) => { 65 | return async(dispatch)=>{ 66 | axios.post(apis.BASE + apis.STUDENT_TEST_REGISTER,details, { 67 | headers : { 68 | 'Authorization':`Bearer ${Auth.retriveToken()}` 69 | } 70 | }).then(response => { 71 | if(response.data.success) { 72 | dispatch({ 73 | type : ActionTypes.CHANGE_TEST_REGISTER, 74 | payload : { 75 | id: details.testid 76 | } 77 | }) 78 | } else { 79 | console.error(response.data); 80 | dispatch({ 81 | type:ActionTypes.SET_ALERT, 82 | payload : { 83 | isAlert:true, 84 | type : 'error', 85 | title : 'error in change status', 86 | message : response.data.message 87 | } 88 | }) 89 | } 90 | }) 91 | } 92 | } 93 | 94 | export const getTestById = (details) => { 95 | return async(dispatch) => { 96 | const response = await axios.post(apis.BASE+ apis.GET_TEST_DETAILS_BY_ID,details,{ 97 | headers:{ 98 | 'Authorization':`Bearer ${Auth.retriveToken()}` 99 | } 100 | }) 101 | if(response.data.success) { 102 | dispatch({ 103 | type:ActionTypes.VIEW_TEST_DETAILS, 104 | payload : { 105 | test : response.data.test 106 | } 107 | }) 108 | } else { 109 | dispatch({ 110 | type:ActionTypes.SET_ALERT, 111 | payload : { 112 | isAlert : true, 113 | title : "Could not get test details", 114 | type : "error", 115 | message : response.data.message 116 | } 117 | }) 118 | } 119 | } 120 | } 121 | 122 | export const getTestResultStudent = (details) => { 123 | 124 | return async(dispatch)=> { 125 | var response = await axios.post(apis.BASE + apis.GET_TEST_RESULT_STUDENT,details,{ 126 | headers:{ 127 | 'Authorization':`Bearer ${Auth.retriveToken()}` 128 | } 129 | }) 130 | if(response.data.success){ 131 | dispatch({ 132 | type : ActionTypes.GET_TEST_RESULT_STUDENT, 133 | payload : { 134 | test : response.data.result 135 | } 136 | }) 137 | } else { 138 | dispatch({ 139 | type : ActionTypes.SET_ALERT, 140 | payload : { 141 | isAlert : true, 142 | type : 'error', 143 | title : 'Error', 144 | message : response.data.message 145 | } 146 | }) 147 | } 148 | 149 | } 150 | } 151 | 152 | 153 | export const getQuestionAnswerActionStudent = (details) => { 154 | return async(dispatch) => { 155 | var response = await axios.post(apis.BASE + apis.GET_RESULT_QUESTIONS_STUDENTS, details, { 156 | headers:{ 157 | 'Authorization':`Bearer ${Auth.retriveToken()}` 158 | } 159 | }) 160 | console.log(response.data); 161 | if(response.data.success) { 162 | dispatch({ 163 | type : ActionTypes.GET_RESULT_QUESTIONS_STUDENTS, 164 | payload : { 165 | questions : response.data.questions 166 | } 167 | }) 168 | } else { 169 | dispatch({ 170 | type : ActionTypes.SET_ALERT, 171 | payload : { 172 | isAlert : true, 173 | type : 'error', 174 | title : 'Error', 175 | message : response.data.message 176 | } 177 | }) 178 | } 179 | } 180 | } --------------------------------------------------------------------------------